Files
memind/scripts/launch-executor.mjs
john 9b4a25799f Add smart ACK provider for WeChat MP replies
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>
2026-06-26 15:19:03 +08:00

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);
});