a15c4177de
Memind CI / Test, build, and release guards (push) Successful in 10m3s
Add dry-run, e2e, and poem-page remediation scripts for cursor executor and help escalation verification. Co-authored-by: Cursor <cursoragent@cursor.com>
128 lines
4.1 KiB
JavaScript
128 lines
4.1 KiB
JavaScript
#!/usr/bin/env node
|
|
import fs from 'node:fs';
|
|
import path from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
import { setTimeout as sleep } from 'node:timers/promises';
|
|
import { createDbPool, migrateSchema } from '../db.mjs';
|
|
import {
|
|
HELP_ESCALATION_STATUS,
|
|
createHelpEscalationService,
|
|
isHelpEscalationIntent,
|
|
} from '../help-escalation.mjs';
|
|
import { processOneHelpEscalation } from '../cursor-help-worker.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;
|
|
}
|
|
}
|
|
|
|
loadEnvFile(path.join(root, '.env'));
|
|
loadEnvFile(path.join(root, '.env.local'));
|
|
|
|
const args = process.argv.slice(2);
|
|
const enqueueOnly = args.includes('--enqueue-only');
|
|
const processLocally = args.includes('--process-locally');
|
|
const userIdArg = args.find((item) => item.startsWith('--user-id='))?.slice('--user-id='.length)?.trim() ?? '';
|
|
const waitMs = Number(
|
|
args.find((item) => item.startsWith('--wait-ms='))?.slice('--wait-ms='.length)
|
|
?? process.env.MEMIND_CURSOR_HELP_E2E_WAIT_MS
|
|
?? 10 * 60 * 1000,
|
|
);
|
|
|
|
const pool = createDbPool();
|
|
|
|
async function waitForEscalation(helpEscalationService, escalationId) {
|
|
const deadline = Date.now() + waitMs;
|
|
while (Date.now() < deadline) {
|
|
const current = await helpEscalationService.getById(escalationId);
|
|
if (
|
|
current?.status === HELP_ESCALATION_STATUS.SUCCEEDED
|
|
|| current?.status === HELP_ESCALATION_STATUS.FAILED
|
|
) {
|
|
return current;
|
|
}
|
|
console.log('waiting for worker...', {
|
|
id: escalationId,
|
|
status: current?.status ?? 'missing',
|
|
});
|
|
await sleep(2000);
|
|
}
|
|
throw new Error(`Timed out after ${waitMs}ms waiting for escalation ${escalationId}`);
|
|
}
|
|
|
|
try {
|
|
await migrateSchema(pool);
|
|
|
|
const helpEscalationService = createHelpEscalationService({ pool });
|
|
if (!helpEscalationService.enabled) {
|
|
console.error('MEMIND_CURSOR_HELP_ENABLED is not enabled');
|
|
process.exitCode = 1;
|
|
} else {
|
|
const sampleText = 'help 本机端到端测试:请检查 Portal /api/status 是否正常,并回复一行 JSON 结果。';
|
|
console.log('help intent:', isHelpEscalationIntent(sampleText));
|
|
|
|
let userId = userIdArg;
|
|
if (!userId) {
|
|
const [rows] = await pool.query('SELECT id, username FROM h5_users ORDER BY created_at ASC LIMIT 1');
|
|
userId = rows[0]?.id;
|
|
}
|
|
if (!userId) {
|
|
console.error('No h5_users row found for e2e test');
|
|
process.exitCode = 1;
|
|
} else {
|
|
const escalation = await helpEscalationService.enqueue({
|
|
userId,
|
|
channel: 'h5',
|
|
userText: sampleText,
|
|
context: {
|
|
origin: 'e2e-test',
|
|
requestId: `help-e2e-${Date.now()}`,
|
|
},
|
|
});
|
|
|
|
console.log('enqueued:', {
|
|
id: escalation.id,
|
|
userId: escalation.userId,
|
|
status: escalation.status,
|
|
});
|
|
|
|
if (!enqueueOnly) {
|
|
let completed = null;
|
|
if (processLocally) {
|
|
console.log('processing locally with agent CLI (portal worker should be disabled)...');
|
|
completed = await processOneHelpEscalation({
|
|
helpEscalationService,
|
|
logger: console,
|
|
});
|
|
} else {
|
|
console.log('waiting for portal/standalone cursor-help worker...');
|
|
completed = await waitForEscalation(helpEscalationService, escalation.id);
|
|
}
|
|
|
|
console.log('completed:', {
|
|
id: completed?.id ?? null,
|
|
status: completed?.status ?? null,
|
|
resultText: completed?.resultText ?? null,
|
|
errorMessage: completed?.errorMessage ?? null,
|
|
});
|
|
|
|
if (!completed || completed.status !== HELP_ESCALATION_STATUS.SUCCEEDED) {
|
|
process.exitCode = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} finally {
|
|
await pool.end();
|
|
}
|