@toon-protocol/rig 4.6.0 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/README.md +118 -25
  2. package/dist/{chunk-X232W4TG.js → chunk-2TG4YHEC.js} +89 -59
  3. package/dist/chunk-2TG4YHEC.js.map +1 -0
  4. package/dist/{chunk-DCGV6ALH.js → chunk-4WVH2ITA.js} +158 -9
  5. package/dist/chunk-4WVH2ITA.js.map +1 -0
  6. package/dist/{chunk-U5A7MHBP.js → chunk-7VIVQHRG.js} +1 -1
  7. package/dist/chunk-7VIVQHRG.js.map +1 -0
  8. package/dist/chunk-CHVOXCDV.js +0 -0
  9. package/dist/chunk-CHVOXCDV.js.map +1 -0
  10. package/dist/{chunk-XCLUB2CY.js → chunk-LMZSAFAB.js} +44 -15
  11. package/dist/chunk-LMZSAFAB.js.map +1 -0
  12. package/dist/{chunk-JBFBD7HV.js → chunk-QKL53YZS.js} +7 -3
  13. package/dist/chunk-QKL53YZS.js.map +1 -0
  14. package/dist/{chunk-UE5TAFEU.js → chunk-ZQGXFFBP.js} +4 -4
  15. package/dist/chunk-ZQGXFFBP.js.map +1 -0
  16. package/dist/{ci-serve-AYFZOY4O.js → ci-serve-BU7NDT7O.js} +152 -26
  17. package/dist/ci-serve-BU7NDT7O.js.map +1 -0
  18. package/dist/cli/rig.js +6 -5
  19. package/dist/cli/rig.js.map +1 -1
  20. package/dist/index.d.ts +117 -4
  21. package/dist/index.js +40 -7
  22. package/dist/index.js.map +1 -1
  23. package/dist/standalone/index.d.ts +109 -74
  24. package/dist/standalone/index.js +13 -3
  25. package/dist/{standalone-mode-UQSP4677.js → standalone-mode-AT4S5EZG.js} +148 -50
  26. package/dist/standalone-mode-AT4S5EZG.js.map +1 -0
  27. package/package.json +2 -2
  28. package/dist/chunk-DCGV6ALH.js.map +0 -1
  29. package/dist/chunk-JBFBD7HV.js.map +0 -1
  30. package/dist/chunk-U5A7MHBP.js.map +0 -1
  31. package/dist/chunk-UE5TAFEU.js.map +0 -1
  32. package/dist/chunk-X232W4TG.js.map +0 -1
  33. package/dist/chunk-XCLUB2CY.js.map +0 -1
  34. package/dist/ci-serve-AYFZOY4O.js.map +0 -1
  35. package/dist/standalone-mode-UQSP4677.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/cli/rig.ts","../../src/cli/output.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * `rig` — the git-native TOON CLI shipped by `@toon-protocol/rig` (epic\n * #222; standalone-only since #248; git passthrough since #250).\n *\n * rig-owned subcommands (see ./dispatch.ts):\n * init one-shot repo setup (identity + toon.* git config)\n * remote relays as git remotes (#249): add/remove/list\n * push estimate → confirm → execute (#229)\n * issue | comment | pr single NIP-34 event publishes (#231), incl.\n * `pr status` (moved from `rig status` in #250)\n * channel payment channels (#262/#263): list, open,\n * close, settle\n * fund | balance client money lifecycle (#263): devnet faucet\n * drip; wallet + channel balances\n *\n * Everything else is `git <argv...>` verbatim: `rig status` runs git status.\n *\n * STRICT `--json` STDOUT (#265): when a rig-owned command runs with `--json`,\n * stdout carries exactly one JSON document — the process-level stdout guard\n * reroutes every other write (including dependencies' `console.log`) to\n * stderr, the io layer sends human lines to stderr, and the post-dispatch\n * backstop emits an error envelope for paths that bailed before emitting.\n * The git passthrough is exempt: it inherits stdio verbatim (./output.ts).\n * The enforcement matrix in ./strict-json.test.ts mirrors this composition.\n */\n\nimport { createInterface } from 'node:readline/promises';\nimport { dispatch } from './dispatch.js';\nimport {\n calmBootstrapNoise,\n isJsonInvocation,\n makeCliIo,\n redirectStdoutToStderr,\n type RigIo,\n} from './output.js';\n\n/** Real terminal I/O: stdout/stderr sinks + readline y/N confirm. */\nfunction makeIo(jsonMode: boolean): RigIo {\n // Reframe the embedded client's expected announce-402 stderr noise (#280)\n // — installed for every mode, before any command code can run.\n calmBootstrapNoise();\n // In --json mode, patch process.stdout FIRST — before any command (or its\n // dynamically imported dependencies) can write — and keep the only real\n // stdout writer for the machine document.\n const guard = jsonMode ? redirectStdoutToStderr() : undefined;\n return makeCliIo({\n jsonMode,\n writeStdout: guard\n ? guard.write\n : (text) => {\n process.stdout.write(text);\n },\n writeStderr: (text) => {\n process.stderr.write(text);\n },\n isInteractive: Boolean(process.stdin.isTTY && process.stdout.isTTY),\n confirm: async (question) => {\n const rl = createInterface({\n input: process.stdin,\n output: process.stderr,\n });\n try {\n const answer = (await rl.question(question)).trim().toLowerCase();\n return answer === 'y' || answer === 'yes';\n } finally {\n rl.close();\n }\n },\n });\n}\n\n/**\n * Exit as soon as stdio is flushed (#279). By the time dispatch resolves,\n * every command has finished its work and awaited its teardown — but the\n * embedded `@toon-protocol/client` can leave a keep-alive socket behind\n * that holds the Node event loop open for ~30 more seconds. That lingering\n * handle — not discovery or negotiation — was the bulk of the uniform\n * \"~32s per paid command\" tax the #279 study measured. Nothing rig can\n * reach unrefs it, so the one-shot CLI does what one-shot CLIs do: flush\n * and exit.\n */\nfunction exitWhenFlushed(code: number): void {\n process.exitCode = code;\n let pending = 2;\n const done = (): void => {\n pending -= 1;\n if (pending === 0) process.exit(code);\n };\n process.stdout.write('', done);\n process.stderr.write('', done);\n}\n\nconst argv = process.argv.slice(2);\nconst io = makeIo(isJsonInvocation(argv));\n\ndispatch(argv, {\n io,\n env: process.env,\n cwd: process.cwd(),\n}).then(\n (code) => {\n io.ensureSingleJsonDoc(code);\n exitWhenFlushed(code);\n },\n (err: unknown) => {\n process.stderr.write(\n `rig: unexpected error: ${err instanceof Error ? (err.stack ?? err.message) : String(err)}\\n`\n );\n io.ensureSingleJsonDoc(1);\n exitWhenFlushed(1);\n }\n);\n","/**\n * The rig CLI output layer (#265): the strict `--json` stdout guarantee.\n *\n * CONTRACT — when `--json` is set on a rig-owned command, stdout carries\n * EXACTLY ONE parseable JSON document and nothing else. Agents consume this\n * stream (`rig … --json | jq`), so any stray banner, progress line, warning,\n * deprecation nudge, or third-party `console.log` on stdout breaks pipelines\n * (the #260 addendum: `[Bootstrap] …` logs from the embedded client's core\n * polluted `rig push --json`). Everything human-facing goes to stderr.\n *\n * Three layers enforce the contract (composed in ./rig.ts; the enforcement\n * matrix in ./strict-json.test.ts mirrors that composition):\n *\n * 1. {@link makeCliIo} — the ONLY output surface commands use. In JSON mode\n * `out()` (human lines) reroutes to stderr and `emitJson()` is the sole\n * writer that reaches the real stdout.\n * 2. {@link redirectStdoutToStderr} — a process-level guard for code the io\n * seam cannot reach: dependencies that `console.log` directly (the\n * embedded `@toon-protocol/client`'s core bootstrap does). Installed\n * before any command code runs, it reroutes EVERY `process.stdout.write`\n * to stderr; only the writer it returns (wired into `emitJson`) still\n * reaches the real stdout.\n * 3. {@link RigIo.ensureSingleJsonDoc} — the backstop for paths that bail\n * out before emitting an envelope (usage errors, pre-payment refusals):\n * after dispatch it emits one error envelope built from the collected\n * stderr lines, so stdout is never empty in JSON mode.\n *\n * GIT PASSTHROUGH IS EXEMPT: `--json` is a per-subcommand flag on rig-owned\n * verbs, not a global rig flag. Any verb rig does not own goes to system git\n * with the EXACT argv tail and inherited stdio — `rig status --json` runs\n * `git status --json` (git rejects it), and flags BEFORE the subcommand\n * (`rig --json status`) are not rig's either: the whole argv passes through\n * to git verbatim. {@link isJsonInvocation} implements exactly that boundary.\n */\n\n// ---------------------------------------------------------------------------\n// The io seam\n// ---------------------------------------------------------------------------\n\n/** Terminal I/O seam — every rig-owned command writes ONLY through this. */\nexport interface CliIo {\n /**\n * One HUMAN-facing line: stdout normally, stderr when `--json` is active\n * (chatter must never pollute the machine stream).\n */\n out(line: string): void;\n /** One human-facing line to stderr (warnings, nudges, errors). */\n err(line: string): void;\n /**\n * THE machine-readable JSON document of a `--json` run — serialized here\n * (2-space indent) and written to the REAL stdout. Must be a command's\n * single stdout emission; call it at most once per invocation.\n */\n emitJson(payload: unknown): void;\n /** True when stdin+stdout are TTYs (interactive confirm possible). */\n isInteractive: boolean;\n /** Ask a y/N question; resolves true on explicit yes. */\n confirm(question: string): Promise<boolean>;\n}\n\n/** {@link CliIo} plus the JSON-mode bookkeeping the entrypoint needs. */\nexport interface RigIo extends CliIo {\n /** True when this invocation runs in `--json` mode. */\n readonly jsonMode: boolean;\n /** True once `emitJson` has written the machine document. */\n readonly emittedJson: boolean;\n /**\n * The #265 backstop: called once after dispatch (see ./rig.ts). In JSON\n * mode, when NO machine document was emitted (usage error, pre-payment\n * refusal, unexpected crash), emits one error envelope carrying the\n * human-facing lines the run wrote to stderr — so `--json` stdout always\n * parses. No-op outside JSON mode or after a real emission.\n */\n ensureSingleJsonDoc(exitCode: number): void;\n}\n\n/** Sinks + terminal facts {@link makeCliIo} builds a {@link RigIo} from. */\nexport interface CliIoOptions {\n /** Whether this invocation is a rig-owned command with `--json`. */\n jsonMode: boolean;\n /**\n * Write to the REAL stdout (in JSON mode: the pre-guard writer returned by\n * {@link redirectStdoutToStderr}). Receives full text including newlines.\n */\n writeStdout(text: string): void;\n /** Write to stderr. Receives full text including newlines. */\n writeStderr(text: string): void;\n isInteractive: boolean;\n confirm(question: string): Promise<boolean>;\n}\n\n/**\n * Build the {@link RigIo} for one invocation. Centralizes the #265 routing:\n * human lines → stderr in JSON mode, and `emitJson` as the only stdout path.\n */\nexport function makeCliIo(options: CliIoOptions): RigIo {\n const { jsonMode, writeStdout, writeStderr } = options;\n let emittedJson = false;\n /** Human lines collected in JSON mode — the backstop envelope's detail. */\n const humanLines: string[] = [];\n\n const toStderr = (line: string): void => {\n if (jsonMode) humanLines.push(line);\n writeStderr(`${line}\\n`);\n };\n\n return {\n jsonMode,\n get emittedJson() {\n return emittedJson;\n },\n out: (line) => {\n if (jsonMode) {\n toStderr(line);\n } else {\n writeStdout(`${line}\\n`);\n }\n },\n err: toStderr,\n emitJson: (payload) => {\n emittedJson = true;\n writeStdout(`${JSON.stringify(payload, null, 2)}\\n`);\n },\n isInteractive: options.isInteractive,\n confirm: options.confirm,\n ensureSingleJsonDoc(exitCode: number): void {\n if (!jsonMode || emittedJson) return;\n const detail =\n humanLines.join('\\n') ||\n (exitCode === 0\n ? 'the command produced no machine output'\n : 'the command failed before emitting machine output — see stderr');\n this.emitJson(\n exitCode === 0\n ? { error: null, exitCode, detail }\n : { error: 'error', exitCode, detail }\n );\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// JSON-mode detection (and the git-passthrough exemption)\n// ---------------------------------------------------------------------------\n\n/**\n * The verbs rig owns — everything else is `git <argv…>` verbatim. Must match\n * the ./dispatch.ts switch (dispatch.test.ts pins both directions).\n */\nexport const RIG_OWNED_VERBS: ReadonlySet<string> = new Set([\n 'init',\n 'identity',\n 'remote',\n 'clone',\n 'fetch',\n 'push',\n 'site',\n 'issue',\n 'comment',\n 'pr',\n 'maintainers',\n 'payout',\n 'refresh',\n 'channel',\n 'channels',\n 'fund',\n 'balance',\n 'chain',\n 'entry',\n 'name',\n 'ci',\n]);\n\n/**\n * True when this argv is a rig-owned command run with `--json` — the strict\n * stdout contract applies. Deliberately NOT true for:\n *\n * - non-owned verbs (`rig status --json` IS `git status --json`: inherited\n * stdio, git's own semantics, rig adds nothing);\n * - flags before the subcommand (`rig --json status`): rig has no global\n * flags besides help/--version, so the argv passes to git verbatim;\n * - `--json` after a bare `--` (parseArgs treats those tokens as\n * positionals, so rig's flag parsers never see it as a flag either).\n *\n * Mirrors a plain token scan rather than each command's full parseArgs\n * config: the one divergence is `--json` consumed as a STRING option's value\n * (e.g. `rig push --relay --json`) — a pathological invocation that ends up\n * with chatter on stderr and a backstop envelope on stdout, which still\n * honors the parse guarantee.\n */\nexport function isJsonInvocation(argv: string[]): boolean {\n const [verb, ...rest] = argv;\n if (verb === undefined || !RIG_OWNED_VERBS.has(verb)) return false;\n for (const arg of rest) {\n if (arg === '--') return false;\n if (arg === '--json') return true;\n }\n return false;\n}\n\n// ---------------------------------------------------------------------------\n// Process-level stdout guard (third-party console.log defense)\n// ---------------------------------------------------------------------------\n\n/** Handle on an installed stdout guard. */\nexport interface StdoutGuard {\n /** Write to the REAL stdout (bypasses the redirection). */\n write(text: string): void;\n /** Undo the patch (tests; the one-shot CLI process never needs it). */\n restore(): void;\n}\n\n/**\n * Reroute every `process.stdout.write` — including `console.log` from\n * dependencies rig cannot re-route at the io seam, like the embedded\n * client's `[Bootstrap] …` logs (#260) — to stderr. Returns the ONLY writer\n * that still reaches the real stdout; ./rig.ts wires it into\n * {@link makeCliIo}'s `writeStdout` so `emitJson` output is the single\n * survivor on the machine stream.\n *\n * @param realWrite Override the saved real-stdout writer (tests capture it);\n * defaults to the unpatched `process.stdout.write`.\n */\n// ---------------------------------------------------------------------------\n// Process-level stderr calmer (third-party bootstrap noise defense, #280)\n// ---------------------------------------------------------------------------\n\n/** Handle on an installed stderr calmer. */\nexport interface StderrCalmer {\n /** Undo the patch (tests; the one-shot CLI process never needs it). */\n restore(): void;\n}\n\n/** The embedded client's core self-announce failure log line. */\nconst ANNOUNCE_FAILED_RE = /\\[Bootstrap\\] Announce failed/;\n/** … specifically the EXPECTED pre-payment 402 refusal (x402 challenge). */\nconst ANNOUNCE_402_RE = /402|payment required/i;\n\n/**\n * The calm reframe of the announce-402: what happened, why it is expected,\n * and that the command is unaffected — instead of a full x402 challenge JSON\n * dump. Exported so tests pin the exact line.\n */\nexport const ANNOUNCE_402_INFO =\n 'rig: skipped the optional identity self-announce — the payment peer ' +\n 'charges for announces and this identity has not paid for one (expected ' +\n 'on a fresh or unfunded identity); harmless, the command continues.\\n';\n\n/**\n * Reframe the embedded client's scary-but-harmless bootstrap announce noise\n * (#280). The core's bootstrap tries a PAID self-announce and `console.warn`s\n * the refusal — `[Bootstrap] Announce failed …: … (402 Payment Required):\n * {…x402…}` — straight to stderr, which rig's io seam cannot reach. A\n * first-run user reads that as their (succeeding!) push having failed.\n *\n * This process-level guard patches `process.stderr.write`:\n *\n * - an announce-402 line becomes {@link ANNOUNCE_402_INFO} once per\n * process (repeats are dropped — one calm line, not one per peer);\n * - announce failures that are NOT the expected 402 (network errors, peer\n * misconfig) pass through untouched — those ARE signal;\n * - everything else passes through untouched.\n *\n * Installed unconditionally in ./rig.ts before dispatch. Composes with\n * {@link redirectStdoutToStderr}: that guard forwards to `process.stderr.write`\n * via a dynamic property lookup, so rerouted stdout noise is calmed too.\n */\nexport function calmBootstrapNoise(): StderrCalmer {\n const original = process.stderr.write;\n let reframed = false;\n const patched = ((\n chunk: Uint8Array | string,\n encodingOrCb?: BufferEncoding | ((err?: Error | null) => void),\n cb?: (err?: Error | null) => void\n ): boolean => {\n let text = chunk;\n if (typeof chunk === 'string' && ANNOUNCE_FAILED_RE.test(chunk)) {\n if (ANNOUNCE_402_RE.test(chunk)) {\n text = reframed ? '' : ANNOUNCE_402_INFO;\n reframed = true;\n }\n }\n if (typeof encodingOrCb === 'function') {\n // `original` is `typeof process.stderr.write`, an overloaded function\n // type; `.call()` on it only resolves against the LAST overload\n // (chunk, encoding, cb), so the 2-arg (chunk, cb) call needs an\n // explicit narrow to the matching overload signature.\n const writeWithCallback = original as (\n chunk: Uint8Array | string,\n cb?: (err?: Error | null) => void\n ) => boolean;\n return writeWithCallback.call(process.stderr, text, encodingOrCb);\n }\n return original.call(process.stderr, text, encodingOrCb, cb);\n }) as typeof process.stderr.write;\n process.stderr.write = patched;\n return {\n restore: () => {\n if (process.stderr.write === patched) process.stderr.write = original;\n },\n };\n}\n\nexport function redirectStdoutToStderr(\n realWrite?: (text: string) => void\n): StdoutGuard {\n const original = process.stdout.write;\n const real = realWrite ?? original.bind(process.stdout);\n const patched = ((\n chunk: Uint8Array | string,\n encodingOrCb?: BufferEncoding | ((err?: Error | null) => void),\n cb?: (err?: Error | null) => void\n ): boolean =>\n typeof encodingOrCb === 'function'\n ? process.stderr.write(chunk, encodingOrCb)\n : process.stderr.write(\n chunk,\n encodingOrCb,\n cb\n )) as typeof process.stdout.write;\n process.stdout.write = patched;\n return {\n write: (text) => {\n real(text);\n },\n restore: () => {\n if (process.stdout.write === patched) process.stdout.write = original;\n },\n };\n}\n"],"mappings":"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","names":["argv"]}
1
+ {"version":3,"sources":["../../src/cli/rig.ts","../../src/cli/output.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * `rig` — the git-native TOON CLI shipped by `@toon-protocol/rig` (epic\n * #222; standalone-only since #248; git passthrough since #250).\n *\n * rig-owned subcommands (see ./dispatch.ts):\n * init one-shot repo setup (identity + toon.* git config)\n * remote relays as git remotes (#249): add/remove/list\n * push estimate → confirm → execute (#229)\n * issue | comment | pr single NIP-34 event publishes (#231), incl.\n * `pr status` (moved from `rig status` in #250)\n * channel payment channels (#262/#263): list, open,\n * close, settle\n * fund | balance client money lifecycle (#263): devnet faucet\n * drip; wallet + channel balances\n *\n * Everything else is `git <argv...>` verbatim: `rig status` runs git status.\n *\n * STRICT `--json` STDOUT (#265): when a rig-owned command runs with `--json`,\n * stdout carries exactly one JSON document — the process-level stdout guard\n * reroutes every other write (including dependencies' `console.log`) to\n * stderr, the io layer sends human lines to stderr, and the post-dispatch\n * backstop emits an error envelope for paths that bailed before emitting.\n * The git passthrough is exempt: it inherits stdio verbatim (./output.ts).\n * The enforcement matrix in ./strict-json.test.ts mirrors this composition.\n */\n\nimport { createInterface } from 'node:readline/promises';\nimport { dispatch } from './dispatch.js';\nimport {\n calmBootstrapNoise,\n isJsonInvocation,\n makeCliIo,\n redirectStdoutToStderr,\n type RigIo,\n} from './output.js';\n\n/** Real terminal I/O: stdout/stderr sinks + readline y/N confirm. */\nfunction makeIo(jsonMode: boolean): RigIo {\n // Reframe the embedded client's expected announce-402 stderr noise (#280)\n // — installed for every mode, before any command code can run.\n calmBootstrapNoise();\n // In --json mode, patch process.stdout FIRST — before any command (or its\n // dynamically imported dependencies) can write — and keep the only real\n // stdout writer for the machine document.\n const guard = jsonMode ? redirectStdoutToStderr() : undefined;\n return makeCliIo({\n jsonMode,\n writeStdout: guard\n ? guard.write\n : (text) => {\n process.stdout.write(text);\n },\n writeStderr: (text) => {\n process.stderr.write(text);\n },\n isInteractive: Boolean(process.stdin.isTTY && process.stdout.isTTY),\n confirm: async (question) => {\n const rl = createInterface({\n input: process.stdin,\n output: process.stderr,\n });\n try {\n const answer = (await rl.question(question)).trim().toLowerCase();\n return answer === 'y' || answer === 'yes';\n } finally {\n rl.close();\n }\n },\n });\n}\n\n/**\n * Exit as soon as stdio is flushed (#279). By the time dispatch resolves,\n * every command has finished its work and awaited its teardown — but the\n * embedded `@toon-protocol/client` can leave a keep-alive socket behind\n * that holds the Node event loop open for ~30 more seconds. That lingering\n * handle — not discovery or negotiation — was the bulk of the uniform\n * \"~32s per paid command\" tax the #279 study measured. Nothing rig can\n * reach unrefs it, so the one-shot CLI does what one-shot CLIs do: flush\n * and exit.\n */\nfunction exitWhenFlushed(code: number): void {\n process.exitCode = code;\n let pending = 2;\n const done = (): void => {\n pending -= 1;\n if (pending === 0) process.exit(code);\n };\n process.stdout.write('', done);\n process.stderr.write('', done);\n}\n\nconst argv = process.argv.slice(2);\nconst io = makeIo(isJsonInvocation(argv));\n\ndispatch(argv, {\n io,\n env: process.env,\n cwd: process.cwd(),\n}).then(\n (code) => {\n io.ensureSingleJsonDoc(code);\n exitWhenFlushed(code);\n },\n (err: unknown) => {\n process.stderr.write(\n `rig: unexpected error: ${err instanceof Error ? (err.stack ?? err.message) : String(err)}\\n`\n );\n io.ensureSingleJsonDoc(1);\n exitWhenFlushed(1);\n }\n);\n","/**\n * The rig CLI output layer (#265): the strict `--json` stdout guarantee.\n *\n * CONTRACT — when `--json` is set on a rig-owned command, stdout carries\n * EXACTLY ONE parseable JSON document and nothing else. Agents consume this\n * stream (`rig … --json | jq`), so any stray banner, progress line, warning,\n * deprecation nudge, or third-party `console.log` on stdout breaks pipelines\n * (the #260 addendum: `[Bootstrap] …` logs from the embedded client's core\n * polluted `rig push --json`). Everything human-facing goes to stderr.\n *\n * Three layers enforce the contract (composed in ./rig.ts; the enforcement\n * matrix in ./strict-json.test.ts mirrors that composition):\n *\n * 1. {@link makeCliIo} — the ONLY output surface commands use. In JSON mode\n * `out()` (human lines) reroutes to stderr and `emitJson()` is the sole\n * writer that reaches the real stdout.\n * 2. {@link redirectStdoutToStderr} — a process-level guard for code the io\n * seam cannot reach: dependencies that `console.log` directly (the\n * embedded `@toon-protocol/client`'s core bootstrap does). Installed\n * before any command code runs, it reroutes EVERY `process.stdout.write`\n * to stderr; only the writer it returns (wired into `emitJson`) still\n * reaches the real stdout.\n * 3. {@link RigIo.ensureSingleJsonDoc} — the backstop for paths that bail\n * out before emitting an envelope (usage errors, pre-payment refusals):\n * after dispatch it emits one error envelope built from the collected\n * stderr lines, so stdout is never empty in JSON mode.\n *\n * GIT PASSTHROUGH IS EXEMPT: `--json` is a per-subcommand flag on rig-owned\n * verbs, not a global rig flag. Any verb rig does not own goes to system git\n * with the EXACT argv tail and inherited stdio — `rig status --json` runs\n * `git status --json` (git rejects it), and flags BEFORE the subcommand\n * (`rig --json status`) are not rig's either: the whole argv passes through\n * to git verbatim. {@link isJsonInvocation} implements exactly that boundary.\n */\n\n// ---------------------------------------------------------------------------\n// The io seam\n// ---------------------------------------------------------------------------\n\n/** Terminal I/O seam — every rig-owned command writes ONLY through this. */\nexport interface CliIo {\n /**\n * One HUMAN-facing line: stdout normally, stderr when `--json` is active\n * (chatter must never pollute the machine stream).\n */\n out(line: string): void;\n /** One human-facing line to stderr (warnings, nudges, errors). */\n err(line: string): void;\n /**\n * THE machine-readable JSON document of a `--json` run — serialized here\n * (2-space indent) and written to the REAL stdout. Must be a command's\n * single stdout emission; call it at most once per invocation.\n */\n emitJson(payload: unknown): void;\n /** True when stdin+stdout are TTYs (interactive confirm possible). */\n isInteractive: boolean;\n /** Ask a y/N question; resolves true on explicit yes. */\n confirm(question: string): Promise<boolean>;\n}\n\n/** {@link CliIo} plus the JSON-mode bookkeeping the entrypoint needs. */\nexport interface RigIo extends CliIo {\n /** True when this invocation runs in `--json` mode. */\n readonly jsonMode: boolean;\n /** True once `emitJson` has written the machine document. */\n readonly emittedJson: boolean;\n /**\n * The #265 backstop: called once after dispatch (see ./rig.ts). In JSON\n * mode, when NO machine document was emitted (usage error, pre-payment\n * refusal, unexpected crash), emits one error envelope carrying the\n * human-facing lines the run wrote to stderr — so `--json` stdout always\n * parses. No-op outside JSON mode or after a real emission.\n */\n ensureSingleJsonDoc(exitCode: number): void;\n}\n\n/** Sinks + terminal facts {@link makeCliIo} builds a {@link RigIo} from. */\nexport interface CliIoOptions {\n /** Whether this invocation is a rig-owned command with `--json`. */\n jsonMode: boolean;\n /**\n * Write to the REAL stdout (in JSON mode: the pre-guard writer returned by\n * {@link redirectStdoutToStderr}). Receives full text including newlines.\n */\n writeStdout(text: string): void;\n /** Write to stderr. Receives full text including newlines. */\n writeStderr(text: string): void;\n isInteractive: boolean;\n confirm(question: string): Promise<boolean>;\n}\n\n/**\n * Build the {@link RigIo} for one invocation. Centralizes the #265 routing:\n * human lines → stderr in JSON mode, and `emitJson` as the only stdout path.\n */\nexport function makeCliIo(options: CliIoOptions): RigIo {\n const { jsonMode, writeStdout, writeStderr } = options;\n let emittedJson = false;\n /** Human lines collected in JSON mode — the backstop envelope's detail. */\n const humanLines: string[] = [];\n\n const toStderr = (line: string): void => {\n if (jsonMode) humanLines.push(line);\n writeStderr(`${line}\\n`);\n };\n\n return {\n jsonMode,\n get emittedJson() {\n return emittedJson;\n },\n out: (line) => {\n if (jsonMode) {\n toStderr(line);\n } else {\n writeStdout(`${line}\\n`);\n }\n },\n err: toStderr,\n emitJson: (payload) => {\n emittedJson = true;\n writeStdout(`${JSON.stringify(payload, null, 2)}\\n`);\n },\n isInteractive: options.isInteractive,\n confirm: options.confirm,\n ensureSingleJsonDoc(exitCode: number): void {\n if (!jsonMode || emittedJson) return;\n const detail =\n humanLines.join('\\n') ||\n (exitCode === 0\n ? 'the command produced no machine output'\n : 'the command failed before emitting machine output — see stderr');\n this.emitJson(\n exitCode === 0\n ? { error: null, exitCode, detail }\n : { error: 'error', exitCode, detail }\n );\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// JSON-mode detection (and the git-passthrough exemption)\n// ---------------------------------------------------------------------------\n\n/**\n * The verbs rig owns — everything else is `git <argv…>` verbatim. Must match\n * the ./dispatch.ts switch (dispatch.test.ts pins both directions).\n */\nexport const RIG_OWNED_VERBS: ReadonlySet<string> = new Set([\n 'init',\n 'identity',\n 'remote',\n 'clone',\n 'fetch',\n 'push',\n 'site',\n 'issue',\n 'comment',\n 'pr',\n 'maintainers',\n 'payout',\n 'refresh',\n 'channel',\n 'channels',\n 'fund',\n 'balance',\n 'chain',\n 'entry',\n 'name',\n 'ci',\n]);\n\n/**\n * True when this argv is a rig-owned command run with `--json` — the strict\n * stdout contract applies. Deliberately NOT true for:\n *\n * - non-owned verbs (`rig status --json` IS `git status --json`: inherited\n * stdio, git's own semantics, rig adds nothing);\n * - flags before the subcommand (`rig --json status`): rig has no global\n * flags besides help/--version, so the argv passes to git verbatim;\n * - `--json` after a bare `--` (parseArgs treats those tokens as\n * positionals, so rig's flag parsers never see it as a flag either).\n *\n * Mirrors a plain token scan rather than each command's full parseArgs\n * config: the one divergence is `--json` consumed as a STRING option's value\n * (e.g. `rig push --relay --json`) — a pathological invocation that ends up\n * with chatter on stderr and a backstop envelope on stdout, which still\n * honors the parse guarantee.\n */\nexport function isJsonInvocation(argv: string[]): boolean {\n const [verb, ...rest] = argv;\n if (verb === undefined || !RIG_OWNED_VERBS.has(verb)) return false;\n for (const arg of rest) {\n if (arg === '--') return false;\n if (arg === '--json') return true;\n }\n return false;\n}\n\n// ---------------------------------------------------------------------------\n// Process-level stdout guard (third-party console.log defense)\n// ---------------------------------------------------------------------------\n\n/** Handle on an installed stdout guard. */\nexport interface StdoutGuard {\n /** Write to the REAL stdout (bypasses the redirection). */\n write(text: string): void;\n /** Undo the patch (tests; the one-shot CLI process never needs it). */\n restore(): void;\n}\n\n/**\n * Reroute every `process.stdout.write` — including `console.log` from\n * dependencies rig cannot re-route at the io seam, like the embedded\n * client's `[Bootstrap] …` logs (#260) — to stderr. Returns the ONLY writer\n * that still reaches the real stdout; ./rig.ts wires it into\n * {@link makeCliIo}'s `writeStdout` so `emitJson` output is the single\n * survivor on the machine stream.\n *\n * @param realWrite Override the saved real-stdout writer (tests capture it);\n * defaults to the unpatched `process.stdout.write`.\n */\n// ---------------------------------------------------------------------------\n// Process-level stderr calmer (third-party bootstrap noise defense, #280)\n// ---------------------------------------------------------------------------\n\n/** Handle on an installed stderr calmer. */\nexport interface StderrCalmer {\n /** Undo the patch (tests; the one-shot CLI process never needs it). */\n restore(): void;\n}\n\n/** The embedded client's core self-announce failure log line. */\nconst ANNOUNCE_FAILED_RE = /\\[Bootstrap\\] Announce failed/;\n/** … specifically the EXPECTED pre-payment 402 refusal (x402 challenge). */\nconst ANNOUNCE_402_RE = /402|payment required/i;\n\n/**\n * The calm reframe of the announce-402: what happened, why it is expected,\n * and that the command is unaffected — instead of a full x402 challenge JSON\n * dump. Exported so tests pin the exact line.\n */\nexport const ANNOUNCE_402_INFO =\n 'rig: skipped the optional identity self-announce — the payment peer ' +\n 'charges for announces and this identity has not paid for one (expected ' +\n 'on a fresh or unfunded identity); harmless, the command continues.\\n';\n\n/**\n * Reframe the embedded client's scary-but-harmless bootstrap announce noise\n * (#280). The core's bootstrap tries a PAID self-announce and `console.warn`s\n * the refusal — `[Bootstrap] Announce failed …: … (402 Payment Required):\n * {…x402…}` — straight to stderr, which rig's io seam cannot reach. A\n * first-run user reads that as their (succeeding!) push having failed.\n *\n * This process-level guard patches `process.stderr.write`:\n *\n * - an announce-402 line becomes {@link ANNOUNCE_402_INFO} once per\n * process (repeats are dropped — one calm line, not one per peer);\n * - announce failures that are NOT the expected 402 (network errors, peer\n * misconfig) pass through untouched — those ARE signal;\n * - everything else passes through untouched.\n *\n * Installed unconditionally in ./rig.ts before dispatch. Composes with\n * {@link redirectStdoutToStderr}: that guard forwards to `process.stderr.write`\n * via a dynamic property lookup, so rerouted stdout noise is calmed too.\n */\nexport function calmBootstrapNoise(): StderrCalmer {\n const original = process.stderr.write;\n let reframed = false;\n const patched = ((\n chunk: Uint8Array | string,\n encodingOrCb?: BufferEncoding | ((err?: Error | null) => void),\n cb?: (err?: Error | null) => void\n ): boolean => {\n let text = chunk;\n if (typeof chunk === 'string' && ANNOUNCE_FAILED_RE.test(chunk)) {\n if (ANNOUNCE_402_RE.test(chunk)) {\n text = reframed ? '' : ANNOUNCE_402_INFO;\n reframed = true;\n }\n }\n if (typeof encodingOrCb === 'function') {\n // `original` is `typeof process.stderr.write`, an overloaded function\n // type; `.call()` on it only resolves against the LAST overload\n // (chunk, encoding, cb), so the 2-arg (chunk, cb) call needs an\n // explicit narrow to the matching overload signature.\n const writeWithCallback = original as (\n chunk: Uint8Array | string,\n cb?: (err?: Error | null) => void\n ) => boolean;\n return writeWithCallback.call(process.stderr, text, encodingOrCb);\n }\n return original.call(process.stderr, text, encodingOrCb, cb);\n }) as typeof process.stderr.write;\n process.stderr.write = patched;\n return {\n restore: () => {\n if (process.stderr.write === patched) process.stderr.write = original;\n },\n };\n}\n\nexport function redirectStdoutToStderr(\n realWrite?: (text: string) => void\n): StdoutGuard {\n const original = process.stdout.write;\n const real = realWrite ?? original.bind(process.stdout);\n const patched = ((\n chunk: Uint8Array | string,\n encodingOrCb?: BufferEncoding | ((err?: Error | null) => void),\n cb?: (err?: Error | null) => void\n ): boolean =>\n typeof encodingOrCb === 'function'\n ? process.stderr.write(chunk, encodingOrCb)\n : process.stderr.write(\n chunk,\n encodingOrCb,\n cb\n )) as typeof process.stdout.write;\n process.stdout.write = patched;\n return {\n write: (text) => {\n real(text);\n },\n restore: () => {\n if (process.stdout.write === patched) process.stdout.write = original;\n },\n };\n}\n"],"mappings":";;;;;;;;;;;AA2BA,SAAS,uBAAuB;;;ACoEzB,SAAS,UAAU,SAA8B;AACtD,QAAM,EAAE,UAAU,aAAa,YAAY,IAAI;AAC/C,MAAI,cAAc;AAElB,QAAM,aAAuB,CAAC;AAE9B,QAAM,WAAW,CAAC,SAAuB;AACvC,QAAI,SAAU,YAAW,KAAK,IAAI;AAClC,gBAAY,GAAG,IAAI;AAAA,CAAI;AAAA,EACzB;AAEA,SAAO;AAAA,IACL;AAAA,IACA,IAAI,cAAc;AAChB,aAAO;AAAA,IACT;AAAA,IACA,KAAK,CAAC,SAAS;AACb,UAAI,UAAU;AACZ,iBAAS,IAAI;AAAA,MACf,OAAO;AACL,oBAAY,GAAG,IAAI;AAAA,CAAI;AAAA,MACzB;AAAA,IACF;AAAA,IACA,KAAK;AAAA,IACL,UAAU,CAAC,YAAY;AACrB,oBAAc;AACd,kBAAY,GAAG,KAAK,UAAU,SAAS,MAAM,CAAC,CAAC;AAAA,CAAI;AAAA,IACrD;AAAA,IACA,eAAe,QAAQ;AAAA,IACvB,SAAS,QAAQ;AAAA,IACjB,oBAAoB,UAAwB;AAC1C,UAAI,CAAC,YAAY,YAAa;AAC9B,YAAM,SACJ,WAAW,KAAK,IAAI,MACnB,aAAa,IACV,2CACA;AACN,WAAK;AAAA,QACH,aAAa,IACT,EAAE,OAAO,MAAM,UAAU,OAAO,IAChC,EAAE,OAAO,SAAS,UAAU,OAAO;AAAA,MACzC;AAAA,IACF;AAAA,EACF;AACF;AAUO,IAAM,kBAAuC,oBAAI,IAAI;AAAA,EAC1D;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAmBM,SAAS,iBAAiBA,OAAyB;AACxD,QAAM,CAAC,MAAM,GAAG,IAAI,IAAIA;AACxB,MAAI,SAAS,UAAa,CAAC,gBAAgB,IAAI,IAAI,EAAG,QAAO;AAC7D,aAAW,OAAO,MAAM;AACtB,QAAI,QAAQ,KAAM,QAAO;AACzB,QAAI,QAAQ,SAAU,QAAO;AAAA,EAC/B;AACA,SAAO;AACT;AAoCA,IAAM,qBAAqB;AAE3B,IAAM,kBAAkB;AAOjB,IAAM,oBACX;AAuBK,SAAS,qBAAmC;AACjD,QAAM,WAAW,QAAQ,OAAO;AAChC,MAAI,WAAW;AACf,QAAM,WAAW,CACf,OACA,cACA,OACY;AACZ,QAAI,OAAO;AACX,QAAI,OAAO,UAAU,YAAY,mBAAmB,KAAK,KAAK,GAAG;AAC/D,UAAI,gBAAgB,KAAK,KAAK,GAAG;AAC/B,eAAO,WAAW,KAAK;AACvB,mBAAW;AAAA,MACb;AAAA,IACF;AACA,QAAI,OAAO,iBAAiB,YAAY;AAKtC,YAAM,oBAAoB;AAI1B,aAAO,kBAAkB,KAAK,QAAQ,QAAQ,MAAM,YAAY;AAAA,IAClE;AACA,WAAO,SAAS,KAAK,QAAQ,QAAQ,MAAM,cAAc,EAAE;AAAA,EAC7D;AACA,UAAQ,OAAO,QAAQ;AACvB,SAAO;AAAA,IACL,SAAS,MAAM;AACb,UAAI,QAAQ,OAAO,UAAU,QAAS,SAAQ,OAAO,QAAQ;AAAA,IAC/D;AAAA,EACF;AACF;AAEO,SAAS,uBACd,WACa;AACb,QAAM,WAAW,QAAQ,OAAO;AAChC,QAAM,OAAO,aAAa,SAAS,KAAK,QAAQ,MAAM;AACtD,QAAM,WAAW,CACf,OACA,cACA,OAEA,OAAO,iBAAiB,aACpB,QAAQ,OAAO,MAAM,OAAO,YAAY,IACxC,QAAQ,OAAO;AAAA,IACb;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACN,UAAQ,OAAO,QAAQ;AACvB,SAAO;AAAA,IACL,OAAO,CAAC,SAAS;AACf,WAAK,IAAI;AAAA,IACX;AAAA,IACA,SAAS,MAAM;AACb,UAAI,QAAQ,OAAO,UAAU,QAAS,SAAQ,OAAO,QAAQ;AAAA,IAC/D;AAAA,EACF;AACF;;;ADnSA,SAAS,OAAO,UAA0B;AAGxC,qBAAmB;AAInB,QAAM,QAAQ,WAAW,uBAAuB,IAAI;AACpD,SAAO,UAAU;AAAA,IACf;AAAA,IACA,aAAa,QACT,MAAM,QACN,CAAC,SAAS;AACR,cAAQ,OAAO,MAAM,IAAI;AAAA,IAC3B;AAAA,IACJ,aAAa,CAAC,SAAS;AACrB,cAAQ,OAAO,MAAM,IAAI;AAAA,IAC3B;AAAA,IACA,eAAe,QAAQ,QAAQ,MAAM,SAAS,QAAQ,OAAO,KAAK;AAAA,IAClE,SAAS,OAAO,aAAa;AAC3B,YAAM,KAAK,gBAAgB;AAAA,QACzB,OAAO,QAAQ;AAAA,QACf,QAAQ,QAAQ;AAAA,MAClB,CAAC;AACD,UAAI;AACF,cAAM,UAAU,MAAM,GAAG,SAAS,QAAQ,GAAG,KAAK,EAAE,YAAY;AAChE,eAAO,WAAW,OAAO,WAAW;AAAA,MACtC,UAAE;AACA,WAAG,MAAM;AAAA,MACX;AAAA,IACF;AAAA,EACF,CAAC;AACH;AAYA,SAAS,gBAAgB,MAAoB;AAC3C,UAAQ,WAAW;AACnB,MAAI,UAAU;AACd,QAAM,OAAO,MAAY;AACvB,eAAW;AACX,QAAI,YAAY,EAAG,SAAQ,KAAK,IAAI;AAAA,EACtC;AACA,UAAQ,OAAO,MAAM,IAAI,IAAI;AAC7B,UAAQ,OAAO,MAAM,IAAI,IAAI;AAC/B;AAEA,IAAM,OAAO,QAAQ,KAAK,MAAM,CAAC;AACjC,IAAM,KAAK,OAAO,iBAAiB,IAAI,CAAC;AAExC,SAAS,MAAM;AAAA,EACb;AAAA,EACA,KAAK,QAAQ;AAAA,EACb,KAAK,QAAQ,IAAI;AACnB,CAAC,EAAE;AAAA,EACD,CAAC,SAAS;AACR,OAAG,oBAAoB,IAAI;AAC3B,oBAAgB,IAAI;AAAA,EACtB;AAAA,EACA,CAAC,QAAiB;AAChB,YAAQ,OAAO;AAAA,MACb,0BAA0B,eAAe,QAAS,IAAI,SAAS,IAAI,UAAW,OAAO,GAAG,CAAC;AAAA;AAAA,IAC3F;AACA,OAAG,oBAAoB,CAAC;AACxB,oBAAgB,CAAC;AAAA,EACnB;AACF;","names":["argv"]}
package/dist/index.d.ts CHANGED
@@ -424,8 +424,10 @@ interface FetchRemoteStateOptions {
424
424
  timeoutMs?: number;
425
425
  /**
426
426
  * Git-SHA → Arweave txId resolver used by {@link RemoteState.resolveMissing}.
427
- * Defaults to the shared GraphQL resolver from @toon-protocol/arweave;
428
- * injectable for tests.
427
+ * Defaults to the shared GraphQL resolver from @toon-protocol/arweave,
428
+ * which asks `arweave.net` — so every command that takes a gateway passes
429
+ * one bound to it instead (`../git-sha-resolver.ts`, #183). Injectable for
430
+ * tests.
429
431
  */
430
432
  resolveSha?: (sha: string, repo: string) => Promise<string | null>;
431
433
  /** WebSocket constructor override (defaults to the global WebSocket). */
@@ -1674,6 +1676,13 @@ declare const CI_MANUAL_TRIGGER_KIND = 9840;
1674
1676
  declare const CI_JOB_RESULT_KIND = 9841;
1675
1677
  declare const CI_WORKFLOW_RESULT_KIND = 9842;
1676
1678
  declare const CI_WORKFLOW_PROGRESS_KIND = 39842;
1679
+ /**
1680
+ * rig's NIP-C1 EXTENSION kind (ADR-0002, docs/specs/nip-c1-live-log-tail.md):
1681
+ * the live log tail of a run in flight. Not in the vendored spec — the number
1682
+ * is rig's choice, offered upstream to ngit-ci, and renumbering it is a deploy
1683
+ * rather than a migration because the event holds no durable data.
1684
+ */
1685
+ declare const CI_LIVE_LOG_TAIL_KIND = 39841;
1677
1686
  /** The one runner family rig supports (`W` tag / `R` prefix). */
1678
1687
  declare const CI_RUNNER_FAMILY = "act";
1679
1688
  /** The `software` tag name rig advertises under. */
@@ -1681,6 +1690,35 @@ declare const CI_SOFTWARE = "rig";
1681
1690
  /** NIP-40 bound on Advertisement + Progress lifetime (seconds). */
1682
1691
  declare const CI_MAX_ADVERTISEMENT_TTL: number;
1683
1692
  declare const CI_MAX_PROGRESS_TTL: number;
1693
+ /** The same NIP-40 bound on a Live Log Tail (seconds). */
1694
+ declare const CI_MAX_LIVE_LOG_TAIL_TTL: number;
1695
+ /**
1696
+ * How often a run in flight republishes its tail. Fixed — a fixed interval is
1697
+ * what makes the worst case budgetable — and expressed in milliseconds because
1698
+ * both consumers (the coordinator's scheduler seam and `estimateRunCost`'s run
1699
+ * timeout) work in milliseconds.
1700
+ */
1701
+ declare const CI_LIVE_LOG_TAIL_INTERVAL_MS = 10000;
1702
+ /**
1703
+ * Bytes of tail one live job gets by default: deliberately far more than the
1704
+ * 4 KiB that rides in a Job Result, because the relay fee ignores event size
1705
+ * and the difference between one screen and several is the difference between
1706
+ * "is it alive" and "what is it doing".
1707
+ */
1708
+ declare const CI_LIVE_LOG_TAIL_JOB_BYTES: number;
1709
+ /**
1710
+ * Ceiling on the tail bytes ONE event may carry, so a run with many live jobs
1711
+ * stays relay-safe. Beyond it the budget is divided evenly — see
1712
+ * {@link liveLogTailBudget}.
1713
+ */
1714
+ declare const CI_LIVE_LOG_TAIL_MAX_BYTES: number;
1715
+ /**
1716
+ * The distinguished key naming the runner channel in a coordinator's per-job
1717
+ * buffers. It is not a job id and can never collide with one (a workflow job id
1718
+ * matches `[A-Za-z_][A-Za-z0-9_-]*`), and it never appears in the wire event's
1719
+ * `jobs` array — the runner channel has its own `runner` slot.
1720
+ */
1721
+ declare const CI_RUNNER_CHANNEL_KEY = "~runner";
1684
1722
  type CiConclusion = 'success' | 'failure' | 'neutral' | 'cancelled' | 'skipped' | 'timed_out' | 'startup_failure';
1685
1723
  declare function isCiConclusion(value: string): value is CiConclusion;
1686
1724
  type CiProgressStatus = 'queued' | 'in_progress' | 'concluded';
@@ -1981,6 +2019,76 @@ interface CiWorkflowProgress {
1981
2019
  }
1982
2020
  declare function buildCiWorkflowProgress(input: CiWorkflowProgressInput, createdAt?: number): UnsignedEvent;
1983
2021
  declare function parseCiWorkflowProgress(ev: NostrEvent): CiWorkflowProgress | null;
2022
+ /** One unfinished job's recent output. */
2023
+ interface CiLiveLogTailJob {
2024
+ /** Job id as declared in the workflow file. */
2025
+ job: string;
2026
+ /** The most recent output, sliced from the END of the redacted log. */
2027
+ tail: string;
2028
+ /** Bytes of that job's output preceding `tail`. */
2029
+ omitted: number;
2030
+ }
2031
+ /** The runner channel: the runner's own account of the run, never a job. */
2032
+ interface CiLiveLogTailChannel {
2033
+ tail: string;
2034
+ omitted: number;
2035
+ }
2036
+ interface CiLiveLogTailInput {
2037
+ trigger: CiTriggerContext;
2038
+ /** The run's Workflow Progress `d`. */
2039
+ runId: string;
2040
+ /** Every unfinished job; a job drops out once its Job Result is published. */
2041
+ jobs: CiLiveLogTailJob[];
2042
+ runner?: CiLiveLogTailChannel;
2043
+ /** NIP-40 expiration: > created_at, ≤ created_at + 30 min. */
2044
+ expiresAt: number;
2045
+ }
2046
+ interface CiLiveLogTail {
2047
+ eventId: string;
2048
+ pubkey: string;
2049
+ createdAt: number;
2050
+ trigger: CiTriggerContext;
2051
+ runId: string;
2052
+ jobs: CiLiveLogTailJob[];
2053
+ runner?: CiLiveLogTailChannel;
2054
+ expiresAt: number;
2055
+ }
2056
+ /**
2057
+ * Bytes of tail each live entry gets when `entries` of them share one event:
2058
+ * the per-job default until that many entries would breach the per-event
2059
+ * ceiling, then the ceiling divided evenly. Entries are unfinished jobs PLUS
2060
+ * the runner channel when it has output — everything that occupies bytes in
2061
+ * the event.
2062
+ */
2063
+ declare function liveLogTailBudget(entries: number): number;
2064
+ /**
2065
+ * Worst-case number of Live Log Tail events a run of at most `runTimeoutMs`
2066
+ * can publish: one per cadence tick plus the closing replacement. This is what
2067
+ * the coordinator must be able to afford BEFORE it starts the run — there is
2068
+ * no best-effort mode, because a stream that silently stops is
2069
+ * indistinguishable to a viewer from a stalled job.
2070
+ */
2071
+ declare function liveLogTailEventBudget(runTimeoutMs: number): number;
2072
+ /**
2073
+ * Take the last `maxBytes` UTF-8 bytes of `text`, reporting how many bytes
2074
+ * precede them. The slice never splits a multi-byte character, and `omitted`
2075
+ * counts BYTES (what a reader comparing against the job log's size needs), not
2076
+ * characters. Callers slice REDACTED output — never the reverse (ADR-0002).
2077
+ */
2078
+ declare function sliceLogTail(text: string, maxBytes: number): {
2079
+ tail: string;
2080
+ omitted: number;
2081
+ };
2082
+ declare function buildCiLiveLogTail(input: CiLiveLogTailInput, createdAt?: number): UnsignedEvent;
2083
+ /**
2084
+ * Parse a Live Log Tail. Unknown fields — anywhere in the content, and unknown
2085
+ * tags — are ignored, so a later rig may add to the shape without breaking a
2086
+ * client; anything the shape does NOT allow (bad JSON, a jobs entry without a
2087
+ * job id, a duplicated job, a runner channel smuggled in as a job, an
2088
+ * expiration beyond the 30-minute bound) is `null` rather than a throw or a
2089
+ * half-populated result.
2090
+ */
2091
+ declare function parseCiLiveLogTail(ev: NostrEvent): CiLiveLogTail | null;
1984
2092
 
1985
2093
  /**
1986
2094
  * Repository Secret Update payloads (rig#125): the NIP-44 v2 ciphertext a
@@ -2172,7 +2280,12 @@ interface RunnerRequest {
2172
2280
  env?: Record<string, string>;
2173
2281
  /** Wall-clock budget for the whole run; exceeding it concludes `timed_out`. */
2174
2282
  timeoutMs: number;
2175
- /** Streaming log sink (per job). */
2283
+ /**
2284
+ * Streaming log sink, called with a job id as output is produced. Output
2285
+ * the runner attributes to no job — its own account of executing the run —
2286
+ * goes to the RUNNER CHANNEL: the reserved key `CI_RUNNER_CHANNEL_KEY`
2287
+ * exported by ./nip-c1-events.ts, which no workflow job id can equal.
2288
+ */
2176
2289
  onLog?: (jobId: string, chunk: string) => void;
2177
2290
  /** Cancels the run (concludes `cancelled`). */
2178
2291
  signal?: AbortSignal;
@@ -2519,4 +2632,4 @@ declare const NGIT_STATUS_NGIT_TARGET: NostrEvent;
2519
2632
  /** Every captured event, flattened — for the id/signature verification test. */
2520
2633
  declare const ALL_NGIT_FIXTURE_EVENTS: NostrEvent[];
2521
2634
 
2522
- export { ALL_NGIT_FIXTURE_EVENTS, type AnnouncementEdits, type BuildIncrementOfferOptions, type BuildNarrationOptions, type BuildResultEventOptions, CI_ADVERTISEMENT_KIND, CI_JOB_RESULT_KIND, CI_MANUAL_TRIGGER_KIND, CI_MAX_ADVERTISEMENT_TTL, CI_MAX_PROGRESS_TTL, CI_RUNNER_FAMILY, CI_SECRET_UPDATE_KIND, CI_SERVICE_REQUEST_KIND, CI_SERVICE_STOP_KIND, CI_SOFTWARE, CI_TRUST_ORDER, CI_WORKFLOW_PROGRESS_KIND, CI_WORKFLOW_RESULT_KIND, type CiAdmissionPolicy, type CiAdvertisement, type CiAdvertisementInput, type CiArtifact, type CiBillingPolicy, type CiConclusion, type CiExecutionPolicy, type CiJobQuote, type CiJobResult, type CiJobResultInput, type CiManualTrigger, type CiPrContext, type CiProgressStatus, type CiProvenance, type CiSecretUpdate, type CiSecretUpdateArgs, type CiStatusJob, type CiStatusReport, type CiStatusRun, type CiStatusSummary, type CiTriggerContext, type CiTriggerReason, type CiTrustLevel, type CiWorkflowProgress, type CiWorkflowProgressInput, type CiWorkflowRef, type CiWorkflowResult, type CiWorkflowResultInput, type ClosureResult, type CollectRepoObjectsOptions, type CollectRepoObjectsResult, DEFAULT_CONCURRENCY, type DeriveTrustLevelArgs, type DiscoveredWorkflow, type DownloadOptions, type DownloadResult, EARLIEST_UNIQUE_COMMIT_MARKER, type EncryptedArtifact, type EncryptedArtifactRef, type ExecuteFactoryJobOptions, type ExecutePushOptions, type ExistingAnnouncement, FACTORY_JOB_FEEDBACK_KIND, FACTORY_JOB_REQUEST_KIND, FACTORY_JOB_RESULT_KIND, type FactoryJobExecution, type FactoryJobHooks, type FactoryJobOutcome, type FactoryJobRequest, type FactoryJobWork, type FactoryMilestone, type FactoryTicket, FakeRunner, type FakeRunnerScript, FeeRates, type FetchLike, type FetchRemoteStateOptions, type FetchedObject, type GateCheck, type GateResult, type GatewayFetchOptions, type GitCommentRequest, GitError, type GitErrorEnvelope, type GitEstimateRequest, type GitEstimateResponse, type GitEventResponse, type GitFeeEstimate, type GitIssueRequest, GitObjectType, type GitPatchRequest, type GitPlannedObject, type GitPublishReceipt, type GitPushRequest, type GitPushResponse, type GitRef, type GitRefUpdate, type GitRepoAddr, GitRepoReader, type GitStatusRequest, type GitStatusValue, type GitUploadStep, type IncrementSpec, type JobDeliveryPort, MAX_SECRET_CIPHERTEXT_BYTES, MAX_SECRET_NAMES, MAX_SECRET_PLAINTEXT_BYTES, MAX_SECRET_VALUE_BYTES, type MissingObject, NGIT_ANNOUNCEMENT_WYRD, NGIT_ANNOUNCEMENT_WYRD_CAPTURED_AT, NGIT_ANNOUNCEMENT_WYRD_EVENT_ID, NGIT_ANNOUNCEMENT_WYRD_RELAY, NGIT_COMMENT_THREAD_CAPTURED_AT, NGIT_COMMENT_THREAD_COMMENTS, NGIT_COMMENT_THREAD_COMMENT_EVENT_IDS, NGIT_COMMENT_THREAD_NESTED_REPLY_ID, NGIT_COMMENT_THREAD_NESTED_REPLY_PARENT_ID, NGIT_COMMENT_THREAD_RELAY, NGIT_COMMENT_THREAD_ROOT, NGIT_COMMENT_THREAD_ROOT_EVENT_ID, NGIT_STATE_NGIT, NGIT_STATE_NGIT_CAPTURED_AT, NGIT_STATE_NGIT_EVENT_ID, NGIT_STATE_NGIT_RELAY, NGIT_STATUS_NGIT, NGIT_STATUS_NGIT_CAPTURED_AT, NGIT_STATUS_NGIT_EVENT_ID, NGIT_STATUS_NGIT_RELAY, NGIT_STATUS_NGIT_TARGET, NGIT_STATUS_NGIT_TARGET_EVENT_ID, NonFastForwardError, type NostrEvent, type NostrFilter, ObjectIntegrityError, type ObjectStat, type ObjectWithPath, ObjectWriteMismatchError, type OfferedIncrement, type OversizeObject, OversizeObjectsError, PayoutPointer, type PlanPushOptions, type PlannedObject, PublishReceipt, Publisher, type PushFeeEstimate, type PushPlan, type PushResult, RELAYS_TAG, RESERVED_SECRET_NAMES, type ReadGitObject, type ReadObjectsResult, type RefFilters, type RefUpdate, type RefUpdateKind, type RejectedRefUpdate, type RelayEvent, type RemoteState, type RepoAddress, type RepoRefs, type Runner, type RunnerArtifact, type RunnerJobResult, type RunnerRequest, type RunnerRunResult, SECRET_NAME_RE, type SecretInventory, type SecretInventoryEntry, type SecretUpdatePlaintext, type SelectServiceRequestsOptions, type SelectedServiceRequests, type ServiceControl, type StatObjectsResult, UnsignedEvent, type UploadStepResult, WEB_TAG, WORKFLOW_DIRS, type WebSocketFactory, type WebSocketLike, type WorkflowJob, type WorkflowTriggers, amendRepoAnnouncement, applySecretUpdate, assembleCiStatus, assertSecretUpdateShape, buildCiAdvertisement, buildCiJobResult, buildCiManualTrigger, buildCiSecretUpdate, buildCiServiceRequest, buildCiServiceStop, buildCiWorkflowProgress, buildCiWorkflowResult, buildIncrementOfferEvent, buildNarrationEvent, buildQuoteEvent, buildRepoAnnouncement, buildResultEvent, collectRepoObjects, commonTriggerTags, controlOrder, decryptSecretUpdate, defaultFakeRunResult, deriveTrustLevel, discoverWorkflows, downloadGitObjects, effectiveSecrets, encryptSecretUpdate, executeFactoryJob, executePush, fetchRemoteState, fetchTxBytes, gatePassed, generateSecretsKey, hexToNpub, isCiConclusion, isCiTrustLevel, isSafeRefname, matchesPullRequest, matchesPush, missingObjectsMessage, npubToHex, ownerToHex, parseCiAdvertisement, parseCiJobResult, parseCiManualTrigger, parseCiSecretUpdate, parseCiServiceControl, parseCiTriggerContext, parseCiWorkflowProgress, parseCiWorkflowResult, parseFactoryJobRequest, parseRepoAddress, parseWorkflow, planFactoryJob, planPush, queryRelay, refMatchesPatterns, referencedShas, repoAddress, selectServiceRequests, serializeEventReceipt, serializeFeeEstimate, serializePushPlan, serializePushResult, setHeadSymref, sha256Hex, trustAtLeast, updateRef, validateSecretUpdate, verifyObjectBody, walkClosure, writeGitObject, writeGitObjects };
2635
+ export { ALL_NGIT_FIXTURE_EVENTS, type AnnouncementEdits, type BuildIncrementOfferOptions, type BuildNarrationOptions, type BuildResultEventOptions, CI_ADVERTISEMENT_KIND, CI_JOB_RESULT_KIND, CI_LIVE_LOG_TAIL_INTERVAL_MS, CI_LIVE_LOG_TAIL_JOB_BYTES, CI_LIVE_LOG_TAIL_KIND, CI_LIVE_LOG_TAIL_MAX_BYTES, CI_MANUAL_TRIGGER_KIND, CI_MAX_ADVERTISEMENT_TTL, CI_MAX_LIVE_LOG_TAIL_TTL, CI_MAX_PROGRESS_TTL, CI_RUNNER_CHANNEL_KEY, CI_RUNNER_FAMILY, CI_SECRET_UPDATE_KIND, CI_SERVICE_REQUEST_KIND, CI_SERVICE_STOP_KIND, CI_SOFTWARE, CI_TRUST_ORDER, CI_WORKFLOW_PROGRESS_KIND, CI_WORKFLOW_RESULT_KIND, type CiAdmissionPolicy, type CiAdvertisement, type CiAdvertisementInput, type CiArtifact, type CiBillingPolicy, type CiConclusion, type CiExecutionPolicy, type CiJobQuote, type CiJobResult, type CiJobResultInput, type CiLiveLogTail, type CiLiveLogTailChannel, type CiLiveLogTailInput, type CiLiveLogTailJob, type CiManualTrigger, type CiPrContext, type CiProgressStatus, type CiProvenance, type CiSecretUpdate, type CiSecretUpdateArgs, type CiStatusJob, type CiStatusReport, type CiStatusRun, type CiStatusSummary, type CiTriggerContext, type CiTriggerReason, type CiTrustLevel, type CiWorkflowProgress, type CiWorkflowProgressInput, type CiWorkflowRef, type CiWorkflowResult, type CiWorkflowResultInput, type ClosureResult, type CollectRepoObjectsOptions, type CollectRepoObjectsResult, DEFAULT_CONCURRENCY, type DeriveTrustLevelArgs, type DiscoveredWorkflow, type DownloadOptions, type DownloadResult, EARLIEST_UNIQUE_COMMIT_MARKER, type EncryptedArtifact, type EncryptedArtifactRef, type ExecuteFactoryJobOptions, type ExecutePushOptions, type ExistingAnnouncement, FACTORY_JOB_FEEDBACK_KIND, FACTORY_JOB_REQUEST_KIND, FACTORY_JOB_RESULT_KIND, type FactoryJobExecution, type FactoryJobHooks, type FactoryJobOutcome, type FactoryJobRequest, type FactoryJobWork, type FactoryMilestone, type FactoryTicket, FakeRunner, type FakeRunnerScript, FeeRates, type FetchLike, type FetchRemoteStateOptions, type FetchedObject, type GateCheck, type GateResult, type GatewayFetchOptions, type GitCommentRequest, GitError, type GitErrorEnvelope, type GitEstimateRequest, type GitEstimateResponse, type GitEventResponse, type GitFeeEstimate, type GitIssueRequest, GitObjectType, type GitPatchRequest, type GitPlannedObject, type GitPublishReceipt, type GitPushRequest, type GitPushResponse, type GitRef, type GitRefUpdate, type GitRepoAddr, GitRepoReader, type GitStatusRequest, type GitStatusValue, type GitUploadStep, type IncrementSpec, type JobDeliveryPort, MAX_SECRET_CIPHERTEXT_BYTES, MAX_SECRET_NAMES, MAX_SECRET_PLAINTEXT_BYTES, MAX_SECRET_VALUE_BYTES, type MissingObject, NGIT_ANNOUNCEMENT_WYRD, NGIT_ANNOUNCEMENT_WYRD_CAPTURED_AT, NGIT_ANNOUNCEMENT_WYRD_EVENT_ID, NGIT_ANNOUNCEMENT_WYRD_RELAY, NGIT_COMMENT_THREAD_CAPTURED_AT, NGIT_COMMENT_THREAD_COMMENTS, NGIT_COMMENT_THREAD_COMMENT_EVENT_IDS, NGIT_COMMENT_THREAD_NESTED_REPLY_ID, NGIT_COMMENT_THREAD_NESTED_REPLY_PARENT_ID, NGIT_COMMENT_THREAD_RELAY, NGIT_COMMENT_THREAD_ROOT, NGIT_COMMENT_THREAD_ROOT_EVENT_ID, NGIT_STATE_NGIT, NGIT_STATE_NGIT_CAPTURED_AT, NGIT_STATE_NGIT_EVENT_ID, NGIT_STATE_NGIT_RELAY, NGIT_STATUS_NGIT, NGIT_STATUS_NGIT_CAPTURED_AT, NGIT_STATUS_NGIT_EVENT_ID, NGIT_STATUS_NGIT_RELAY, NGIT_STATUS_NGIT_TARGET, NGIT_STATUS_NGIT_TARGET_EVENT_ID, NonFastForwardError, type NostrEvent, type NostrFilter, ObjectIntegrityError, type ObjectStat, type ObjectWithPath, ObjectWriteMismatchError, type OfferedIncrement, type OversizeObject, OversizeObjectsError, PayoutPointer, type PlanPushOptions, type PlannedObject, PublishReceipt, Publisher, type PushFeeEstimate, type PushPlan, type PushResult, RELAYS_TAG, RESERVED_SECRET_NAMES, type ReadGitObject, type ReadObjectsResult, type RefFilters, type RefUpdate, type RefUpdateKind, type RejectedRefUpdate, type RelayEvent, type RemoteState, type RepoAddress, type RepoRefs, type Runner, type RunnerArtifact, type RunnerJobResult, type RunnerRequest, type RunnerRunResult, SECRET_NAME_RE, type SecretInventory, type SecretInventoryEntry, type SecretUpdatePlaintext, type SelectServiceRequestsOptions, type SelectedServiceRequests, type ServiceControl, type StatObjectsResult, UnsignedEvent, type UploadStepResult, WEB_TAG, WORKFLOW_DIRS, type WebSocketFactory, type WebSocketLike, type WorkflowJob, type WorkflowTriggers, amendRepoAnnouncement, applySecretUpdate, assembleCiStatus, assertSecretUpdateShape, buildCiAdvertisement, buildCiJobResult, buildCiLiveLogTail, buildCiManualTrigger, buildCiSecretUpdate, buildCiServiceRequest, buildCiServiceStop, buildCiWorkflowProgress, buildCiWorkflowResult, buildIncrementOfferEvent, buildNarrationEvent, buildQuoteEvent, buildRepoAnnouncement, buildResultEvent, collectRepoObjects, commonTriggerTags, controlOrder, decryptSecretUpdate, defaultFakeRunResult, deriveTrustLevel, discoverWorkflows, downloadGitObjects, effectiveSecrets, encryptSecretUpdate, executeFactoryJob, executePush, fetchRemoteState, fetchTxBytes, gatePassed, generateSecretsKey, hexToNpub, isCiConclusion, isCiTrustLevel, isSafeRefname, liveLogTailBudget, liveLogTailEventBudget, matchesPullRequest, matchesPush, missingObjectsMessage, npubToHex, ownerToHex, parseCiAdvertisement, parseCiJobResult, parseCiLiveLogTail, parseCiManualTrigger, parseCiSecretUpdate, parseCiServiceControl, parseCiTriggerContext, parseCiWorkflowProgress, parseCiWorkflowResult, parseFactoryJobRequest, parseRepoAddress, parseWorkflow, planFactoryJob, planPush, queryRelay, refMatchesPatterns, referencedShas, repoAddress, selectServiceRequests, serializeEventReceipt, serializeFeeEstimate, serializePushPlan, serializePushResult, setHeadSymref, sha256Hex, sliceLogTail, trustAtLeast, updateRef, validateSecretUpdate, verifyObjectBody, walkClosure, writeGitObject, writeGitObjects };
package/dist/index.js CHANGED
@@ -1,9 +1,15 @@
1
1
  import {
2
2
  CI_ADVERTISEMENT_KIND,
3
3
  CI_JOB_RESULT_KIND,
4
+ CI_LIVE_LOG_TAIL_INTERVAL_MS,
5
+ CI_LIVE_LOG_TAIL_JOB_BYTES,
6
+ CI_LIVE_LOG_TAIL_KIND,
7
+ CI_LIVE_LOG_TAIL_MAX_BYTES,
4
8
  CI_MANUAL_TRIGGER_KIND,
5
9
  CI_MAX_ADVERTISEMENT_TTL,
10
+ CI_MAX_LIVE_LOG_TAIL_TTL,
6
11
  CI_MAX_PROGRESS_TTL,
12
+ CI_RUNNER_CHANNEL_KEY,
7
13
  CI_RUNNER_FAMILY,
8
14
  CI_SECRET_UPDATE_KIND,
9
15
  CI_SERVICE_REQUEST_KIND,
@@ -36,6 +42,7 @@ import {
36
42
  assertSecretUpdateShape,
37
43
  buildCiAdvertisement,
38
44
  buildCiJobResult,
45
+ buildCiLiveLogTail,
39
46
  buildCiManualTrigger,
40
47
  buildCiSecretUpdate,
41
48
  buildCiServiceRequest,
@@ -64,6 +71,8 @@ import {
64
71
  isCiConclusion,
65
72
  isCiTrustLevel,
66
73
  isSafeRefname,
74
+ liveLogTailBudget,
75
+ liveLogTailEventBudget,
67
76
  matchesPullRequest,
68
77
  matchesPush,
69
78
  missingObjectsMessage,
@@ -71,6 +80,7 @@ import {
71
80
  ownerToHex,
72
81
  parseCiAdvertisement,
73
82
  parseCiJobResult,
83
+ parseCiLiveLogTail,
74
84
  parseCiManualTrigger,
75
85
  parseCiSecretUpdate,
76
86
  parseCiServiceControl,
@@ -90,6 +100,7 @@ import {
90
100
  serializePushResult,
91
101
  setHeadSymref,
92
102
  sha256Hex,
103
+ sliceLogTail,
93
104
  trustAtLeast,
94
105
  updateRef,
95
106
  validateSecretUpdate,
@@ -97,7 +108,7 @@ import {
97
108
  walkClosure,
98
109
  writeGitObject,
99
110
  writeGitObjects
100
- } from "./chunk-DCGV6ALH.js";
111
+ } from "./chunk-4WVH2ITA.js";
101
112
  import {
102
113
  COMMENT_KIND,
103
114
  LEGACY_COMMENT_KIND,
@@ -117,8 +128,8 @@ import {
117
128
  parseMaintainers,
118
129
  parsePayout,
119
130
  queryRelay
120
- } from "./chunk-JBFBD7HV.js";
121
- import "./chunk-U5A7MHBP.js";
131
+ } from "./chunk-QKL53YZS.js";
132
+ import "./chunk-7VIVQHRG.js";
122
133
 
123
134
  // src/factory-job-gate.ts
124
135
  function gatePassed(gate) {
@@ -426,10 +437,21 @@ var FakeRunner = class {
426
437
  /** Every request, in call order. */
427
438
  requests = [];
428
439
  async run(request) {
429
- this.requests.push(request);
430
- const result = await this.script(request);
431
- if (request.onLog) {
432
- for (const job of result.jobs) request.onLog(job.jobId, job.log);
440
+ const sink = request.onLog;
441
+ const streamed = /* @__PURE__ */ new Set();
442
+ const scripted = sink ? {
443
+ ...request,
444
+ onLog: (jobId, chunk) => {
445
+ streamed.add(jobId);
446
+ sink(jobId, chunk);
447
+ }
448
+ } : request;
449
+ this.requests.push(scripted);
450
+ const result = await this.script(scripted);
451
+ if (sink) {
452
+ for (const job of result.jobs) {
453
+ if (!streamed.has(job.jobId)) sink(job.jobId, job.log);
454
+ }
433
455
  }
434
456
  return result;
435
457
  }
@@ -892,9 +914,15 @@ export {
892
914
  ALL_NGIT_FIXTURE_EVENTS,
893
915
  CI_ADVERTISEMENT_KIND,
894
916
  CI_JOB_RESULT_KIND,
917
+ CI_LIVE_LOG_TAIL_INTERVAL_MS,
918
+ CI_LIVE_LOG_TAIL_JOB_BYTES,
919
+ CI_LIVE_LOG_TAIL_KIND,
920
+ CI_LIVE_LOG_TAIL_MAX_BYTES,
895
921
  CI_MANUAL_TRIGGER_KIND,
896
922
  CI_MAX_ADVERTISEMENT_TTL,
923
+ CI_MAX_LIVE_LOG_TAIL_TTL,
897
924
  CI_MAX_PROGRESS_TTL,
925
+ CI_RUNNER_CHANNEL_KEY,
898
926
  CI_RUNNER_FAMILY,
899
927
  CI_SECRET_UPDATE_KIND,
900
928
  CI_SERVICE_REQUEST_KIND,
@@ -960,6 +988,7 @@ export {
960
988
  authorizedStatusAuthors,
961
989
  buildCiAdvertisement,
962
990
  buildCiJobResult,
991
+ buildCiLiveLogTail,
963
992
  buildCiManualTrigger,
964
993
  buildCiSecretUpdate,
965
994
  buildCiServiceRequest,
@@ -1003,6 +1032,8 @@ export {
1003
1032
  isCiTrustLevel,
1004
1033
  isSafeRefname,
1005
1034
  isValidEvmPayoutAddress,
1035
+ liveLogTailBudget,
1036
+ liveLogTailEventBudget,
1006
1037
  matchesPullRequest,
1007
1038
  matchesPush,
1008
1039
  missingObjectsMessage,
@@ -1010,6 +1041,7 @@ export {
1010
1041
  ownerToHex,
1011
1042
  parseCiAdvertisement,
1012
1043
  parseCiJobResult,
1044
+ parseCiLiveLogTail,
1013
1045
  parseCiManualTrigger,
1014
1046
  parseCiSecretUpdate,
1015
1047
  parseCiServiceControl,
@@ -1034,6 +1066,7 @@ export {
1034
1066
  serializePushResult,
1035
1067
  setHeadSymref,
1036
1068
  sha256Hex,
1069
+ sliceLogTail,
1037
1070
  trustAtLeast,
1038
1071
  updateRef,
1039
1072
  validateSecretUpdate,