| 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 |