返回 CodeWhale
turn-hold.mjs
根目录 / crates / tui / plugins / computer-use / mcp / turn-hold.mjs
1 #!/usr/bin/env node
2 // codewhale-cu-turn-hold — hold a Sprite awake for exactly one turn.
3 //
4 // The Engine spawns this at turn start with a piped stdin and ends it at turn
5 // end by closing stdin (or SIGTERM). It registers a Sprite Task (5 min
6 // expiry), refreshes it every 60 s, and deletes it on stdin EOF, SIGTERM,
7 // SIGINT or SIGHUP. If the Engine dies, stdin closes and the hold is released;
8 // if this process is SIGKILLed, the Task lapses within its expiry.
9 // Receipts are JSON lines on stdout.
10 //
11 // codewhale-cu-turn-hold --name turn-<id> [--expire 5m] [--refresh 60] [--socket /.sprite/api.sock]
12 import { createTaskHold, DEFAULT_SOCKET } from "../src/sprite-task.mjs";
13
14 function arg(flag, fallback) {
15 const i = process.argv.indexOf(flag);
16 return i >= 0 && i + 1 < process.argv.length ? process.argv[i + 1] : fallback;
17 }
18 const emit = (obj) => process.stdout.write(`${JSON.stringify(obj)}\n`);
19
20 let hold;
21 try {
22 hold = createTaskHold({
23 name: arg("--name", null),
24 expire: arg("--expire", "5m"),
25 refreshMs: Number(arg("--refresh", "60")) * 1000,
26 socket: arg("--socket", process.env.CODEWHALE_SPRITE_API_SOCKET || DEFAULT_SOCKET),
27 onEvent: emit,
28 });
29 } catch (error) {
30 emit({ event: "refused", error: error.message, code: error.code ?? "bad_args" });
31 process.exit(2);
32 }
33
34 let ending = false;
35 async function end(code = 0) {
36 if (ending) return;
37 ending = true;
38 await hold.release();
39 process.exit(code);
40 }
41
42 try {
43 await hold.acquire();
44 } catch (error) {
45 emit({ event: "acquire_failed", error: error.message, code: error.code ?? "task_api_error" });
46 process.exit(1);
47 }
48 process.stdin.on("end", () => end(0));
49 process.stdin.on("error", () => end(0));
50 process.stdin.resume();
51 for (const signal of ["SIGTERM", "SIGINT", "SIGHUP"]) process.on(signal, () => end(0));
52
52 lines Plain Text