Centralize all path management via src/config/paths.ts
- Add getPaths() with workspace, scripts, ppt skin/template paths - Export getWorkspacePath(args?) helper — removes duplicate local functions and stale process.cwd()/workspace fallbacks from sqlite, pdf, excel, image, pubmed, email, audio-transcribe (7 files) - weather.ts: replace __dirname-relative scripts path with getPaths().scripts - Move ppt/ to .smallclaw/skills/presenter/ppt/; update server-v2.ts, pptx.ts, pptx_gen.py (argparse --skin-dir/--template-dir) - Fix PPT skin thumbnail serving: res.sendFile → fs.createReadStream (bypasses send module dotfiles restriction for .smallclaw/ paths) - Fix workspace default: .smallclaw/workspace (was project root workspace/) - Fix subagent paths to use SMALLCLAW_DATA_DIR env var - Fix Monaco marked module load failure: restore define.amd on DOMContentLoaded after deferred UMD libs (tone, magenta) have loaded - Add AudioContext resume on first user click (Chrome autoplay policy) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import { getConfig } from '../config/config.js';
|
||||
import { getWorkspacePath } from '../config/paths.js';
|
||||
import { ToolResult } from '../types.js';
|
||||
import { transcribeAudio } from './stt.js';
|
||||
|
||||
@@ -26,7 +27,7 @@ export const audioTranscribeTool = {
|
||||
const filePath = String(args?.path || '').trim();
|
||||
if (!filePath) return { success: false, error: 'path is required' };
|
||||
|
||||
const workspacePath = args?._workspacePath || args?._workspace || getConfig().getConfig()?.workspace?.path || process.cwd();
|
||||
const workspacePath = getWorkspacePath(args);
|
||||
const resolved = path.isAbsolute(filePath) ? filePath : path.resolve(workspacePath, filePath);
|
||||
|
||||
if (!fs.existsSync(resolved)) {
|
||||
|
||||
+2
-1
@@ -2,6 +2,7 @@ import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { ToolResult } from '../types.js';
|
||||
import { getConfig } from '../config/config.js';
|
||||
import { getWorkspacePath } from '../config/paths.js';
|
||||
|
||||
// ── Config ────────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -333,7 +334,7 @@ export const emailReadTool = {
|
||||
resolvedWorkspacePath = path.resolve(String(injectedWorkspace));
|
||||
} else {
|
||||
const cfg = getConfig().getConfig();
|
||||
resolvedWorkspacePath = path.resolve(cfg?.workspace?.path || path.join(process.cwd(), 'workspace'));
|
||||
resolvedWorkspacePath = path.resolve(getWorkspacePath(args));
|
||||
}
|
||||
const relDir = path.join('attachments', `uid-${uid}`);
|
||||
const saveDir = path.join(resolvedWorkspacePath, relDir);
|
||||
|
||||
+1
-11
@@ -2,21 +2,11 @@ import { execFile } from 'child_process';
|
||||
import { promisify } from 'util';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import { getConfig } from '../config/config.js';
|
||||
import { ToolResult } from '../types.js';
|
||||
import { getWorkspacePath } from '../config/paths.js';
|
||||
|
||||
const execFileAsync = promisify(execFile);
|
||||
|
||||
function getWorkspacePath(args?: any): string {
|
||||
const sessionPath = args?._workspacePath || args?._workspace;
|
||||
if (sessionPath) return sessionPath;
|
||||
try {
|
||||
return getConfig().getConfig()?.workspace?.path || path.join(process.cwd(), 'workspace');
|
||||
} catch {
|
||||
return path.join(process.cwd(), 'workspace');
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Python scripts ────────────────────────────────────────────────────────────
|
||||
|
||||
const READ_SCRIPT = `
|
||||
|
||||
+1
-11
@@ -1,18 +1,8 @@
|
||||
import { spawn } from 'child_process';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import { getConfig } from '../config/config.js';
|
||||
import { ToolResult } from '../types.js';
|
||||
|
||||
function getWorkspacePath(args?: any): string {
|
||||
const sessionPath = args?._workspacePath || args?._workspace;
|
||||
if (sessionPath) return sessionPath;
|
||||
try {
|
||||
return getConfig().getConfig()?.workspace?.path || path.join(process.cwd(), 'workspace');
|
||||
} catch {
|
||||
return path.join(process.cwd(), 'workspace');
|
||||
}
|
||||
}
|
||||
import { getWorkspacePath } from '../config/paths.js';
|
||||
|
||||
function isPathInsideDir(base: string, target: string): boolean {
|
||||
const resolvedBase = path.resolve(base);
|
||||
|
||||
+1
-11
@@ -2,21 +2,11 @@ import { execFile } from 'child_process';
|
||||
import { promisify } from 'util';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import { getConfig } from '../config/config.js';
|
||||
import { ToolResult } from '../types.js';
|
||||
import { getWorkspacePath } from '../config/paths.js';
|
||||
|
||||
const execFileAsync = promisify(execFile);
|
||||
|
||||
function getWorkspacePath(args?: any): string {
|
||||
const sessionPath = args?._workspacePath || args?._workspace;
|
||||
if (sessionPath) return sessionPath;
|
||||
try {
|
||||
return getConfig().getConfig()?.workspace?.path || path.join(process.cwd(), 'workspace');
|
||||
} catch {
|
||||
return path.join(process.cwd(), 'workspace');
|
||||
}
|
||||
}
|
||||
|
||||
function isPathInsideDir(base: string, target: string): boolean {
|
||||
const resolvedBase = path.resolve(base);
|
||||
const resolvedTarget = path.resolve(target);
|
||||
|
||||
+6
-15
@@ -2,26 +2,17 @@ import path from 'path';
|
||||
import fs from 'fs';
|
||||
import { execFile } from 'child_process';
|
||||
import { getConfig } from '../config/config.js';
|
||||
import { getPaths } from '../config/paths.js';
|
||||
import { ToolResult } from '../types.js';
|
||||
|
||||
type SSESender = (type: string, data: any) => void;
|
||||
|
||||
// ─── Engine paths ──────────────────────────────────────────────────────────────
|
||||
const PYTHON_SCRIPT = path.join(__dirname, '..', '..', 'scripts', 'pptx_gen.py');
|
||||
|
||||
// ─── Paths ──────────────────────────────────────────────────────────────────────
|
||||
const SKIN_DIR = path.join(__dirname, '..', '..', '.smallclaw', 'skills', 'presenter', 'ppt', 'skin');
|
||||
const TEMPLATE_DIR = path.join(__dirname, '..', '..', '.smallclaw', 'skills', 'presenter', 'ppt', 'template');
|
||||
const LEGACY_SKIN_DIR = path.join(__dirname, '..', '..', 'assets', 'pptx_template');
|
||||
|
||||
// Resolve skin directory: prefer ppt/skin, fall back to legacy assets/pptx_template
|
||||
const ACTIVE_SKIN_DIR = fs.existsSync(SKIN_DIR) ? SKIN_DIR
|
||||
: fs.existsSync(LEGACY_SKIN_DIR) ? LEGACY_SKIN_DIR
|
||||
: SKIN_DIR;
|
||||
// ─── Paths (from central paths.ts) ──────────────────────────────────────────────
|
||||
const { pptxScript: PYTHON_SCRIPT, pptSkinDir: SKIN_DIR, pptTemplateDir: TEMPLATE_DIR } = getPaths();
|
||||
|
||||
const SKIN_EXTENSIONS = ['.png', '.jpg', '.jpeg'];
|
||||
const SKIN_NAMES = fs.existsSync(ACTIVE_SKIN_DIR)
|
||||
? fs.readdirSync(ACTIVE_SKIN_DIR)
|
||||
const SKIN_NAMES = fs.existsSync(SKIN_DIR)
|
||||
? fs.readdirSync(SKIN_DIR)
|
||||
.filter(f => SKIN_EXTENSIONS.includes(path.extname(f).toLowerCase()))
|
||||
.map(f => path.basename(f, path.extname(f)))
|
||||
: [];
|
||||
@@ -180,7 +171,7 @@ async function generateWithPython(spec: PresentationSpec, workspacePath: string)
|
||||
|
||||
const result = await new Promise<{ success: boolean; path?: string; folder?: string; filename?: string; slides?: number; warnings?: string[]; stdout?: string; error?: string; download_url?: string; preview_url?: string }>((resolve, reject) => {
|
||||
const pythonCmd = process.platform === 'win32' ? 'python' : 'python3';
|
||||
execFile(pythonCmd, [PYTHON_SCRIPT, tmpSpecPath, workspacePath], {
|
||||
execFile(pythonCmd, [PYTHON_SCRIPT, tmpSpecPath, workspacePath, '--skin-dir', SKIN_DIR, '--template-dir', TEMPLATE_DIR], {
|
||||
timeout: 60_000,
|
||||
maxBuffer: 1024 * 1024 * 10,
|
||||
windowsHide: true,
|
||||
|
||||
+3
-2
@@ -4,6 +4,7 @@ import { execFile } from 'child_process';
|
||||
import { promisify } from 'util';
|
||||
import { ToolResult } from '../types.js';
|
||||
import { getConfig } from '../config/config.js';
|
||||
import { getWorkspacePath } from '../config/paths.js';
|
||||
|
||||
const execFileAsync = promisify(execFile);
|
||||
|
||||
@@ -349,7 +350,7 @@ export const pubmedFulltextTool = {
|
||||
|
||||
// Download PDF
|
||||
const config = getConfig().getConfig();
|
||||
const workspaceDir = args?._workspacePath || args?._workspace || config.workspace?.path || path.join(process.cwd(), 'workspace');
|
||||
const workspaceDir = getWorkspacePath(args);
|
||||
const pdfDir = path.join(workspaceDir, 'pubmed');
|
||||
fs.mkdirSync(pdfDir, { recursive: true });
|
||||
|
||||
@@ -466,7 +467,7 @@ export const pubmedFulltextTool = {
|
||||
|
||||
// Save to workspace (prefer per-user path injected by v2 executeTool)
|
||||
const config = getConfig().getConfig();
|
||||
const workspaceDir = args?._workspacePath || args?._workspace || config.workspace?.path || path.join(process.cwd(), 'workspace');
|
||||
const workspaceDir = getWorkspacePath(args);
|
||||
const savePath = args?.save_path
|
||||
? path.join(workspaceDir, args.save_path)
|
||||
: path.join(workspaceDir, 'pubmed', `${pmcid}.txt`);
|
||||
|
||||
+1
-9
@@ -1,16 +1,8 @@
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import { getConfig } from '../config/config.js';
|
||||
import { getWorkspacePath } from '../config/paths.js';
|
||||
import { ToolResult } from '../types.js';
|
||||
|
||||
function getWorkspacePath(args?: any): string {
|
||||
try {
|
||||
return args?._workspacePath || args?._workspace || getConfig().getConfig()?.workspace?.path || path.join(process.cwd(), 'workspace');
|
||||
} catch {
|
||||
return path.join(process.cwd(), 'workspace');
|
||||
}
|
||||
}
|
||||
|
||||
function isPathInsideDir(base: string, target: string): boolean {
|
||||
const resolvedBase = path.resolve(base);
|
||||
const resolvedTarget = path.resolve(target);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { ToolResult } from '../types.js';
|
||||
import { getConfig } from '../config/config.js';
|
||||
import { getVault } from '../security/vault.js';
|
||||
import { getPaths } from '../config/paths.js';
|
||||
import { spawn } from 'child_process';
|
||||
import path from 'path';
|
||||
import https from 'https';
|
||||
@@ -1010,7 +1011,7 @@ function getCdsApiKey(): string | undefined {
|
||||
|
||||
function runPythonCds(input: object, timeoutMs = 300_000): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const scriptPath = path.resolve(__dirname, '../../scripts/era5_cds_fetch.py');
|
||||
const scriptPath = path.join(getPaths().scripts, 'era5_cds_fetch.py');
|
||||
const child = spawn('python3', [scriptPath], { stdio: ['pipe', 'pipe', 'pipe'] });
|
||||
let stdout = '';
|
||||
let stderr = '';
|
||||
|
||||
Reference in New Issue
Block a user