MCPcopy
hub / github.com/afar1/fieldtheory-cli / invokeEngineAsync

Function invokeEngineAsync

src/engine.ts:426–538  ·  view source on GitHub ↗
(engine: ResolvedEngine, prompt: string, opts: InvokeOptions = {})

Source from the content-addressed store, hash-verified

424 * `spawn` we get direct control and can `child.stdin.end()` immediately.
425 */
426export function invokeEngineAsync(engine: ResolvedEngine, prompt: string, opts: InvokeOptions = {}): Promise<string> {
427 const { bin, args } = engine.config;
428 const timeout = opts.timeout ?? DEFAULT_TIMEOUT;
429 const maxBuffer = opts.maxBuffer ?? DEFAULT_MAXBUF;
430
431 return new Promise((resolve, reject) => {
432 const child = spawn(bin, args(prompt, engine), {
433 stdio: ['pipe', 'pipe', 'pipe'],
434 });
435
436 // Close stdin immediately with EOF so `claude -p` doesn't wait on it.
437 // If spawn itself failed (ENOENT etc) `child.stdin` may be null — guard.
438 try { child.stdin?.end(); } catch { /* spawn error will surface below */ }
439
440 const stdoutChunks: Buffer[] = [];
441 const stderrChunks: Buffer[] = [];
442 let stdoutBytes = 0;
443 let stderrBytes = 0;
444 let settled = false;
445 let timer: ReturnType<typeof setTimeout> | undefined;
446
447 /** Compute the redacted tail of buffered stderr for error reporting. */
448 const stderrTail = () =>
449 redactSecrets(tailString(Buffer.concat(stderrChunks), STDERR_TAIL_BYTES));
450
451 /** Send SIGTERM, then escalate to SIGKILL after a grace period in case
452 * the child traps SIGTERM. `.unref()` so the escalation timer does not
453 * keep the event loop alive past shutdown. */
454 const killChild = () => {
455 try { child.kill('SIGTERM'); } catch { /* already dead */ }
456 const escalate = setTimeout(() => {
457 try { child.kill('SIGKILL'); } catch { /* already dead */ }
458 }, SIGKILL_GRACE_MS);
459 escalate.unref();
460 };
461
462 const fail = (err: EngineInvocationError) => {
463 if (settled) return;
464 settled = true;
465 if (timer !== undefined) clearTimeout(timer);
466 killChild();
467 reject(err);
468 };
469
470 const succeed = (out: string) => {
471 if (settled) return;
472 settled = true;
473 if (timer !== undefined) clearTimeout(timer);
474 resolve(out);
475 };
476
477 child.stdout?.on('data', (d: Buffer) => {
478 stdoutBytes += d.length;
479 if (stdoutBytes > maxBuffer) {
480 const stderr = stderrTail();
481 fail(new EngineInvocationError({
482 engine: engine.name, bin, stderr,
483 killed: true, code: null, signal: null, reason: 'maxbuffer',

Callers 10

doCompileFunction · 0.85
modelOrganizeSeedsFunction · 0.85
askMdFunction · 0.85
stageReadFunction · 0.85
stageSurveyFunction · 0.85
stageGenerateFunction · 0.85
stageCritiqueFunction · 0.85
stageScoreFunction · 0.85
engine.test.tsFile · 0.85

Calls 4

stderrTailFunction · 0.85
failFunction · 0.85
buildMessageFunction · 0.85
succeedFunction · 0.85

Tested by

no test coverage detected