9b4a25799f
Replace fixed ackText with a rule-based AckProvider that picks response templates by message type and intent (translate, summary, rewrite, poster, ppt, mindmap, code, search, schedule). Pure sync, zero I/O, auto-falls back to config.ackText on any error. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
121 lines
3.8 KiB
JavaScript
121 lines
3.8 KiB
JavaScript
#!/usr/bin/env node
|
|
import fs from 'node:fs';
|
|
import path from 'node:path';
|
|
import { spawn } from 'node:child_process';
|
|
import { fileURLToPath } from 'node:url';
|
|
import { createDbPool } from '../db.mjs';
|
|
import { buildExecutorLaunchPlan, createLlmProviderService } from '../llm-providers.mjs';
|
|
|
|
const root = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
|
|
function loadEnvFile(filePath) {
|
|
if (!fs.existsSync(filePath)) return;
|
|
for (const line of fs.readFileSync(filePath, 'utf8').split('\n')) {
|
|
const trimmed = line.trim();
|
|
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
const eq = trimmed.indexOf('=');
|
|
if (eq < 0) continue;
|
|
const key = trimmed.slice(0, eq).trim();
|
|
const value = trimmed.slice(eq + 1).trim();
|
|
if (!process.env[key]) process.env[key] = value;
|
|
}
|
|
}
|
|
|
|
function parseArgs(argv) {
|
|
if (argv[0] === '--help' || argv[0] === '-h') {
|
|
return { help: true, executor: '' };
|
|
}
|
|
const [executor, ...rest] = argv;
|
|
const out = {
|
|
executor: String(executor ?? '').trim(),
|
|
cwd: process.cwd(),
|
|
mode: 'headless',
|
|
instruction: '',
|
|
purpose: 'default',
|
|
};
|
|
for (let i = 0; i < rest.length; i += 1) {
|
|
const item = rest[i];
|
|
if (item === '--cwd') out.cwd = String(rest[++i] ?? out.cwd);
|
|
else if (item === '--mode') out.mode = String(rest[++i] ?? out.mode);
|
|
else if (item === '--instruction') out.instruction = String(rest[++i] ?? out.instruction);
|
|
else if (item === '--purpose') out.purpose = String(rest[++i] ?? out.purpose);
|
|
else if (item === '--help' || item === '-h') out.help = true;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
function printHelp() {
|
|
console.log([
|
|
'Usage:',
|
|
' node scripts/launch-executor.mjs <goose|aider|openhands> [--cwd <dir>] [--mode headless|serve] [--instruction <text>] [--purpose default]',
|
|
'',
|
|
'Examples:',
|
|
' node scripts/launch-executor.mjs aider --cwd /path/to/repo --instruction "fix login button"',
|
|
' node scripts/launch-executor.mjs openhands --mode serve --cwd /path/to/repo',
|
|
' node scripts/launch-executor.mjs openhands --mode headless --cwd /path/to/repo --instruction "add search"',
|
|
].join('\n'));
|
|
}
|
|
|
|
loadEnvFile(path.join(root, '.env'));
|
|
loadEnvFile(path.join(root, '.env.local'));
|
|
|
|
const parsed = parseArgs(process.argv.slice(2));
|
|
if (parsed.help || !parsed.executor) {
|
|
printHelp();
|
|
process.exit(parsed.help ? 0 : 1);
|
|
}
|
|
|
|
const pool = createDbPool();
|
|
const svc = createLlmProviderService(pool, {
|
|
apiTarget: process.env.TKMIND_API_TARGET ?? 'https://127.0.0.1:18006',
|
|
apiSecret: process.env.TKMIND_SERVER__SECRET_KEY ?? 'local-dev-secret',
|
|
});
|
|
|
|
const plan = await svc.getExecutorLaunchPlan(parsed.executor, {
|
|
cwd: path.resolve(parsed.cwd),
|
|
mode: parsed.mode,
|
|
instruction: parsed.instruction,
|
|
purpose: parsed.purpose,
|
|
includeSecret: true,
|
|
});
|
|
|
|
if (!plan.ok) {
|
|
console.error(plan.message ?? '执行器启动计划不可用');
|
|
await pool.end();
|
|
process.exit(1);
|
|
}
|
|
|
|
console.log(`==> ${plan.executorLabel ?? plan.executor} launch plan`);
|
|
console.log(`cwd: ${plan.cwd}`);
|
|
console.log(`cmd: ${plan.command} ${plan.args?.join(' ') ?? ''}`.trim());
|
|
if (plan.notes?.length) {
|
|
for (const note of plan.notes) console.log(`note: ${note}`);
|
|
}
|
|
|
|
const child = spawn(plan.command, plan.args ?? [], {
|
|
cwd: plan.cwd,
|
|
env: { ...process.env, ...(plan.env ?? {}) },
|
|
stdio: 'inherit',
|
|
});
|
|
|
|
const shutdown = (code = 0) => {
|
|
child.kill('SIGTERM');
|
|
setTimeout(() => process.exit(code), 200);
|
|
};
|
|
|
|
process.on('SIGINT', () => shutdown(0));
|
|
process.on('SIGTERM', () => shutdown(0));
|
|
child.on('exit', async (code, signal) => {
|
|
await pool.end().catch(() => {});
|
|
if (signal) {
|
|
process.exit(0);
|
|
return;
|
|
}
|
|
process.exit(code ?? 0);
|
|
});
|
|
child.on('error', async (err) => {
|
|
console.error(err instanceof Error ? err.message : String(err));
|
|
await pool.end().catch(() => {});
|
|
process.exit(1);
|
|
});
|