Files
memind/cursor-agent-launch.mjs
T
john f95d766f69
Memind CI / Test, build, and release guards (push) Failing after 14m36s
feat(billing): bill Cursor executor usage via stream-json token parsing
Parse Cursor CLI usage from stream-json output and charge through billSessionUsage so subscription quota is consumed first and wallet overage applies when needed.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-27 10:16:38 +08:00

110 lines
3.3 KiB
JavaScript

import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
function envFlag(value, fallback = false) {
const raw = String(value ?? '').trim().toLowerCase();
if (!raw) return fallback;
return ['1', 'true', 'yes', 'on'].includes(raw);
}
export function expandHome(value) {
const text = String(value ?? '').trim();
if (!text) return text;
if (text.startsWith('~/')) return path.join(os.homedir(), text.slice(2));
if (text === '~') return os.homedir();
return text;
}
export function cursorExecutorEnabled(env = process.env) {
return envFlag(env.MEMIND_CURSOR_EXECUTOR_ENABLED, false);
}
export function resolvePreferredCodeExecutor(env = process.env) {
if (!cursorExecutorEnabled(env)) return 'aider';
if (envFlag(env.MEMIND_AIDER_SKILL_USE_CURSOR, true)) return 'cursor';
return 'aider';
}
export function cursorPageTasksDefaultEnabled(env = process.env) {
if (!cursorExecutorEnabled(env)) return false;
return envFlag(env.MEMIND_CURSOR_PAGE_TASKS_DEFAULT, true);
}
/** @deprecated 普通 Agent 编排不再默认走 Cursor;仅页面/问卷/Excel 等代码任务使用 enforcePageGenerationCursorRuntime */
export function cursorAgentTasksDefaultEnabled(env = process.env) {
if (!cursorExecutorEnabled(env)) return false;
return envFlag(env.MEMIND_CURSOR_AGENT_TASKS_DEFAULT, false);
}
/** 智趣执行失败时回退 DeepSeek / Goose 会话 */
export function cursorDeepseekFallbackEnabled(env = process.env) {
return envFlag(env.MEMIND_CURSOR_DEEPSEEK_FALLBACK, false);
}
export function resolveCursorAgentBin(env = process.env) {
const configured = expandHome(
env.MEMIND_CURSOR_EXECUTOR_AGENT_BIN
?? env.MEMIND_CURSOR_HELP_AGENT_BIN
?? '~/.local/bin/agent',
);
if (configured && fs.existsSync(configured)) return configured;
return 'agent';
}
export function buildCursorExecutorInstruction(instruction) {
const base = String(instruction ?? '').trim();
if (!base) return base;
return [
base,
'',
'执行要求:',
'- 在当前 MindSpace 工作区(cwd)内完成改动',
'- 页面产物写入 public/ 目录下的 .html 文件',
'- 直接完成可交付产物,不要等待用户确认',
'- 不要执行 git commit、git push、git checkout、git reset、生产发布或远端同步命令',
'- 不要修改当前工作区之外的文件',
'- 完成后在最终输出中简述改动文件与验证结果',
].join('\n');
}
export function buildCursorExecutorLaunchPlan({
cwd,
instruction,
env = process.env,
agentBin = resolveCursorAgentBin(env),
} = {}) {
const workspace = path.resolve(String(cwd ?? env.MEMIND_CURSOR_EXECUTOR_WORKSPACE ?? process.cwd()));
const prompt = buildCursorExecutorInstruction(instruction);
if (!prompt) {
return {
ok: false,
executor: 'cursor',
message: 'TKMind 智趣执行器需要 instruction',
};
}
return {
ok: true,
executor: 'cursor',
executorLabel: 'TKMind 智趣',
cwd: workspace,
command: agentBin,
args: [
'--print',
'--trust',
'--force',
'--approve-mcps',
'--output-format',
'stream-json',
'--workspace',
workspace,
prompt,
],
env: {},
notes: [
'TKMind 智趣执行器在 MindSpace 工作区 headless 执行。',
'Goose 仍负责对话编排,智趣只负责代码/页面落盘。',
],
};
}