8f91c52c67
Introduce help escalation persistence, cursor help worker processing, optional OpenAI-compatible chat bridge, and page delivery remediation. Co-authored-by: Cursor <cursoragent@cursor.com>
83 lines
2.5 KiB
JavaScript
83 lines
2.5 KiB
JavaScript
import path from 'node:path';
|
|
import { spawn } from 'node:child_process';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
import {
|
|
extractHelpEscalationPagePath,
|
|
extractHelpEscalationSessionId,
|
|
} from './help-escalation.mjs';
|
|
|
|
const repoRoot = path.join(path.dirname(fileURLToPath(import.meta.url)));
|
|
|
|
function envFlag(value, fallback = true) {
|
|
const raw = String(value ?? '').trim().toLowerCase();
|
|
if (!raw) return fallback;
|
|
return ['1', 'true', 'yes', 'on'].includes(raw);
|
|
}
|
|
|
|
export function shouldAutoRemediateHelpViaGoose(escalation, env = process.env) {
|
|
if (!envFlag(env.MEMIND_CURSOR_HELP_AUTO_GOOSE_REMEDIATE, true)) return false;
|
|
const pagePath = extractHelpEscalationPagePath(escalation?.userText, escalation?.context ?? {});
|
|
return Boolean(pagePath && escalation?.userId);
|
|
}
|
|
|
|
export function runHelpPageDeliveryRemediationScript({
|
|
escalation,
|
|
env = process.env,
|
|
logger = console,
|
|
viaGoose = true,
|
|
} = {}) {
|
|
const userId = escalation?.userId;
|
|
const sessionId = extractHelpEscalationSessionId(escalation);
|
|
const pagePath = extractHelpEscalationPagePath(escalation?.userText, escalation?.context ?? {});
|
|
if (!userId || !pagePath) {
|
|
return Promise.resolve(null);
|
|
}
|
|
|
|
const scriptPath = path.join(repoRoot, 'scripts/help-remediate-page-delivery.mjs');
|
|
const args = [
|
|
scriptPath,
|
|
`--user-id=${userId}`,
|
|
`--page=${pagePath}`,
|
|
`--port=${Number(env.H5_PORT ?? 8081)}`,
|
|
];
|
|
if (sessionId) args.push(`--session-id=${sessionId}`);
|
|
if (viaGoose && sessionId) args.push('--via-goose');
|
|
|
|
logger.info?.('[cursor-help] auto goose remediation', {
|
|
userId,
|
|
sessionId,
|
|
pagePath,
|
|
viaGoose: Boolean(viaGoose && sessionId),
|
|
});
|
|
|
|
return new Promise((resolve, reject) => {
|
|
const child = spawn(process.execPath, args, {
|
|
cwd: repoRoot,
|
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
env,
|
|
});
|
|
let stdout = '';
|
|
let stderr = '';
|
|
child.stdout.on('data', (chunk) => {
|
|
stdout += String(chunk);
|
|
});
|
|
child.stderr.on('data', (chunk) => {
|
|
stderr += String(chunk);
|
|
});
|
|
child.on('error', reject);
|
|
child.on('close', (code) => {
|
|
if (code !== 0) {
|
|
reject(new Error(stderr.trim() || stdout.trim() || `remediation exited ${code}`));
|
|
return;
|
|
}
|
|
try {
|
|
const jsonLine = stdout.trim().split('\n').reverse().find((line) => line.startsWith('{'));
|
|
resolve(jsonLine ? JSON.parse(jsonLine) : { stdout });
|
|
} catch {
|
|
resolve({ stdout });
|
|
}
|
|
});
|
|
});
|
|
}
|