Files
memind/cursor-help-worker.mjs
T
john 8f91c52c67 feat(help): add cursor help escalation worker and chat bridge
Introduce help escalation persistence, cursor help worker processing,
optional OpenAI-compatible chat bridge, and page delivery remediation.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-27 09:33:49 +08:00

172 lines
4.5 KiB
JavaScript

import {
HELP_ESCALATION_STATUS,
createHelpEscalationService,
deliverHelpEscalationResult,
parseHelpEscalationAgentResult,
resolveCursorHelpAgentBin,
resolveCursorHelpWorkspace,
runCursorHelpAgent,
} from './help-escalation.mjs';
import {
runHelpPageDeliveryRemediationScript,
shouldAutoRemediateHelpViaGoose,
} from './help-page-delivery-remediation.mjs';
function envFlag(value, fallback = false) {
const raw = String(value ?? '').trim().toLowerCase();
if (!raw) return fallback;
return ['1', 'true', 'yes', 'on'].includes(raw);
}
export function isCursorHelpWorkerEnabled(env = process.env) {
return envFlag(env.MEMIND_CURSOR_HELP_WORKER_ENABLED, false);
}
export async function processOneHelpEscalation({
helpEscalationService,
scheduleService = null,
sendWechatTextToUser = null,
logger = console,
env = process.env,
workerId = 'cursor-help-worker',
} = {}) {
const escalation = await helpEscalationService.claimNext(workerId);
if (!escalation) return null;
const workspace = resolveCursorHelpWorkspace(env, helpEscalationService.memindRoot);
const agentBin = resolveCursorHelpAgentBin(env);
const prompt = helpEscalationService.buildPrompt(escalation);
try {
const { stdout, stderr } = await runCursorHelpAgent({
prompt,
workspace,
agentBin,
env,
logger,
});
const parsed = parseHelpEscalationAgentResult(stdout);
let resultText = parsed.userMessage;
let agentOutput = stdout;
if (
parsed.status === HELP_ESCALATION_STATUS.SUCCEEDED
&& shouldAutoRemediateHelpViaGoose(escalation, env)
) {
try {
const remediated = await runHelpPageDeliveryRemediationScript({
escalation,
env,
logger,
viaGoose: true,
});
if (remediated?.userMessage) {
resultText = remediated.userMessage;
agentOutput = `${stdout}\n\n--- auto goose remediation ---\n${JSON.stringify(remediated)}`;
}
} catch (remediationError) {
logger.warn?.(
'[cursor-help] auto goose remediation failed:',
remediationError instanceof Error ? remediationError.message : remediationError,
);
}
}
const completed = await helpEscalationService.complete(escalation.id, {
status: parsed.status,
resultText,
agentOutput,
errorMessage: parsed.status === HELP_ESCALATION_STATUS.FAILED
? stderr || parsed.userMessage
: null,
});
await deliverHelpEscalationResult({
escalation: completed,
scheduleService,
sendWechatTextToUser,
logger,
});
return completed;
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
const completed = await helpEscalationService.complete(escalation.id, {
status: HELP_ESCALATION_STATUS.FAILED,
resultText: '工程师处理失败,请稍后重试或联系平台管理员。',
agentOutput: error?.stdout ?? null,
errorMessage: message,
});
await deliverHelpEscalationResult({
escalation: completed,
scheduleService,
sendWechatTextToUser,
logger,
});
return completed;
}
}
export function startCursorHelpWorker({
pool,
scheduleService = null,
sendWechatTextToUser = null,
logger = console,
env = process.env,
pollMs = Number(env.MEMIND_CURSOR_HELP_POLL_MS ?? 2000),
runOnStart = true,
} = {}) {
const helpEscalationService = createHelpEscalationService({ pool, logger, env });
if (!helpEscalationService.enabled || !isCursorHelpWorkerEnabled(env)) {
return { stop() {}, helpEscalationService };
}
let stopped = false;
let running = false;
let timer = null;
const tick = async () => {
if (stopped || running) return;
running = true;
try {
await processOneHelpEscalation({
helpEscalationService,
scheduleService,
sendWechatTextToUser,
logger,
env,
});
} catch (error) {
logger.error?.('[cursor-help] worker tick failed:', error);
} finally {
running = false;
}
};
timer = setInterval(() => {
void tick();
}, pollMs);
timer.unref?.();
if (runOnStart) {
void tick();
}
logger.log?.('[cursor-help] worker enabled');
return {
helpEscalationService,
stop() {
stopped = true;
if (timer) clearInterval(timer);
},
async processOnce() {
return processOneHelpEscalation({
helpEscalationService,
scheduleService,
sendWechatTextToUser,
logger,
env,
});
},
};
}