@toon-protocol/rig 4.3.0 → 4.5.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.
- package/README.md +53 -12
- package/dist/{chunk-GWA3CSUR.js → chunk-HMED76GU.js} +2 -4
- package/dist/{chunk-GWA3CSUR.js.map → chunk-HMED76GU.js.map} +1 -1
- package/dist/{chunk-3RWM7PBM.js → chunk-S2BMLXAD.js} +64 -7
- package/dist/chunk-S2BMLXAD.js.map +1 -0
- package/dist/{chunk-5HFOPKAB.js → chunk-UZZ2JKEC.js} +29 -24
- package/dist/chunk-UZZ2JKEC.js.map +1 -0
- package/dist/{ci-serve-ZVJBZ3W3.js → ci-serve-PYEHIBN7.js} +104 -24
- package/dist/ci-serve-PYEHIBN7.js.map +1 -0
- package/dist/cli/rig.js +3 -3
- package/dist/index.d.ts +14 -0
- package/dist/index.js +2 -2
- package/dist/{standalone-mode-JIEECDTI.js → standalone-mode-W3ZEKC6M.js} +2 -2
- package/dist/{standalone-mode-JIEECDTI.js.map → standalone-mode-W3ZEKC6M.js.map} +1 -1
- package/package.json +3 -3
- package/dist/chunk-3RWM7PBM.js.map +0 -1
- package/dist/chunk-5HFOPKAB.js.map +0 -1
- package/dist/ci-serve-ZVJBZ3W3.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/ci-serve.ts","../src/ci/act-runner.ts","../src/ci/coordinator.ts","../src/ci/materialize-commit.ts","../src/ci/relay-subscription.ts","../src/ci/state.ts"],"sourcesContent":["/**\n * `rig ci serve` — run a CI coordinator (#125).\n *\n * The coordinator is an ORDINARY rig identity: the same seed-phrase chain,\n * wallet, channel, and connector configuration every paid command uses\n * (`rig identity create` under a separate `TOON_CLIENT_HOME` or account, `rig\n * entry <url>`, `rig fund`). Its spend is bounded by what its operator put in\n * its wallet — there is no separate cap. The maintainer's seed is never on\n * this host: authorization arrives as kind:9843 Service Requests on the\n * relay, and `rig ci serve` just listens.\n *\n * This module is the thin CLI shell around ../ci/coordinator.ts: flags →\n * paid session (always embedded; the daemon has no NIP-C1 routes) → Runner\n * (act on the host's Docker, or an injected one) → `startCoordinator` →\n * block until SIGINT/SIGTERM (or the injected abort signal) → clean stop.\n * Under `--once` the block also ends when the first run concludes — its\n * Workflow Result and final progress marker are on the relay — so one\n * `rig ci trigger` can be answered by one bounded `rig ci serve --once`\n * (#127); ignored or refused triggers publish nothing and do not count.\n * Everything the coordinator says goes to stderr; under `--json` stdout\n * carries exactly ONE document, emitted at start, describing what is being\n * served.\n *\n * Loaded lazily from ./ci.ts so one-shot `rig ci` verbs never pay for the\n * coordinator's imports.\n */\n\nimport { join } from 'node:path';\nimport { parseArgs } from 'node:util';\nimport { ActRunner, DEFAULT_ACT_PLATFORMS } from '../ci/act-runner.js';\nimport {\n DEFAULT_CONCURRENCY,\n DEFAULT_RUN_TIMEOUT_MS,\n startCoordinator,\n type CanAfford,\n type ConcludedRun,\n type CoordinatorHandle,\n type CoordinatorRepo,\n type RunCostEstimate,\n} from '../ci/coordinator.js';\nimport { uploadChargeFor } from '../publisher.js';\nimport {\n ChannelMapStore,\n channelStatus,\n resolveChannelPaths,\n} from '../standalone/channel-map.js';\nimport type { Runner } from '../ci/runner.js';\nimport { defaultCoordinatorStateDir } from '../ci/state.js';\nimport { ARWEAVE_GATEWAYS } from '@toon-protocol/arweave';\nimport { PREFERRED_GATEWAY } from '../gateway-preference.js';\nimport { hexToNpub, ownerToHex } from '../npub.js';\nimport type { CiDeps } from './ci.js';\nimport { rigVersion } from './dispatch.js';\nimport { emitCliError } from './errors.js';\nimport {\n identityReport,\n loadPaidSession,\n withForcedStandalone,\n} from './push.js';\nimport { WS_URL_RE } from './repo-command.js';\nimport { renderIdentityLine } from './render.js';\nimport type { StandaloneContext } from './standalone-context.js';\n\nexport const CI_SERVE_USAGE = `Usage: rig ci serve --relay <ws-url> --repo <owner>/<repo-id> [--repo …] [options]\n\nRun a CI coordinator: watch the named repos on the relay, run their\nGitHub-Actions-syntax workflows with act on this host's Docker, and publish\nNIP-C1 progress and results back to the relay as PAID writes from THIS\nidentity's channel. Every run is a handful of relay writes (one per progress\nupdate, job, and result) plus one store upload per job log and artifact.\n\nA repo is served only while one of its maintainers has published a Service\nRequest (\\`rig ci request <this-coordinator>\\`) with no later Stop; a\nmaintainer's \\`rig ci trigger\\` runs one workflow without a standing request.\nSecrets set with \\`rig ci secret\\` are injected only into runs authored by a\nmaintainer — never into a third-party pull request.\n\nOptions:\n --relay <url> the relay to watch AND publish to (ws:// or wss://)\n [required, exactly one]\n --repo <owner>/<id> a repo to serve (owner as npub or hex); repeatable\n --requester <pubkey> also accept Service Requests from this pubkey (npub\n or hex; NIP-C1 operator policy); repeatable — for\n running your own coordinator against a repo you do\n not maintain; such runs show as lower trust\n --concurrency <n> runs executed at once (default ${DEFAULT_CONCURRENCY}); more queue\n --timeout <seconds> wall-clock budget per run → timed_out\n (default ${DEFAULT_RUN_TIMEOUT_MS / 1000})\n --gateway <url> gateway that serves the store's raw bytes: where log +\n artifact links point, and the first place the\n coordinator reads objects from (<url>/raw/<txId>)\n before the shared gateway list (default:\n RIG_ARWEAVE_GATEWAY, else ${PREFERRED_GATEWAY})\n --act-bin <path> the act executable (default: RIG_ACT_BIN, else PATH)\n --platform <label=img> runs-on label → Docker image; repeatable\n (default: ubuntu-latest=${DEFAULT_ACT_PLATFORMS['ubuntu-latest']})\n --workdir <dir> where commits are checked out for runs (default:\n <state-dir>/work)\n --once stop after the first run concludes (its Workflow\n Result and final progress marker are on the relay);\n ignored or refused triggers do not count\n --json emit ONE JSON document describing the coordinator\n at start; everything else goes to stderr\n --standalone accepted for symmetry; the coordinator always runs\n embedded (alias: --no-daemon)\n -h, --help show this help\n\nState (cursor, secret inventory) lives under\n<TOON_CLIENT_HOME|~/.toon-client>/rig-ci/<coordinator-pubkey>/ so a restart\nresumes from the last processed event. The NIP-44 secrets-key is generated\nfresh every start and never written to disk: after a restart maintainers\nre-send \\`rig ci secret set\\`; values already accepted survive.\n\nBefore each run the coordinator checks that a recorded payment channel (or,\nbefore the first channel opens, the wallet's USDC) can cover the run's writes;\notherwise it says so on stderr and starts nothing, so a run is never\nhalf-published (story 15).\n\nStop with Ctrl-C (SIGINT) or SIGTERM: active runs conclude \\`cancelled\\`.`;\n\ninterface ServeFlags {\n relay: string;\n repos: CoordinatorRepo[];\n /** Operator-accepted requester pubkeys (lowercase hex). */\n requesters: string[];\n concurrency: number;\n timeoutMs: number;\n /** `--gateway` as given; absent → RIG_ARWEAVE_GATEWAY, else the preferred one. */\n gateway?: string;\n actBin?: string;\n platforms?: Record<string, string>;\n workdir?: string;\n /** Stop after the first run concludes. */\n once: boolean;\n json: boolean;\n}\n\nclass ServeUsageError extends Error {}\n\nfunction parseServeArgs(args: string[]): ServeFlags | 'help' {\n const { values } = parseArgs({\n args,\n options: {\n relay: { type: 'string' },\n repo: { type: 'string', multiple: true },\n requester: { type: 'string', multiple: true },\n concurrency: { type: 'string' },\n timeout: { type: 'string' },\n gateway: { type: 'string' },\n 'act-bin': { type: 'string' },\n platform: { type: 'string', multiple: true },\n workdir: { type: 'string' },\n once: { type: 'boolean', default: false },\n json: { type: 'boolean', default: false },\n standalone: { type: 'boolean', default: false },\n 'no-daemon': { type: 'boolean', default: false },\n help: { type: 'boolean', short: 'h', default: false },\n },\n allowPositionals: false,\n strict: true,\n });\n if (values.help) return 'help';\n if (!values.relay) throw new ServeUsageError('--relay <ws-url> is required');\n // A long-lived subscription needs a WebSocket relay URL (the http(s) relay\n // spellings other rig commands accept only work for paid publishes).\n if (!WS_URL_RE.test(values.relay)) {\n throw new ServeUsageError(\n `--relay must be a ws:// or wss:// URL, got ${JSON.stringify(values.relay)}`\n );\n }\n\n const repos: CoordinatorRepo[] = [];\n for (const spec of values.repo ?? []) {\n const slash = spec.indexOf('/');\n if (slash <= 0 || slash === spec.length - 1) {\n throw new ServeUsageError(\n `--repo expects <owner>/<repo-id>, got ${JSON.stringify(spec)}`\n );\n }\n let ownerPubkey: string;\n try {\n ownerPubkey = ownerToHex(spec.slice(0, slash));\n } catch (err) {\n throw new ServeUsageError(\n `--repo ${JSON.stringify(spec)}: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n repos.push({ ownerPubkey, repoId: spec.slice(slash + 1) });\n }\n if (repos.length === 0)\n throw new ServeUsageError(\n 'at least one --repo <owner>/<repo-id> is required'\n );\n\n const requesters: string[] = [];\n for (const spec of values.requester ?? []) {\n try {\n requesters.push(ownerToHex(spec));\n } catch (err) {\n throw new ServeUsageError(\n `--requester ${JSON.stringify(spec)}: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n }\n\n const positiveInt = (\n flag: string,\n raw: string | undefined,\n fallback: number\n ): number => {\n if (raw === undefined) return fallback;\n const n = Number(raw);\n if (!Number.isInteger(n) || n <= 0)\n throw new ServeUsageError(`${flag} expects a positive integer`);\n return n;\n };\n const concurrency = positiveInt(\n '--concurrency',\n values.concurrency,\n DEFAULT_CONCURRENCY\n );\n const timeoutMs =\n positiveInt('--timeout', values.timeout, DEFAULT_RUN_TIMEOUT_MS / 1000) *\n 1000;\n\n let platforms: Record<string, string> | undefined;\n if (values.platform && values.platform.length > 0) {\n platforms = {};\n for (const spec of values.platform) {\n const eq = spec.indexOf('=');\n if (eq <= 0 || eq === spec.length - 1) {\n throw new ServeUsageError(\n `--platform expects <label>=<image>, got ${JSON.stringify(spec)}`\n );\n }\n platforms[spec.slice(0, eq)] = spec.slice(eq + 1);\n }\n }\n\n return {\n relay: values.relay,\n repos,\n requesters,\n concurrency,\n timeoutMs,\n ...(values.gateway !== undefined ? { gateway: values.gateway } : {}),\n ...(values['act-bin'] !== undefined ? { actBin: values['act-bin'] } : {}),\n ...(platforms ? { platforms } : {}),\n ...(values.workdir !== undefined ? { workdir: values.workdir } : {}),\n once: values.once,\n json: values.json,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Affordability (story 15)\n// ---------------------------------------------------------------------------\n\n/** What one run is estimated to cost, in the smallest asset unit. */\nexport function estimateRunCost(estimate: RunCostEstimate): bigint {\n // Logs and artifacts are metered per KiB on the store route; 64 KiB is a\n // generous per-upload envelope for a log tail's full file.\n return (\n BigInt(estimate.events) * estimate.rates.eventFee +\n BigInt(estimate.uploads) * uploadChargeFor(estimate.rates, 64 * 1024)\n );\n}\n\n/**\n * The coordinator's wallet check: a run is affordable when a recorded, still\n * open payment channel of THIS identity has `deposited − claimed ≥ cost`, or —\n * before any channel is recorded (the first paid write opens one lazily\n * from the wallet) — when the wallet holds that much USDC on some chain. An\n * unreadable wallet is NOT affordable: the coordinator must never start a\n * run it may not be able to finish publishing.\n */\nexport function makeAffordabilityCheck(args: {\n ctx: StandaloneContext;\n env: NodeJS.ProcessEnv;\n log: (line: string) => void;\n}): CanAfford {\n const { ctx, env, log } = args;\n return async (estimate) => {\n const cost = estimateRunCost(estimate);\n const store = new ChannelMapStore(resolveChannelPaths(env));\n const mine = store\n .list()\n .filter(\n (record) =>\n record.identity.toLowerCase() === ctx.ownerPubkey.toLowerCase() &&\n record.supersededAt === undefined\n );\n if (mine.length > 0) {\n let best = 0n;\n for (const record of mine) {\n const watermark = store.readWatermark(record.channelId);\n if (channelStatus(watermark) !== 'open') continue;\n if (record.depositTotal === undefined) continue;\n const remaining =\n BigInt(record.depositTotal) -\n BigInt(watermark?.cumulativeAmount ?? '0');\n if (remaining > best) best = remaining;\n }\n if (best >= cost) return true;\n log(\n `[ci] wallet check: the best open channel has ${best} available, the run needs ~${cost} — ` +\n 'fund this coordinator (rig fund / rig channel open --deposit) to resume'\n );\n return false;\n }\n if (!ctx.money) {\n log(\n '[ci] wallet check: no channel recorded and no wallet reader — refusing'\n );\n return false;\n }\n let chains;\n try {\n chains = await ctx.money.walletChainBalances();\n } catch (err) {\n log(\n `[ci] wallet check: wallet unreadable (${err instanceof Error ? err.message : String(err)}) — refusing`\n );\n return false;\n }\n let best = 0n;\n for (const chain of chains) {\n if (chain.unreadable) continue;\n for (const token of chain.tokens) {\n if (token.symbol !== undefined && !/usdc/i.test(token.symbol)) continue;\n const amount = BigInt(token.amount);\n if (amount > best) best = amount;\n }\n }\n if (best >= cost) return true;\n log(\n `[ci] wallet check: no channel recorded yet and the wallet holds ${best} USDC base units, the run needs ~${cost} — ` +\n 'fund this coordinator (rig fund) to resume'\n );\n return false;\n };\n}\n\n/**\n * Resolve on SIGINT/SIGTERM, when `signal` aborts, or — under `--once` —\n * when `firstRun` settles, whichever first. The value says why.\n */\nfunction untilStopRequested(\n signal: AbortSignal | undefined,\n firstRun?: Promise<ConcludedRun>\n): Promise<string | ConcludedRun> {\n return new Promise((resolve) => {\n let settled = false;\n const finish = (reason: string | ConcludedRun): void => {\n if (settled) return;\n settled = true;\n process.off('SIGINT', onSigint);\n process.off('SIGTERM', onSigterm);\n resolve(reason);\n };\n const onSigint = (): void => finish('SIGINT');\n const onSigterm = (): void => finish('SIGTERM');\n if (signal?.aborted) return finish('abort');\n signal?.addEventListener('abort', () => finish('abort'), { once: true });\n process.once('SIGINT', onSigint);\n process.once('SIGTERM', onSigterm);\n void firstRun?.then(finish);\n });\n}\n\nexport async function runCiServe(\n args: string[],\n deps: CiDeps\n): Promise<number> {\n // Always embedded: the coordinator signs and pays as itself.\n const forced = withForcedStandalone(deps);\n const { io } = forced;\n\n let flags: ServeFlags;\n try {\n const parsed = parseServeArgs(args);\n if (parsed === 'help') {\n io.out(CI_SERVE_USAGE);\n return 0;\n }\n flags = parsed;\n } catch (err) {\n io.err(err instanceof Error ? err.message : String(err));\n io.err(CI_SERVE_USAGE);\n return 2;\n }\n\n // The gateway that serves the store's raw bytes — `--gateway`, else\n // RIG_ARWEAVE_GATEWAY (the variable `rig push` and `rig site` honour), else\n // rig's preferred public gateway. It is where log/artifact links point AND\n // the first place the coordinator reads objects from, on the store's\n // raw-bytes route `<gateway>/raw/<txId>`, ahead of the shared public list:\n // a private or local gateway (the sandbox's answers only on /raw/) is\n // otherwise unreachable on the read path, and a public one serves the same\n // bytes there as at `/<txId>`.\n const gateway = (\n flags.gateway ??\n forced.env['RIG_ARWEAVE_GATEWAY'] ??\n PREFERRED_GATEWAY\n ).replace(/\\/+$/, '');\n const readGateways = [`${gateway}/raw`, ...ARWEAVE_GATEWAYS];\n\n // Runner first: a missing act binary must fail BEFORE the identity is\n // loaded or anything is paid.\n let runner: Runner;\n if (forced.runner) {\n runner = forced.runner;\n } else {\n const act = new ActRunner({\n ...(flags.actBin !== undefined ? { actBin: flags.actBin } : {}),\n ...(flags.platforms ? { platforms: flags.platforms } : {}),\n });\n if (act.binary(forced.env) === null) {\n io.err(\n 'rig ci serve: the act executable was not found — install nektos/act ' +\n '(https://github.com/nektos/act) on this host, or point --act-bin / RIG_ACT_BIN at it. ' +\n 'Nothing was started or paid.'\n );\n return 1;\n }\n runner = act;\n }\n\n let ctx: StandaloneContext | undefined;\n let handle: CoordinatorHandle | undefined;\n try {\n const session = await loadPaidSession(forced, flags.relay);\n if (session.path !== 'standalone') {\n throw new Error('rig ci serve requires the standalone paid path');\n }\n ctx = session.ctx;\n const identity = identityReport(ctx);\n const coordinator = ctx.ownerPubkey.toLowerCase();\n const stateDir =\n forced.stateDir ?? defaultCoordinatorStateDir(forced.env, coordinator);\n const workdir = flags.workdir ?? join(stateDir, 'work');\n const version = rigVersion();\n\n // --once: the first concluded run ends the session (see the header).\n let concludeFirstRun: (run: ConcludedRun) => void = () => undefined;\n const firstRun = flags.once\n ? new Promise<ConcludedRun>((resolve) => {\n concludeFirstRun = resolve;\n })\n : undefined;\n\n handle = await startCoordinator({\n coordinatorPubkey: coordinator,\n publisher: ctx.publisher,\n relayUrl: flags.relay,\n gatewayUrl: gateway,\n gateways: readGateways,\n repos: flags.repos,\n runner,\n stateDir,\n workdir,\n version,\n concurrency: flags.concurrency,\n timeoutMs: flags.timeoutMs,\n acceptedRequesters: flags.requesters,\n canAfford:\n forced.canAfford ??\n makeAffordabilityCheck({\n ctx,\n env: forced.env,\n log: (line) => io.err(line),\n }),\n ...(forced.webSocketFactory\n ? { webSocketFactory: forced.webSocketFactory }\n : {}),\n ...(forced.fetchFn ? { fetchFn: forced.fetchFn } : {}),\n ...(forced.resolveSha ? { resolveSha: forced.resolveSha } : {}),\n ...(forced.clock ? { clock: forced.clock } : {}),\n ...(firstRun\n ? { onRunConcluded: (run: ConcludedRun) => concludeFirstRun(run) }\n : {}),\n log: (line) => io.err(line),\n });\n\n const npub = hexToNpub(coordinator);\n const repoLabels = flags.repos.map(\n (r) => `${hexToNpub(r.ownerPubkey)}/${r.repoId}`\n );\n if (flags.json) {\n io.emitJson({\n command: 'ci serve',\n path: 'standalone',\n identity,\n coordinator,\n coordinatorNpub: npub,\n relay: flags.relay,\n gateway,\n repos: flags.repos,\n requesters: flags.requesters,\n runner: { family: runner.family, selectors: runner.selectors },\n concurrency: flags.concurrency,\n timeoutSeconds: flags.timeoutMs / 1000,\n once: flags.once,\n stateDir,\n workdir,\n secretsKey: handle.secretsKeyPubkey,\n advertisementId: handle.advertisementId,\n version,\n });\n } else {\n io.out(renderIdentityLine(identity));\n io.out(`Coordinator: ${npub}`);\n io.out(`Relay: ${flags.relay}`);\n io.out(`Serving: ${repoLabels.join(', ')}`);\n if (flags.requesters.length > 0)\n io.out(\n `Requesters: maintainers + ${flags.requesters.map(hexToNpub).join(', ')}`\n );\n io.out(\n `Runner: ${runner.family} (${runner.selectors.join(', ')}); concurrency ${flags.concurrency}, timeout ${flags.timeoutMs / 1000}s`\n );\n io.out(`State: ${stateDir}`);\n io.out('Maintainers authorize this coordinator with:');\n io.out(` rig ci request ${npub}`);\n io.out(\n flags.once\n ? 'Listening for one run — stops after it concludes (or Ctrl-C).'\n : 'Listening — press Ctrl-C to stop.'\n );\n }\n\n const reason = await untilStopRequested(forced.signal, firstRun);\n io.err(\n typeof reason === 'string'\n ? `rig ci serve: ${reason} — stopping (active runs conclude cancelled)`\n : `rig ci serve: run ${reason.runId.slice(0, 8)} concluded ${reason.conclusion} ` +\n `(result ${reason.resultEventId.slice(0, 8)}) — --once, stopping`\n );\n await handle.stop();\n handle = undefined;\n return 0;\n } catch (err) {\n if (handle) await handle.stop().catch(() => undefined);\n return emitCliError(io, flags.json, 'ci serve', err);\n } finally {\n if (ctx) {\n try {\n await ctx.stop();\n } catch {\n // best-effort teardown\n }\n }\n }\n}\n","/**\n * ActRunner (rig#125) — the first `Runner` implementation: shell out to\n * `act` (nektos/act) against the host Docker daemon, in the coordinator's\n * own process. This is the \"coordinator is its own compute provider\" shape\n * ngit's reference coordinator uses today; a TOON-lease runner replaces it\n * later behind the same ./runner.ts interface without touching the\n * coordinator.\n *\n * What crosses the seam and how:\n *\n * - The checkout is a real git repository at the run's commit, already\n * materialized by the coordinator from the relay + Arweave. act copies\n * it into the job container itself (`-C <dir>`), so `actions/checkout`\n * steps resolve to the local copy and never reach GitHub. Other\n * GitHub-hosted actions ARE fetched by act — the accepted dependency the\n * spec names.\n * - Secrets go in as `-s NAME` with the VALUE in the child's environment,\n * never on argv (argv is world-readable via /proc). act masks them in\n * its output.\n * - The event payload (`-e <file>`) carries the ref/sha or the PR\n * head/base so `github.*` context in workflows is truthful.\n * - Logs are act's `--json` line stream, grouped per `jobID`; the\n * terminal `jobResult` field is each job's conclusion, passed through\n * as NIP-C1 wants (\"preserve the execution backend's conclusion\").\n * - Artifacts: `--artifact-server-path` makes `actions/upload-artifact`\n * work; act stores each named artifact as ONE zip under\n * `<dir>/<run>/<name>/<name>.zip`, which is unpacked here so every file\n * becomes its own `artifact` tag (NIP-C1 forbids archives).\n *\n * - Timeout/cancel: act is sent SIGTERM (then SIGKILL after a grace\n * period). act exits, but with act 0.2.89 the JOB CONTAINER it started\n * keeps running — verified with and without act's `--rm` — so a hung job\n * would spend forever on the host, exactly what the wall clock exists to\n * stop. After a run this runner killed, it therefore removes the leftover\n * containers itself: act names them deterministically\n * (`act-<workflow name>-<job name>-<sha256>`, see\n * {@link actContainerNamePrefix}), so `docker ps` by name prefix, filtered\n * to containers created after the run started, finds exactly this run's.\n * That naming also means two concurrent runs of the same workflow + job\n * on one host collide on the container name — an act limitation the\n * coordinator's concurrency setting has to respect; not solved here.\n *\n * All child-process arguments are ARRAYS (never a shell), matching the rest\n * of the package.\n */\n\nimport { execFile, spawn } from 'node:child_process';\nimport {\n existsSync,\n mkdirSync,\n readdirSync,\n readFileSync,\n statSync,\n writeFileSync,\n} from 'node:fs';\nimport { mkdtemp, rm } from 'node:fs/promises';\nimport { tmpdir } from 'node:os';\nimport { basename, dirname, join, relative, resolve, sep } from 'node:path';\nimport { promisify } from 'node:util';\nimport { inflateRawSync } from 'node:zlib';\nimport { accessSync, constants } from 'node:fs';\nimport type { CiConclusion, CiTriggerContext } from './nip-c1-events.js';\nimport type {\n Runner,\n RunnerArtifact,\n RunnerJobResult,\n RunnerRequest,\n RunnerRunResult,\n} from './runner.js';\nimport { parseWorkflow, type WorkflowTriggers } from './workflows.js';\n\n/** Environment variable naming the act binary (overrides PATH lookup). */\nexport const RIG_ACT_BIN_ENV = 'RIG_ACT_BIN';\n\n/** Default `-P` platform mapping: the community act image for ubuntu-latest. */\nexport const DEFAULT_ACT_PLATFORMS: Readonly<Record<string, string>> = {\n 'ubuntu-latest': 'catthehacker/ubuntu:act-latest',\n};\n\nexport interface ActRunnerOptions {\n /** act executable; default `RIG_ACT_BIN` env, else `act` on PATH. */\n actBin?: string;\n /** `runs-on` label → Docker image (`-P label=image`). */\n platforms?: Record<string, string>;\n /** Where act's artifact server stores uploads; default a fresh temp dir per run. */\n artifactDir?: string;\n /** Pass `--pull=false` (use local images only). Default: act's own default (pull). */\n pull?: boolean;\n /** Grace period between SIGTERM and SIGKILL on timeout/cancel (ms). */\n killGraceMs?: number;\n /** Docker CLI used to remove the job containers a killed act leaves behind; default `docker`. */\n dockerBin?: string;\n /** Sink for runner-level notes (containers removed, cleanup failures). */\n warn?: (line: string) => void;\n}\n\nconst execFileAsync = promisify(execFile);\n\n// ---------------------------------------------------------------------------\n// act container naming (pure)\n// ---------------------------------------------------------------------------\n\n/**\n * act's `createContainerName` sanitizer, mirrored exactly: every\n * non-alphanumeric byte becomes `-`, then ONE pass replaces `--` with `-`\n * (so `---` becomes `--`), and the result is cut to 63 characters with\n * trailing dashes trimmed (act appends `-<sha256 hex>` of the untrimmed\n * name, which this deliberately omits).\n */\nexport function sanitizeActContainerName(raw: string): string {\n const dashed = raw.replace(/[^a-zA-Z0-9]/g, '-').replaceAll('--', '-');\n return dashed.slice(0, 63).replace(/-+$/, '');\n}\n\n/**\n * The name prefix shared by every job container act starts for a workflow:\n * `act-<workflow name>-` sanitized as act does. The workflow name is the\n * file's `name:` key, else its basename (act's own fallback), so\n * `.github/workflows/hang.yml` with no `name:` yields `act-hang-yml-` and its\n * `hang` job runs in `act-hang-yml-hang-<sha256>`.\n */\nexport function actContainerNamePrefix(workflowName: string): string {\n const prefix = `act-${workflowName}-`\n .replace(/[^a-zA-Z0-9]/g, '-')\n .replaceAll('--', '-');\n // The full name is cut at 63 before the hash; a longer prefix can never\n // match a real container, so cut it the same way (keeping the dash).\n return prefix.length > 63 ? prefix.slice(0, 63) : prefix;\n}\n\n/** Slack allowed between our clock and Docker's `Created` stamp (ms). */\nconst CONTAINER_CREATED_SLACK_MS = 5_000;\n\n// ---------------------------------------------------------------------------\n// Binary resolution\n// ---------------------------------------------------------------------------\n\nfunction isExecutable(path: string): boolean {\n try {\n accessSync(path, constants.X_OK);\n return statSync(path).isFile();\n } catch {\n return false;\n }\n}\n\n/**\n * Find an act binary: `RIG_ACT_BIN` (must exist), else the first `act` on\n * PATH. Returns null when neither is present — the coordinator refuses to\n * start and the Docker-gated test skips.\n */\nexport function resolveActBinary(\n env: NodeJS.ProcessEnv,\n explicit?: string\n): string | null {\n const candidate = explicit ?? env[RIG_ACT_BIN_ENV];\n if (candidate) return isExecutable(candidate) ? candidate : null;\n for (const dir of (env['PATH'] ?? '').split(':')) {\n if (!dir) continue;\n const p = join(dir, 'act');\n if (isExecutable(p)) return p;\n }\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// act --json line parsing (pure)\n// ---------------------------------------------------------------------------\n\n/** One decoded line of act's `--json` stream that belongs to a job. */\nexport interface ActLine {\n jobId: string;\n /** act's display name minus the `<workflow>/` prefix and padding. */\n jobName: string;\n msg: string;\n level: string;\n /** Step stdout/stderr (as opposed to act's own narration). */\n rawOutput: boolean;\n /** Present on the job's terminal line. */\n jobResult?: string;\n /** Parsed from act's `exitcode '<n>': failure` error line. */\n exitCode?: number;\n /** Epoch milliseconds from the line's `time` field (0 when absent). */\n timeMs: number;\n}\n\nconst EXITCODE_RE = /^exitcode '(\\d+)'/;\n\n/** Decode one line; null for non-JSON lines or lines without a `jobID`. */\nexport function parseActJsonLine(line: string): ActLine | null {\n if (!line.startsWith('{')) return null;\n let obj: Record<string, unknown>;\n try {\n obj = JSON.parse(line) as Record<string, unknown>;\n } catch {\n return null;\n }\n const jobId = obj['jobID'];\n if (typeof jobId !== 'string' || jobId === '') return null;\n const display = typeof obj['job'] === 'string' ? obj['job'] : jobId;\n const slash = display.indexOf('/');\n const jobName =\n (slash >= 0 ? display.slice(slash + 1) : display).trim() || jobId;\n const msg = typeof obj['msg'] === 'string' ? obj['msg'] : '';\n const out: ActLine = {\n jobId,\n jobName,\n msg,\n level: typeof obj['level'] === 'string' ? obj['level'] : 'info',\n rawOutput: obj['raw_output'] === true,\n timeMs: typeof obj['time'] === 'string' ? Date.parse(obj['time']) || 0 : 0,\n };\n if (typeof obj['jobResult'] === 'string') out.jobResult = obj['jobResult'];\n const exit = EXITCODE_RE.exec(msg);\n if (exit) out.exitCode = Number(exit[1]);\n return out;\n}\n\nexport interface ActRunOutcome {\n /** act's exit code, or null when it was killed. */\n exitCode: number | null;\n timedOut: boolean;\n cancelled: boolean;\n}\n\nexport interface ActJobSummary {\n jobId: string;\n name: string;\n conclusion: CiConclusion;\n exitCode?: number;\n log: string;\n startedAt: number;\n finishedAt: number;\n}\n\nexport interface ActRunSummary {\n conclusion: CiConclusion;\n jobs: ActJobSummary[];\n}\n\n/**\n * act's verdict for one job, corrected for a run WE ended: act reports the\n * job it tears down on SIGTERM as `failure`, but that failure is our kill,\n * not the job's — so once the wall clock expired (or the run was cancelled)\n * only jobs that already finished cleanly keep their own conclusion.\n */\nfunction mapJobResult(\n result: string | undefined,\n outcome: ActRunOutcome\n): CiConclusion {\n if (result === 'success') return 'success';\n if (result === 'skipped') return 'skipped';\n if (outcome.timedOut) return 'timed_out';\n if (outcome.cancelled) return 'cancelled';\n switch (result) {\n case 'failure':\n return 'failure';\n case 'cancelled':\n return 'cancelled';\n default:\n return outcome.exitCode === 0 ? 'success' : 'failure';\n }\n}\n\n/**\n * Fold the job lines into per-job results and one combined conclusion.\n * The combined verdict is act's own where it has one (a failed job fails\n * the run); an unfinished run is `timed_out`/`cancelled`; no job lines at\n * all with a non-zero exit is `startup_failure` (act could not even plan\n * the workflow — parse error, missing image, bad `runs-on`).\n */\nexport function summarizeActRun(\n lines: ActLine[],\n outcome: ActRunOutcome\n): ActRunSummary {\n const byJob = new Map<string, ActLine[]>();\n for (const l of lines) {\n const list = byJob.get(l.jobId);\n if (list) list.push(l);\n else byJob.set(l.jobId, [l]);\n }\n const jobs: ActJobSummary[] = [];\n for (const [jobId, list] of byJob) {\n const terminal = list.find((l) => l.jobResult !== undefined);\n const exitLine = list.find((l) => l.exitCode !== undefined);\n const conclusion = mapJobResult(terminal?.jobResult, outcome);\n const times = list.map((l) => l.timeMs).filter((t) => t > 0);\n const startedAt =\n times.length > 0 ? Math.floor(Math.min(...times) / 1000) : 0;\n const finishedAt =\n times.length > 0 ? Math.floor(Math.max(...times) / 1000) : 0;\n const log = list\n .filter((l) => l.exitCode === undefined)\n .map((l) => (l.rawOutput || l.msg.endsWith('\\n') ? l.msg : `${l.msg}\\n`))\n .join('');\n const job: ActJobSummary = {\n jobId,\n name: list[0]?.jobName ?? jobId,\n conclusion,\n log,\n startedAt,\n finishedAt,\n };\n if (exitLine?.exitCode !== undefined) job.exitCode = exitLine.exitCode;\n else if (conclusion === 'success') job.exitCode = 0;\n jobs.push(job);\n }\n\n let conclusion: CiConclusion;\n if (jobs.length === 0) {\n conclusion = outcome.timedOut\n ? 'timed_out'\n : outcome.cancelled\n ? 'cancelled'\n : outcome.exitCode === 0\n ? 'success'\n : 'startup_failure';\n } else if (\n outcome.timedOut &&\n jobs.some((j) => j.conclusion === 'timed_out')\n ) {\n conclusion = 'timed_out';\n } else if (\n outcome.cancelled &&\n jobs.some((j) => j.conclusion === 'cancelled')\n ) {\n conclusion = 'cancelled';\n } else if (jobs.some((j) => j.conclusion === 'failure')) {\n conclusion = 'failure';\n } else if (jobs.some((j) => j.conclusion === 'timed_out')) {\n conclusion = 'timed_out';\n } else if (jobs.some((j) => j.conclusion === 'cancelled')) {\n conclusion = 'cancelled';\n } else {\n conclusion = 'success';\n }\n return { conclusion, jobs };\n}\n\n// ---------------------------------------------------------------------------\n// Event name + payload (pure)\n// ---------------------------------------------------------------------------\n\n/**\n * The act event for a trigger. A manual replay (NIP-C1 `o: manual`) runs a\n * workflow that need not declare `workflow_dispatch`, so it borrows the\n * first event the workflow DOES declare — otherwise act would plan no jobs.\n */\nexport function actEventName(\n trigger: CiTriggerContext,\n triggers: WorkflowTriggers\n): string {\n if (trigger.reason === 'push') return 'push';\n if (trigger.reason === 'pull_request') return 'pull_request';\n if (triggers.push) return 'push';\n if (triggers.pull_request) return 'pull_request';\n return 'workflow_dispatch';\n}\n\nfunction shortBranch(ref: string | undefined): string {\n if (!ref) return 'main';\n return ref.startsWith('refs/heads/') ? ref.slice('refs/heads/'.length) : ref;\n}\n\n/** The `github.event` payload act reads from `-e`. */\nexport function buildActEventPayload(\n trigger: CiTriggerContext\n): Record<string, unknown> {\n if (trigger.reason === 'pull_request' && trigger.pr) {\n const base = shortBranch(trigger.ref);\n return {\n action: 'synchronize',\n number: 0,\n ref: `refs/pull/0/merge`,\n pull_request: {\n number: 0,\n head: { sha: trigger.commit, ref: trigger.commit.slice(0, 12) },\n base: { ref: base },\n },\n };\n }\n return {\n ref: trigger.ref ?? 'refs/heads/main',\n before: '0'.repeat(40),\n after: trigger.commit,\n head_commit: { id: trigger.commit },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Zip extraction (central-directory driven: upload-artifact zips use data\n// descriptors, so local headers carry zero sizes) + artifact collection\n// ---------------------------------------------------------------------------\n\nconst EOCD_SIG = 0x06054b50;\nconst CDIR_SIG = 0x02014b50;\nconst LOCAL_SIG = 0x04034b50;\n\n/**\n * Extract a (non-zip64) zip into `destDir`, returning the extracted entry\n * names (forward-slash relative paths). Directory entries are skipped;\n * entries that would escape `destDir` throw.\n */\nexport function extractZip(zipPath: string, destDir: string): string[] {\n const buf = readFileSync(zipPath);\n // Find the End Of Central Directory record (scan back over the comment).\n let eocd = -1;\n for (\n let i = buf.length - 22;\n i >= Math.max(0, buf.length - 22 - 0xffff);\n i--\n ) {\n if (buf.readUInt32LE(i) === EOCD_SIG) {\n eocd = i;\n break;\n }\n }\n if (eocd < 0) throw new Error(`not a zip file: ${zipPath}`);\n const entryCount = buf.readUInt16LE(eocd + 10);\n let offset = buf.readUInt32LE(eocd + 16);\n if (offset === 0xffffffff)\n throw new Error(`zip64 archives are not supported: ${zipPath}`);\n\n const root = resolve(destDir);\n const names: string[] = [];\n for (let n = 0; n < entryCount; n++) {\n if (buf.readUInt32LE(offset) !== CDIR_SIG)\n throw new Error(`corrupt central directory: ${zipPath}`);\n const method = buf.readUInt16LE(offset + 10);\n const compSize = buf.readUInt32LE(offset + 20);\n const nameLen = buf.readUInt16LE(offset + 28);\n const extraLen = buf.readUInt16LE(offset + 30);\n const commentLen = buf.readUInt16LE(offset + 32);\n const localOffset = buf.readUInt32LE(offset + 42);\n const name = buf.toString('utf-8', offset + 46, offset + 46 + nameLen);\n offset += 46 + nameLen + extraLen + commentLen;\n\n if (name.endsWith('/')) continue;\n const target = resolve(root, name);\n if (target !== root && !target.startsWith(root + sep)) {\n throw new Error(\n `zip entry would escape the destination: ${JSON.stringify(name)}`\n );\n }\n if (buf.readUInt32LE(localOffset) !== LOCAL_SIG)\n throw new Error(`corrupt local header: ${zipPath}`);\n const localNameLen = buf.readUInt16LE(localOffset + 26);\n const localExtraLen = buf.readUInt16LE(localOffset + 28);\n const dataStart = localOffset + 30 + localNameLen + localExtraLen;\n const data = buf.subarray(dataStart, dataStart + compSize);\n let bytes: Buffer;\n if (method === 0) bytes = Buffer.from(data);\n else if (method === 8) bytes = inflateRawSync(data);\n else\n throw new Error(\n `unsupported zip compression method ${method} in ${zipPath}`\n );\n mkdirSync(dirname(target), { recursive: true });\n writeFileSync(target, bytes);\n names.push(name);\n }\n return names;\n}\n\nfunction walkFiles(dir: string): string[] {\n const out: string[] = [];\n for (const entry of readdirSync(dir, { withFileTypes: true }).sort((a, b) =>\n a.name.localeCompare(b.name)\n )) {\n const p = join(dir, entry.name);\n if (entry.isDirectory()) out.push(...walkFiles(p));\n else if (entry.isFile()) out.push(p);\n }\n return out;\n}\n\n/**\n * Turn act's artifact-server tree (`<dir>/<run>/<name>/…`) into one\n * {@link RunnerArtifact} per FILE. Zips are unpacked beside themselves into\n * `<name>/unpacked/`; anything else is listed as-is.\n */\nexport function collectArtifacts(artifactDir: string): RunnerArtifact[] {\n if (!existsSync(artifactDir)) return [];\n const out: RunnerArtifact[] = [];\n for (const run of readdirSync(artifactDir, { withFileTypes: true })) {\n if (!run.isDirectory()) continue;\n const runDir = join(artifactDir, run.name);\n for (const art of readdirSync(runDir, { withFileTypes: true }).sort(\n (a, b) => a.name.localeCompare(b.name)\n )) {\n if (!art.isDirectory()) continue;\n const artDir = join(runDir, art.name);\n const unpackedDir = join(artDir, 'unpacked');\n for (const file of walkFiles(artDir)) {\n if (file.startsWith(unpackedDir + sep)) continue;\n if (file.toLowerCase().endsWith('.zip')) {\n for (const name of extractZip(file, unpackedDir)) {\n out.push({\n path: join(unpackedDir, name),\n filename: name,\n name: art.name,\n });\n }\n } else {\n out.push({\n path: file,\n filename: relative(artDir, file).split(sep).join('/'),\n name: art.name,\n });\n }\n }\n }\n }\n return out;\n}\n\n// ---------------------------------------------------------------------------\n// The Runner\n// ---------------------------------------------------------------------------\n\n/** Shell out to `act` on the host Docker daemon. See the module header. */\nexport class ActRunner implements Runner {\n readonly family = 'act' as const;\n readonly selectors: string[];\n private readonly platforms: Record<string, string>;\n\n constructor(private readonly options: ActRunnerOptions = {}) {\n this.platforms = options.platforms ?? { ...DEFAULT_ACT_PLATFORMS };\n this.selectors = Object.keys(this.platforms);\n }\n\n /** The resolved act binary, or null when none is available. */\n binary(env: NodeJS.ProcessEnv = process.env): string | null {\n return resolveActBinary(env, this.options.actBin);\n }\n\n async run(request: RunnerRequest): Promise<RunnerRunResult> {\n const startedAt = Math.floor(Date.now() / 1000);\n const bin = this.binary();\n if (bin === null) {\n return startupFailure(\n startedAt,\n 'act binary not found (set RIG_ACT_BIN or install act)'\n );\n }\n\n const workflowText = readFileSync(\n join(request.checkoutDir, request.workflow.path)\n );\n const parsed = parseWorkflow(request.workflow.path, workflowText);\n if (parsed.parseError !== undefined) {\n return startupFailure(\n startedAt,\n `workflow parse error: ${parsed.parseError}`\n );\n }\n\n const scratch = await mkdtemp(join(tmpdir(), 'rig-act-'));\n const artifactDir = this.options.artifactDir ?? join(scratch, 'artifacts');\n mkdirSync(artifactDir, { recursive: true });\n const eventPath = join(scratch, 'event.json');\n writeFileSync(\n eventPath,\n JSON.stringify(buildActEventPayload(request.trigger))\n );\n\n const args = [\n actEventName(request.trigger, parsed.triggers),\n '-W',\n request.workflow.path,\n '-C',\n request.checkoutDir,\n '--json',\n '-e',\n eventPath,\n '--artifact-server-path',\n artifactDir,\n ];\n if (this.options.pull === false) args.push('--pull=false');\n for (const [label, image] of Object.entries(this.platforms)) {\n args.push('-P', `${label}=${image}`);\n }\n for (const name of Object.keys(request.secrets)) args.push('-s', name);\n for (const name of Object.keys(request.env ?? {})) args.push('--env', name);\n\n const env: NodeJS.ProcessEnv = {\n ...process.env,\n ...request.env,\n ...request.secrets,\n };\n\n const lines: ActLine[] = [];\n let timedOut = false;\n let cancelled = false;\n const startedAtMs = Date.now();\n const grace = this.options.killGraceMs ?? 10_000;\n\n const exitCode = await new Promise<number | null>((resolveExit) => {\n const child = spawn(bin, args, {\n cwd: request.checkoutDir,\n env,\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n let killTimer: ReturnType<typeof setTimeout> | undefined;\n const kill = (): void => {\n child.kill('SIGTERM');\n killTimer = setTimeout(() => child.kill('SIGKILL'), grace);\n };\n const timeout = setTimeout(() => {\n timedOut = true;\n kill();\n }, request.timeoutMs);\n const onAbort = (): void => {\n cancelled = true;\n kill();\n };\n if (request.signal?.aborted) onAbort();\n else request.signal?.addEventListener('abort', onAbort, { once: true });\n\n const consume = (stream: NodeJS.ReadableStream): void => {\n let buffered = '';\n stream.setEncoding('utf-8');\n stream.on('data', (chunk: string) => {\n buffered += chunk;\n let nl: number;\n while ((nl = buffered.indexOf('\\n')) >= 0) {\n const line = buffered.slice(0, nl);\n buffered = buffered.slice(nl + 1);\n const parsedLine = parseActJsonLine(line);\n if (!parsedLine) continue;\n lines.push(parsedLine);\n if (request.onLog && parsedLine.exitCode === undefined) {\n request.onLog(\n parsedLine.jobId,\n parsedLine.rawOutput || parsedLine.msg.endsWith('\\n')\n ? parsedLine.msg\n : `${parsedLine.msg}\\n`\n );\n }\n }\n });\n stream.on('end', () => {\n const parsedLine = parseActJsonLine(buffered);\n if (parsedLine) lines.push(parsedLine);\n });\n };\n consume(child.stdout);\n consume(child.stderr);\n\n child.on('error', () => resolveExit(null));\n child.on('close', (code) => {\n clearTimeout(timeout);\n if (killTimer) clearTimeout(killTimer);\n request.signal?.removeEventListener('abort', onAbort);\n resolveExit(code);\n });\n });\n\n if (timedOut || cancelled) {\n await this.removeOrphanedContainers(\n actContainerNamePrefix(parsed.name ?? basename(request.workflow.path)),\n startedAtMs,\n (note) => {\n this.options.warn?.(note);\n request.onLog?.('runner', `${note}\\n`);\n }\n );\n }\n\n const summary = summarizeActRun(lines, { exitCode, timedOut, cancelled });\n const artifacts = collectArtifacts(artifactDir);\n const finishedAt = Math.floor(Date.now() / 1000);\n\n // act's artifact server does not record which job uploaded what; the\n // uploading job announces \"Artifact <name> has been successfully\n // uploaded\" in its log, so attribute by that line, else to the first job.\n const jobs: RunnerJobResult[] = summary.jobs.map((j) => ({\n jobId: j.jobId,\n name: j.name,\n conclusion: j.conclusion,\n ...(j.exitCode !== undefined ? { exitCode: j.exitCode } : {}),\n startedAt: j.startedAt || startedAt,\n finishedAt: j.finishedAt || finishedAt,\n log: j.log,\n artifacts: [],\n }));\n for (const artifact of artifacts) {\n const owner =\n jobs.find((j) =>\n j.log.includes(\n `Artifact ${artifact.name} has been successfully uploaded`\n )\n ) ?? jobs[0];\n owner?.artifacts.push(artifact);\n }\n\n // Keep the scratch dir when it holds the artifacts the caller will read.\n if (this.options.artifactDir !== undefined) {\n await rm(scratch, { recursive: true, force: true });\n }\n\n return { conclusion: summary.conclusion, jobs, startedAt, finishedAt };\n }\n\n /**\n * Remove the job containers a killed act left running (see the module\n * header): every container whose name starts with `prefix` and whose\n * Docker `Created` stamp is at or after this run's start (minus a little\n * slack). Never throws — a failed cleanup is reported through `note` and\n * the run result is still returned.\n */\n private async removeOrphanedContainers(\n prefix: string,\n startedAtMs: number,\n note: (line: string) => void\n ): Promise<void> {\n const docker = this.options.dockerBin ?? 'docker';\n const run = async (args: string[]): Promise<string> => {\n const { stdout } = await execFileAsync(docker, args, {\n encoding: 'utf-8',\n timeout: 30_000,\n });\n return stdout;\n };\n try {\n const ids = (await run(['ps', '-aq', '--filter', `name=^${prefix}`]))\n .split('\\n')\n .map((id) => id.trim())\n .filter((id) => id !== '');\n const ours: string[] = [];\n for (const id of ids) {\n const created = Date.parse(\n (await run(['inspect', '-f', '{{.Created}}', id])).trim()\n );\n if (\n Number.isNaN(created) ||\n created >= startedAtMs - CONTAINER_CREATED_SLACK_MS\n ) {\n ours.push(id);\n }\n }\n if (ours.length === 0) return;\n await run(['rm', '-f', ...ours]);\n note(\n `removed ${ours.length} job container(s) act left running after the kill (${prefix}*)`\n );\n } catch (err) {\n note(\n `could not remove act's leftover job containers (${prefix}*): ` +\n `${err instanceof Error ? err.message : String(err)} — remove them by hand`\n );\n }\n }\n}\n\nfunction startupFailure(startedAt: number, reason: string): RunnerRunResult {\n return {\n conclusion: 'startup_failure',\n startedAt,\n finishedAt: Math.floor(Date.now() / 1000),\n jobs: [\n {\n jobId: 'startup',\n conclusion: 'startup_failure',\n startedAt,\n finishedAt: Math.floor(Date.now() / 1000),\n log: `${reason}\\n`,\n artifacts: [],\n },\n ],\n };\n}\n","/**\n * The coordinator loop (#125): what `rig ci serve` runs.\n *\n * A Coordinator is an ordinary rig identity (its own seed, wallet, and\n * channel) that watches served repos on ONE relay and turns triggers into\n * runs, publishing NIP-C1 progress and results as paid writes from its own\n * channel. The relay is the only control plane: every input is a relay\n * event, every output is a relay event or a store upload.\n *\n * inputs kind:30617/30618 (owner's announcement + state → maintainers,\n * default branch, pushes), kind:1617/1618/1619 (pull requests and\n * their updates), and the coordinator inbox — kind:9843/9844\n * (Service Request / Stop), kind:9840 (Manual Trigger), kind:29846\n * (Repository Secret Update), all with `p` = this coordinator.\n * outputs kind:19843 Advertisement (renewed before it expires), and per\n * run the NIP-C1 sequence: 39842 `queued` → 39842 `in_progress`\n * (frozen provenance, job list) → per job a log upload + 9841 +\n * a renewed 39842 quoting it → 9842 → 39842 `concluded`.\n *\n * Authorization (user story 12): a repo is served only while\n * `selectServiceRequests` finds an accepted, unstopped 9843 from the owner or\n * a declared maintainer — or from a pubkey the operator explicitly accepts\n * (`acceptedRequesters`, NIP-C1's operator policy; story 21: a contributor\n * serving a repo they do not maintain, at lower trust) — re-evaluated at\n * EVERY trigger and again at runner handoff (the frozen `service-request`\n * quote). A Manual Trigger needs its author to be a maintainer and no\n * standing request. Secrets (story 7) are\n * injected only when the trigger's author is a maintainer; a stranger's pull\n * request runs with an empty secret set.\n *\n * Money (story 15): before a run publishes anything the coordinator asks the\n * publisher for its fee rates (a failure means the channel is not usable)\n * and consults the injectable `canAfford` seam; a refused run is logged and\n * never half-published. Every relay write and store upload goes through the\n * ONE `Publisher` rig already has (story 28), serialized — the standalone\n * publisher is a sequential, nonce-guarded transport.\n *\n * Restart safety (story 13): ./state.ts persists a per-repo cursor (last\n * processed event, the ref map pushes are diffed against, processed ids) and\n * the secret inventory; ./relay-subscription.ts reconnects with backoff and\n * resumes from the cursor. The very first sight of a repo records its refs\n * WITHOUT running anything, so a fresh coordinator never storms through a\n * repository's existing history.\n *\n * Runner seam (story 22): the coordinator materializes the commit itself\n * (./materialize-commit.ts, rig's free read path) and hands a checkout to a\n * `Runner`; nothing here knows about act or Docker.\n *\n * Deliberate departures from ngit's reference coordinator, all noted in the\n * spec: logs and artifacts go to the TOON store and are referenced by\n * gateway URL; the advertisement carries `B = out-of-band`; the repo's own\n * relay doubles as the secret inbox; kind:1617 patches (what `rig pr\n * create` publishes) are pull-request triggers too — the base is materialized\n * and the patch applied with `git am`, so the run's commit is the resulting\n * HEAD, with the patch's declared tip kept as a second `c` tag so a `#c`\n * lookup by either id finds the run.\n */\n\nimport { randomUUID } from 'node:crypto';\nimport { readFile, rm } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { REPOSITORY_ANNOUNCEMENT_KIND } from '@toon-protocol/core/nip34';\nimport { contentTypeForPath } from '../mime.js';\nimport {\n REPOSITORY_STATE_KIND,\n authorizedStatusAuthors,\n} from '../nip34-events.js';\nimport type { UnsignedEvent } from '../nip34-events.js';\nimport type { FetchLike } from '../object-fetch.js';\nimport type { FeeRates, Publisher } from '../publisher.js';\nimport {\n defaultWebSocketFactory,\n fetchRemoteState,\n queryRelay,\n type NostrEvent,\n type NostrFilter,\n type WebSocketFactory,\n} from '../remote-state.js';\nimport { runGit } from '../materialize.js';\nimport {\n MaterializeError,\n materializeCommit,\n type MaterializeCommit,\n} from './materialize-commit.js';\nimport {\n CI_MANUAL_TRIGGER_KIND,\n CI_SECRET_UPDATE_KIND,\n CI_SERVICE_REQUEST_KIND,\n CI_SERVICE_STOP_KIND,\n CI_WORKFLOW_PROGRESS_KIND,\n buildCiAdvertisement,\n buildCiJobResult,\n buildCiWorkflowProgress,\n buildCiWorkflowResult,\n parseCiManualTrigger,\n parseCiSecretUpdate,\n parseCiServiceControl,\n parseRepoAddress,\n repoAddress,\n selectServiceRequests,\n type CiConclusion,\n type CiJobQuote,\n type CiPrContext,\n type CiProvenance,\n type CiTriggerContext,\n type CiTriggerReason,\n type CiWorkflowRef,\n type RepoAddress,\n type ServiceControl,\n} from './nip-c1-events.js';\nimport {\n subscribeRelay,\n realScheduler,\n type RelaySubscription,\n type Scheduler,\n} from './relay-subscription.js';\nimport type { Runner, RunnerRunResult } from './runner.js';\nimport {\n applySecretUpdate,\n containsSecretValue,\n decryptSecretUpdate,\n effectiveSecrets,\n generateSecretsKey,\n redactSecretValues,\n validateSecretUpdate,\n type SecretInventory,\n} from './secrets.js';\nimport {\n loadCoordinatorState,\n saveCursor,\n saveSecrets,\n type CoordinatorCursor,\n type InboxCursor,\n type RepoCursor,\n} from './state.js';\nimport {\n discoverWorkflows,\n matchesPullRequest,\n matchesPush,\n type DiscoveredWorkflow,\n} from './workflows.js';\n\n// ---------------------------------------------------------------------------\n// Options + handle\n// ---------------------------------------------------------------------------\n\nexport interface CoordinatorRepo {\n ownerPubkey: string;\n repoId: string;\n}\n\n/** What one run is expected to cost, for the affordability seam. */\nexport interface RunCostEstimate {\n /** Relay events the run will publish (progress, job results, result). */\n events: number;\n /** Store uploads (one log per job, plus artifacts). */\n uploads: number;\n rates: FeeRates;\n}\n\nexport type CanAfford = (estimate: RunCostEstimate) => Promise<boolean>;\n\n/** What {@link CoordinatorOptions.onRunConcluded} reports: a run whose result is on the relay. */\nexport interface ConcludedRun {\n runId: string;\n repoAddr: RepoAddress;\n trigger: CiTriggerContext;\n conclusion: CiConclusion;\n /** Event id of the run's Workflow Result (9842). */\n resultEventId: string;\n}\n\nexport interface CoordinatorOptions {\n /** Hex pubkey of the coordinator identity (the publisher signs as it). */\n coordinatorPubkey: string;\n publisher: Publisher;\n /** The ONE relay: subscriptions and paid publishes alike. */\n relayUrl: string;\n /** Gateway base URL for log/artifact links (`<gateway>/raw/<txId>`). */\n gatewayUrl: string;\n repos: CoordinatorRepo[];\n runner: Runner;\n stateDir: string;\n /** Where checkouts are materialized (one directory per trigger). */\n workdir: string;\n /** rig version, for the advertisement's `software` tag. */\n version: string;\n /** Concurrent runs (default 1). */\n concurrency?: number;\n /** Wall-clock budget per run in ms (default 30 min) → `timed_out`. */\n timeoutMs?: number;\n webSocketFactory?: WebSocketFactory;\n fetchFn?: FetchLike;\n resolveSha?: (sha: string, repo: string) => Promise<string | null>;\n gateways?: readonly string[];\n /** Unix-seconds clock (default: Date.now()/1000). */\n clock?: () => number;\n /** Timer seam (default: real timers). */\n scheduler?: Scheduler;\n log?: (line: string) => void;\n /** Materialization seam (default: the free read path). */\n materialize?: MaterializeCommit;\n /** Affordability seam (default: always true once fee rates are readable). */\n canAfford?: CanAfford;\n /**\n * Requester pubkeys (hex) whose Service Requests are accepted in addition\n * to the repo's owner and maintainers (NIP-C1 operator policy). They are\n * NOT maintainers: their Stops close only their own Requests, their pushes\n * and PRs get no secrets, and runs they cause carry lower trust.\n */\n acceptedRequesters?: Iterable<string>;\n /** Advertisement TTL in seconds (≤ 1800; default 1800, renewed at 5/6). */\n advertisementTtlSeconds?: number;\n /** Keep checkouts after their runs (default: delete). */\n keepCheckouts?: boolean;\n /**\n * Called once per run after its Workflow Result (9842) and final\n * `concluded` progress marker are published — including runs that never\n * reached the Runner (`startup_failure`, `cancelled`). Ignored or refused\n * triggers publish nothing and never reach it. `rig ci serve --once`\n * stops on the first call.\n */\n onRunConcluded?: (run: ConcludedRun) => void;\n}\n\nexport interface CoordinatorHandle {\n pubkey: string;\n /** The current NIP-44 recipient (`secrets-key`) pubkey. */\n readonly secretsKeyPubkey: string;\n /** Id of the current advertisement event, once published. */\n readonly advertisementId: string | null;\n /** Resolves after `stop()` has finished. */\n stopped: Promise<void>;\n stop(): Promise<void>;\n /** Resolves when no event is being handled and no run is queued or active. */\n idle(): Promise<void>;\n /** Which served repos currently have an accepted, unstopped Service Request. */\n serving(): { repoAddr: RepoAddress; serving: boolean }[];\n}\n\n// ---------------------------------------------------------------------------\n// Internal shapes\n// ---------------------------------------------------------------------------\n\ninterface RepoRuntime {\n addr: RepoAddress;\n ownerPubkey: string;\n repoId: string;\n authorized: Set<string>;\n /** Every 9843/9844 seen for this repo (by event id). */\n controls: Map<string, ServiceControl>;\n cursor: RepoCursor | undefined;\n secrets: SecretInventory;\n /** Known 1617/1618 events by id, so a 1619 update can find its PR. */\n prEvents: Map<string, NostrEvent>;\n defaultBranch: string | null;\n}\n\ninterface PendingTrigger {\n repo: RepoRuntime;\n reason: CiTriggerReason;\n /** Who caused the trigger — decides whether secrets are injected. */\n authorPubkey: string;\n /** The commit (or tag object) to materialize; a patch's base when `patch`. */\n commit: string;\n ref?: string;\n pr?: CiPrContext;\n patch?: { content: string; declaredTip?: string };\n manual?: {\n eventId: string;\n pubkey: string;\n workflow: CiWorkflowRef;\n tagObjectIds?: string[];\n };\n}\n\ninterface Checkout {\n dir: string;\n refs: number;\n}\n\ninterface QueuedRun {\n repo: RepoRuntime;\n trigger: CiTriggerContext;\n workflow: DiscoveredWorkflow;\n checkout: Checkout;\n secrets: Record<string, string>;\n manualProvenance?: CiProvenance;\n runId: string;\n queuedAt: number;\n /** Set while the run executes, so a superseding PR update can abort it. */\n abort?: AbortController;\n /** A later update to the same pull request replaced this run's tip. */\n superseded?: boolean;\n}\n\n/** Runs executed at once (`--concurrency`). */\nexport const DEFAULT_CONCURRENCY = 1;\nexport const DEFAULT_RUN_TIMEOUT_MS = 30 * 60 * 1000;\nexport const DEFAULT_ADVERTISEMENT_TTL = 30 * 60;\n/** Renew progress markers and the advertisement at this fraction of their TTL. */\nconst RENEW_FRACTION = 5 / 6;\n/** How much of a job log rides in the 9841 content. */\nexport const LOG_TAIL_BYTES = 4096;\n/** Rotate the secrets key at least this often (NIP-C1: no later than 86,400 s). */\nconst SECRETS_KEY_MAX_AGE = 86_400;\n\nfunction nowSeconds(): number {\n return Math.floor(Date.now() / 1000);\n}\n\n/** The last {@link LOG_TAIL_BYTES} of a log, plus how many bytes were cut. */\nexport function logTail(log: string): { tail: string; omitted: number } {\n const bytes = Buffer.from(log, 'utf-8');\n if (bytes.byteLength <= LOG_TAIL_BYTES) return { tail: log, omitted: 0 };\n const cut = bytes.subarray(bytes.byteLength - LOG_TAIL_BYTES);\n return {\n tail: cut.toString('utf-8'),\n omitted: bytes.byteLength - LOG_TAIL_BYTES,\n };\n}\n\n/** `<gateway>/raw/<txId>` — the TOON store's raw-bytes route. */\nexport function gatewayRawUrl(gatewayUrl: string, txId: string): string {\n return `${gatewayUrl.replace(/\\/+$/, '')}/raw/${txId}`;\n}\n\n/** Which refs moved between two ref maps (new or changed shas). */\nexport function movedRefs(\n before: Record<string, string>,\n after: Record<string, string>\n): { ref: string; sha: string }[] {\n const moved: { ref: string; sha: string }[] = [];\n for (const [ref, sha] of Object.entries(after)) {\n if (before[ref] !== sha) moved.push({ ref, sha });\n }\n return moved;\n}\n\nfunction parseRefsTags(ev: NostrEvent): {\n refs: Record<string, string>;\n head: string | null;\n} {\n const refs: Record<string, string> = {};\n let head: string | null = null;\n for (const [name, v1, v2] of ev.tags) {\n if (name === 'r' && v1 && v2) {\n if (v1 === 'HEAD' && v2.startsWith('ref: ')) head = v2.slice(5);\n else refs[v1] = v2;\n } else if (name === 'HEAD' && v1?.startsWith('ref: ')) {\n head = v1.slice(5);\n }\n }\n return { refs, head };\n}\n\nfunction tagValues(tags: string[][], name: string): string[] {\n return tags\n .filter((t) => t[0] === name)\n .map((t) => t[1] ?? '')\n .filter((v) => v !== '');\n}\n\n/** A promise-based mutex: `publishEvent`/`uploadBlob` are called one at a time. */\nclass Serial {\n private chain: Promise<unknown> = Promise.resolve();\n run<T>(fn: () => Promise<T>): Promise<T> {\n const next = this.chain.then(fn, fn);\n this.chain = next.catch(() => undefined);\n return next;\n }\n}\n\n// ---------------------------------------------------------------------------\n// startCoordinator\n// ---------------------------------------------------------------------------\n\nexport async function startCoordinator(\n opts: CoordinatorOptions\n): Promise<CoordinatorHandle> {\n const me = opts.coordinatorPubkey.toLowerCase();\n const { publisher, relayUrl, runner } = opts;\n const concurrency = Math.max(1, opts.concurrency ?? DEFAULT_CONCURRENCY);\n const timeoutMs = opts.timeoutMs ?? DEFAULT_RUN_TIMEOUT_MS;\n const clock = opts.clock ?? nowSeconds;\n const scheduler = opts.scheduler ?? realScheduler;\n const log = opts.log ?? (() => undefined);\n const materialize = opts.materialize ?? materializeCommit;\n const canAfford: CanAfford = opts.canAfford ?? (async () => true);\n const acceptedRequesters = new Set(\n [...(opts.acceptedRequesters ?? [])].map((p) => p.toLowerCase())\n );\n const adTtl = Math.min(\n opts.advertisementTtlSeconds ?? DEFAULT_ADVERTISEMENT_TTL,\n 1800\n );\n\n if (!publisher.uploadBlob) {\n throw new Error(\n 'the coordinator needs a publisher that can upload blobs (job logs and artifacts) — ' +\n 'the standalone publisher provides uploadBlob'\n );\n }\n const uploadBlob = publisher.uploadBlob.bind(publisher);\n if (opts.repos.length === 0)\n throw new Error('the coordinator needs at least one --repo to serve');\n\n const serial = new Serial();\n const publish = (event: UnsignedEvent): Promise<string> =>\n serial.run(\n async () => (await publisher.publishEvent(event, [relayUrl])).eventId\n );\n\n // ── State ────────────────────────────────────────────────────────────────\n const state = await loadCoordinatorState(opts.stateDir);\n const repos = new Map<RepoAddress, RepoRuntime>();\n for (const r of opts.repos) {\n const owner = r.ownerPubkey.toLowerCase();\n const addr = repoAddress(owner, r.repoId);\n repos.set(addr, {\n addr,\n ownerPubkey: owner,\n repoId: r.repoId,\n authorized: new Set([owner]),\n controls: new Map(),\n cursor: state.cursor[addr],\n secrets: state.secrets[addr] ?? {},\n prEvents: new Map(),\n defaultBranch: null,\n });\n }\n const inbox: InboxCursor = state.inbox;\n const inboxProcessed = new Set(inbox.processed);\n\n const persistCursor = async (): Promise<void> => {\n const cursor: CoordinatorCursor = {};\n for (const repo of repos.values())\n if (repo.cursor) cursor[repo.addr] = repo.cursor;\n inbox.processed = [...inboxProcessed];\n await saveCursor(opts.stateDir, cursor, inbox);\n };\n const persistSecrets = async (): Promise<void> => {\n const secrets: Record<string, SecretInventory> = {};\n for (const repo of repos.values()) secrets[repo.addr] = repo.secrets;\n await saveSecrets(opts.stateDir, secrets);\n };\n\n // ── Seed each repo from the relay (maintainers, default branch, refs) ────\n for (const repo of repos.values()) {\n const remote = await fetchRemoteState({\n relayUrls: [relayUrl],\n ownerPubkey: repo.ownerPubkey,\n repoId: repo.repoId,\n ...(opts.webSocketFactory\n ? { webSocketFactory: opts.webSocketFactory }\n : {}),\n ...(opts.resolveSha ? { resolveSha: opts.resolveSha } : {}),\n });\n if (remote.announceEvent) {\n repo.authorized = authorizedStatusAuthors(\n repo.ownerPubkey,\n remote.announceEvent.tags\n );\n }\n repo.defaultBranch = remote.headSymref;\n if (repo.cursor === undefined) {\n // First sight of this repo: remember where it stands, run nothing.\n repo.cursor = {\n lastCreatedAt: remote.refsEvent?.created_at ?? 0,\n lastEventId: remote.refsEvent?.id ?? '',\n refs: Object.fromEntries(remote.refs),\n processed: remote.refsEvent ? [remote.refsEvent.id] : [],\n };\n log(\n `[ci] ${repo.repoId}: recorded ${remote.refs.size} ref(s); watching for pushes`\n );\n }\n }\n await persistCursor();\n\n // ── Seed the Service Request / Stop history ─────────────────────────────\n // The live subscription replays it too, but a backlog push must not be\n // judged before the requests that authorize it have been read.\n const isServing = (repo: RepoRuntime): boolean =>\n selectServiceRequests([...repo.controls.values()], {\n coordinatorPubkey: me,\n repoAddr: repo.addr,\n authorized: repo.authorized,\n acceptedRequesters,\n }).active !== null;\n const handleControl = (ev: NostrEvent): void => {\n const control = parseCiServiceControl(ev);\n if (!control || control.coordinatorPubkey !== me) return;\n const repo = repos.get(control.repoAddr);\n if (!repo || repo.controls.has(control.eventId)) return;\n const before = isServing(repo);\n repo.controls.set(control.eventId, control);\n const after = isServing(repo);\n if (before !== after) {\n const who = repo.authorized.has(control.pubkey)\n ? 'maintainer'\n : 'operator-accepted requester';\n log(\n `[ci] ${repo.repoId}: ${after ? `serving (${who} Service Request accepted)` : 'service stopped'}`\n );\n } else if (\n control.kind === 'request' &&\n !repo.authorized.has(control.pubkey) &&\n !acceptedRequesters.has(control.pubkey.toLowerCase())\n ) {\n log(\n `[ci] ${repo.repoId}: Service Request from non-maintainer ${control.pubkey.slice(0, 8)} ignored`\n );\n }\n };\n const controlFilter: NostrFilter = {\n kinds: [CI_SERVICE_REQUEST_KIND, CI_SERVICE_STOP_KIND],\n '#p': [me],\n };\n try {\n const history = await queryRelay(\n relayUrl,\n controlFilter,\n 10_000,\n opts.webSocketFactory ?? defaultWebSocketFactory\n );\n for (const ev of history) handleControl(ev);\n } catch (err) {\n log(\n `[ci] could not read the Service Request history: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n\n // ── Advertisement + secrets key ──────────────────────────────────────────\n let secretsKey = generateSecretsKey();\n let secretsKeyBorn = clock();\n let advertisementId: string | null = null;\n /** Advertisements naming the CURRENT secrets key (id → [created_at, expiration)). */\n const liveAdvertisements = new Map<\n string,\n { createdAt: number; expiresAt: number }\n >();\n let adTimer: unknown = null;\n let stopping = false;\n\n const advertise = async (): Promise<void> => {\n if (stopping) return;\n const now = clock();\n if (now - secretsKeyBorn >= SECRETS_KEY_MAX_AGE) {\n secretsKey = generateSecretsKey();\n secretsKeyBorn = now;\n liveAdvertisements.clear();\n log('[ci] rotated the secrets key');\n }\n const expiresAt = now + adTtl;\n const event = buildCiAdvertisement(\n {\n version: opts.version,\n selectors: runner.selectors,\n admission: 'maintainer-request',\n execution: 'request-required',\n billing: 'out-of-band',\n secretsKey: { pubkey: secretsKey.pubkey, inboxRelays: [relayUrl] },\n expiresAt,\n },\n now\n );\n try {\n advertisementId = await publish(event);\n liveAdvertisements.set(advertisementId, { createdAt: now, expiresAt });\n } catch (err) {\n log(\n `[ci] advertisement publish failed: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n if (!stopping) {\n adTimer = scheduler.setTimeout(\n () => void advertise(),\n adTtl * RENEW_FRACTION * 1000\n );\n }\n };\n await advertise();\n\n // ── Run queue + concurrency slots ───────────────────────────────────────\n const queue: QueuedRun[] = [];\n const activeAborts = new Set<AbortController>();\n const activeRuns = new Set<QueuedRun>();\n let active = 0;\n let inflight = 0; // triggers being handled + runs active\n const idleWaiters: (() => void)[] = [];\n const checkIdle = (): void => {\n if (\n inflight === 0 &&\n queue.length === 0 &&\n eventQueue.length === 0 &&\n !draining\n ) {\n for (const w of idleWaiters.splice(0)) w();\n }\n };\n\n const releaseCheckout = async (checkout: Checkout): Promise<void> => {\n checkout.refs -= 1;\n if (checkout.refs <= 0 && !opts.keepCheckouts) {\n await rm(checkout.dir, { recursive: true, force: true }).catch(\n () => undefined\n );\n }\n };\n\n const progressAddress = (runId: string): string =>\n `${CI_WORKFLOW_PROGRESS_KIND}:${me}:${runId}`;\n\n const concludeWithoutJobs = async (\n run: QueuedRun,\n conclusion: CiConclusion,\n provenance: CiProvenance | undefined,\n startedAt?: number\n ): Promise<void> => {\n const base = {\n trigger: run.trigger,\n runId: run.runId,\n queuedAt: run.queuedAt,\n ...(startedAt !== undefined ? { startedAt } : {}),\n ...(provenance ? { provenance } : {}),\n jobs: [] as CiJobQuote[],\n };\n const now = clock();\n const resultEventId = await publish(\n buildCiWorkflowResult({ ...base, conclusion }, now)\n );\n await publish(\n buildCiWorkflowProgress(\n { ...base, status: 'concluded', conclusion, expiresAt: now + adTtl },\n now\n )\n );\n opts.onRunConcluded?.({\n runId: run.runId,\n repoAddr: run.repo.addr,\n trigger: run.trigger,\n conclusion,\n resultEventId,\n });\n };\n\n const executeRun = async (run: QueuedRun): Promise<void> => {\n const { repo, trigger, workflow } = run;\n const label = `${repo.repoId} ${workflow.path}@${trigger.commit.slice(0, 7)} [${run.runId.slice(0, 8)}]`;\n\n // Final handoff authorization: the frozen provenance quote.\n let provenance: CiProvenance | undefined = run.manualProvenance;\n if (!provenance) {\n const { active: request } = selectServiceRequests(\n [...repo.controls.values()],\n {\n coordinatorPubkey: me,\n repoAddr: repo.addr,\n authorized: repo.authorized,\n acceptedRequesters,\n }\n );\n if (!request) {\n log(`[ci] ${label}: service stopped before handoff — cancelled`);\n await concludeWithoutJobs(run, 'cancelled', undefined);\n return;\n }\n provenance = {\n kind: 'service-request',\n eventId: request.eventId,\n relayUrl,\n pubkey: request.pubkey,\n };\n }\n\n if (workflow.parseError !== undefined) {\n log(`[ci] ${label}: workflow parse error — startup_failure`);\n await concludeWithoutJobs(run, 'startup_failure', provenance, clock());\n return;\n }\n\n const startedAt = clock();\n const jobIds = workflow.jobs.map((j) => j.id);\n const quotes: CiJobQuote[] = [];\n const progress = (\n status: 'in_progress' | 'concluded',\n conclusion?: CiConclusion\n ) => {\n const now = clock();\n return buildCiWorkflowProgress(\n {\n trigger,\n runId: run.runId,\n status,\n ...(conclusion !== undefined ? { conclusion } : {}),\n inProgress:\n status === 'in_progress'\n ? jobIds.filter((id) => !quotes.some((q) => q.jobId === id))\n : [],\n queuedAt: run.queuedAt,\n startedAt,\n provenance: provenance as CiProvenance,\n jobs: quotes,\n expiresAt: now + adTtl,\n },\n now\n );\n };\n await publish(progress('in_progress'));\n log(`[ci] ${label}: in_progress (${jobIds.length} job(s))`);\n\n // Renew the in-progress marker before it expires on long runs.\n let renewTimer: unknown = null;\n const scheduleRenew = (): void => {\n renewTimer = scheduler.setTimeout(\n () => {\n void publish(progress('in_progress')).catch(() => undefined);\n scheduleRenew();\n },\n adTtl * RENEW_FRACTION * 1000\n );\n };\n scheduleRenew();\n\n const abort = new AbortController();\n activeAborts.add(abort);\n run.abort = abort;\n let timedOut = false;\n const timeoutHandle = scheduler.setTimeout(() => {\n timedOut = true;\n abort.abort();\n }, timeoutMs);\n\n let result: RunnerRunResult;\n try {\n result = await Promise.race([\n runner.run({\n checkoutDir: run.checkout.dir,\n workflow: { path: workflow.path, sha256: workflow.sha256 },\n trigger,\n secrets: run.secrets,\n timeoutMs,\n signal: abort.signal,\n }),\n new Promise<RunnerRunResult>((resolve) => {\n abort.signal.addEventListener(\n 'abort',\n () => {\n // Give the runner a moment to report its own teardown; if it\n // does not, conclude on its behalf.\n scheduler.setTimeout(\n () =>\n resolve({\n conclusion: timedOut ? 'timed_out' : 'cancelled',\n jobs: [],\n startedAt,\n finishedAt: clock(),\n }),\n 1000\n );\n },\n { once: true }\n );\n }),\n ]);\n } catch (err) {\n log(\n `[ci] ${label}: runner failed: ${redactSecretValues(err instanceof Error ? err.message : String(err), Object.values(run.secrets))}`\n );\n result = {\n conclusion: 'startup_failure',\n jobs: [],\n startedAt,\n finishedAt: clock(),\n };\n } finally {\n scheduler.clearTimeout(timeoutHandle);\n scheduler.clearTimeout(renewTimer);\n activeAborts.delete(abort);\n }\n let conclusion = result.conclusion;\n if (timedOut) conclusion = 'timed_out';\n else if (run.superseded || (stopping && abort.signal.aborted))\n conclusion = 'cancelled';\n\n // Per job: upload the log (+ artifacts), publish 9841, renew 39842.\n // An injected value never leaves this process in the clear: not in the\n // uploaded log, not in the 9841 tail, not in an artifact, not in this\n // coordinator's own log (act masks its own output, but no Runner is\n // trusted to).\n const secretValues = Object.values(run.secrets);\n for (const job of result.jobs) {\n const jobLog = redactSecretValues(job.log, secretValues);\n const { tail, omitted } = logTail(jobLog);\n const logReceipt = await serial.run(() =>\n uploadBlob({\n body: Buffer.from(jobLog, 'utf-8'),\n contentType: 'text/plain; charset=utf-8',\n repoId: repo.repoId,\n })\n );\n const artifacts: { url: string; filename: string; name: string }[] = [];\n for (const artifact of job.artifacts) {\n try {\n const body = await readFile(artifact.path);\n if (containsSecretValue(body, secretValues)) {\n log(\n `[ci] ${label}: artifact ${redactSecretValues(artifact.filename, secretValues)} not uploaded — it contains an injected secret`\n );\n continue;\n }\n const receipt = await serial.run(() =>\n uploadBlob({\n body,\n contentType: contentTypeForPath(artifact.filename),\n repoId: repo.repoId,\n })\n );\n artifacts.push({\n url: gatewayRawUrl(opts.gatewayUrl, receipt.txId),\n filename: artifact.filename,\n name: artifact.name,\n });\n } catch (err) {\n log(\n `[ci] ${label}: artifact ${redactSecretValues(`${artifact.filename} skipped: ${err instanceof Error ? err.message : String(err)}`, secretValues)}`\n );\n }\n }\n const runsOn = workflow.jobs.find((j) => j.id === job.jobId)?.runsOn;\n const jobConclusion: CiConclusion =\n timedOut && job.conclusion === 'failure' ? 'timed_out' : job.conclusion;\n const jobEventId = await publish(\n buildCiJobResult(\n {\n trigger,\n progressAddress: progressAddress(run.runId),\n relayUrl,\n jobId: job.jobId,\n ...(job.name !== undefined ? { name: job.name } : {}),\n conclusion: jobConclusion,\n logsUrl: gatewayRawUrl(opts.gatewayUrl, logReceipt.txId),\n logTail: tail,\n logOmittedBytes: omitted,\n artifacts,\n queuedAt: run.queuedAt,\n startedAt: job.startedAt,\n ...(job.exitCode !== undefined ? { exitCode: job.exitCode } : {}),\n ...(runsOn ? { runsOn } : {}),\n },\n clock()\n )\n );\n quotes.push({\n eventId: jobEventId,\n relayUrl,\n pubkey: me,\n jobId: job.jobId,\n });\n await publish(progress('in_progress'));\n }\n\n const now = clock();\n const resultEventId = await publish(\n buildCiWorkflowResult(\n {\n trigger,\n runId: run.runId,\n conclusion,\n queuedAt: run.queuedAt,\n startedAt,\n provenance,\n jobs: quotes,\n },\n now\n )\n );\n await publish(progress('concluded', conclusion));\n log(`[ci] ${label}: ${conclusion}`);\n opts.onRunConcluded?.({\n runId: run.runId,\n repoAddr: repo.addr,\n trigger,\n conclusion,\n resultEventId,\n });\n };\n\n const pump = (): void => {\n while (active < concurrency && queue.length > 0 && !stopping) {\n const run = queue.shift() as QueuedRun;\n active += 1;\n inflight += 1;\n activeRuns.add(run);\n void executeRun(run)\n .catch((err) => {\n log(\n `[ci] run ${run.runId.slice(0, 8)} failed: ${err instanceof Error ? err.message : String(err)}`\n );\n })\n .finally(async () => {\n activeRuns.delete(run);\n active -= 1;\n inflight -= 1;\n await releaseCheckout(run.checkout);\n pump();\n checkIdle();\n });\n }\n };\n\n const enqueueRun = async (\n repo: RepoRuntime,\n trigger: CiTriggerContext,\n workflow: DiscoveredWorkflow,\n checkout: Checkout,\n secrets: Record<string, string>,\n manualProvenance?: CiProvenance\n ): Promise<void> => {\n // Money first (story 15): unreadable fee rates or an unaffordable run\n // means nothing is published for it.\n let rates: FeeRates;\n try {\n rates = await publisher.getFeeRates();\n } catch (err) {\n log(\n `[ci] refusing run of ${workflow.path} for ${repo.repoId}: cannot read fee rates ` +\n `(${err instanceof Error ? err.message : String(err)}) — is the wallet funded?`\n );\n return;\n }\n const jobs = Math.max(1, workflow.jobs.length);\n if (!(await canAfford({ events: 4 + 2 * jobs, uploads: jobs, rates }))) {\n log(\n `[ci] refusing run of ${workflow.path} for ${repo.repoId}: wallet cannot cover its writes`\n );\n return;\n }\n const run: QueuedRun = {\n repo,\n trigger,\n workflow,\n checkout,\n secrets,\n ...(manualProvenance ? { manualProvenance } : {}),\n runId: randomUUID(),\n queuedAt: clock(),\n };\n checkout.refs += 1;\n const position = active + queue.length + 1;\n const now = clock();\n await publish(\n buildCiWorkflowProgress(\n {\n trigger,\n runId: run.runId,\n status: 'queued',\n queue: Math.ceil(position / concurrency),\n queuedAt: run.queuedAt,\n ...(manualProvenance ? { provenance: manualProvenance } : {}),\n jobs: [],\n expiresAt: now + adTtl,\n },\n now\n )\n );\n queue.push(run);\n log(\n `[ci] queued ${workflow.path} for ${repo.repoId}@${trigger.commit.slice(0, 7)} [${run.runId.slice(0, 8)}]`\n );\n pump();\n };\n\n /**\n * A PR update supersedes the previous tip (#130): every run for the same\n * pull request that has not concluded is cancelled before the update's\n * own runs are queued. A still-queued run concludes `cancelled` from the\n * queue without reaching the Runner; an active one is aborted and its\n * Workflow Result + final progress marker say `cancelled`.\n */\n const supersedePrRuns = async (\n repo: RepoRuntime,\n prEventId: string,\n updateEventId: string\n ): Promise<void> => {\n const samePr = (run: QueuedRun): boolean =>\n run.repo === repo && run.trigger.pr?.prEventId === prEventId;\n const why = `superseded by PR update ${updateEventId.slice(0, 8)} — cancelled`;\n for (const run of queue.filter(samePr)) {\n queue.splice(queue.indexOf(run), 1);\n run.superseded = true;\n log(\n `[ci] ${repo.repoId} ${run.workflow.path}@${run.trigger.commit.slice(0, 7)} [${run.runId.slice(0, 8)}]: queued run ${why}`\n );\n await concludeWithoutJobs(run, 'cancelled', undefined);\n await releaseCheckout(run.checkout);\n }\n for (const run of activeRuns) {\n if (!samePr(run) || run.superseded) continue;\n run.superseded = true;\n log(\n `[ci] ${repo.repoId} ${run.workflow.path}@${run.trigger.commit.slice(0, 7)} [${run.runId.slice(0, 8)}]: ${why}`\n );\n run.abort?.abort();\n }\n };\n\n // ── Trigger handling: materialize → discover → enqueue ──────────────────\n let checkoutCounter = 0;\n\n const handleTrigger = async (t: PendingTrigger): Promise<void> => {\n const { repo } = t;\n inflight += 1;\n try {\n const dir = join(\n opts.workdir,\n `${repo.repoId}-${t.commit.slice(0, 7)}-${++checkoutCounter}`\n );\n let materialized;\n try {\n materialized = await materialize({\n ownerPubkey: repo.ownerPubkey,\n repoId: repo.repoId,\n relayUrls: [relayUrl],\n commit: t.commit,\n dir,\n ...(t.patch ? { patch: { content: t.patch.content } } : {}),\n ...(t.manual?.tagObjectIds && t.manual.tagObjectIds.length > 0\n ? { extraObjectIds: t.manual.tagObjectIds }\n : {}),\n ...(opts.webSocketFactory\n ? { webSocketFactory: opts.webSocketFactory }\n : {}),\n ...(opts.fetchFn ? { fetchFn: opts.fetchFn } : {}),\n ...(opts.resolveSha ? { resolveSha: opts.resolveSha } : {}),\n ...(opts.gateways ? { gateways: opts.gateways } : {}),\n });\n } catch (err) {\n const reason =\n err instanceof MaterializeError\n ? err.message\n : `materialize failed: ${err instanceof Error ? err.message : String(err)}`;\n log(`[ci] ${repo.repoId}@${t.commit.slice(0, 7)}: ${reason}`);\n if (t.manual) {\n // The workflow is known from the request, so the failure can be published.\n const trigger: CiTriggerContext = {\n repoAddr: repo.addr,\n commit: t.commit,\n workflow: t.manual.workflow,\n reason: 'manual',\n ...(t.ref !== undefined ? { ref: t.ref } : {}),\n ...(t.pr ? { pr: t.pr } : {}),\n };\n const checkout: Checkout = { dir, refs: 0 };\n const provenance: CiProvenance = {\n kind: 'manual-trigger',\n eventId: t.manual.eventId,\n relayUrl,\n pubkey: t.manual.pubkey,\n };\n const broken: DiscoveredWorkflow = {\n ...t.manual.workflow,\n triggers: {},\n jobs: [],\n parseError: reason,\n };\n await enqueueRun(repo, trigger, broken, checkout, {}, provenance);\n }\n return;\n }\n\n const checkout: Checkout = { dir: materialized.dir, refs: 0 };\n const commit = materialized.commit;\n\n // NIP-C1 Manual Trigger: \"A coordinator MUST resolve every supplied `c`\n // object and ignore the request unless all supplied object ids peel to\n // the same commit id.\" Extra `c` values are annotated tags; peel each\n // in the checkout and drop the request on any miss or mismatch.\n if (t.manual?.tagObjectIds && t.manual.tagObjectIds.length > 0) {\n const mismatch = await findUnpeelableTagId(\n materialized.dir,\n t.manual.tagObjectIds,\n commit\n );\n if (mismatch !== null) {\n log(\n `[ci] manual trigger ignored: c ${mismatch.slice(0, 7)} does not peel to ${commit.slice(0, 7)} (or is not in the repo)`\n );\n if (!opts.keepCheckouts)\n await rm(materialized.dir, { recursive: true, force: true }).catch(\n () => undefined\n );\n return;\n }\n }\n\n const workflows = await discoverWorkflows(materialized.dir);\n const tagObjectIds = [\n ...(t.manual?.tagObjectIds ?? []),\n ...(commit !== t.commit && !t.patch ? [t.commit] : []),\n ...(t.patch?.declaredTip && t.patch.declaredTip !== commit\n ? [t.patch.declaredTip]\n : []),\n ];\n const secretsAllowed = repo.authorized.has(t.authorPubkey.toLowerCase());\n const secrets = secretsAllowed ? effectiveSecrets(repo.secrets) : {};\n\n let selected: DiscoveredWorkflow[];\n let manualProvenance: CiProvenance | undefined;\n if (t.manual) {\n const wanted = t.manual.workflow;\n const wf = workflows.find((w) => w.path === wanted.path);\n if (!wf) {\n log(\n `[ci] manual trigger ignored: ${wanted.path} does not exist at ${commit.slice(0, 7)}`\n );\n selected = [];\n } else if (wf.sha256 !== wanted.sha256.toLowerCase()) {\n log(\n `[ci] manual trigger ignored: ${wanted.path} at ${commit.slice(0, 7)} has sha256 ${wf.sha256}, request named ${wanted.sha256}`\n );\n selected = [];\n } else {\n selected = [wf];\n }\n manualProvenance = {\n kind: 'manual-trigger',\n eventId: t.manual.eventId,\n relayUrl,\n pubkey: t.manual.pubkey,\n };\n } else if (t.reason === 'push') {\n selected = workflows.filter(\n (wf) =>\n wf.parseError !== undefined || matchesPush(wf, t.ref as string)\n );\n } else {\n const base = repo.defaultBranch ?? undefined;\n selected = workflows.filter(\n (wf) => wf.parseError !== undefined || matchesPullRequest(wf, base)\n );\n }\n\n if (selected.length === 0) {\n if (!opts.keepCheckouts)\n await rm(materialized.dir, { recursive: true, force: true }).catch(\n () => undefined\n );\n return;\n }\n for (const wf of selected) {\n // Only the served perspective is published as `a`. NIP-C1 lets a\n // Manual Trigger name other maintainers' announcements, but \"a\n // coordinator MUST resolve every `a` tag independently and MUST NOT\n // run a workflow for a repository merely because it was named\": rig\n // serves one perspective per repo and verifies only that one, so it\n // does not republish the others as if it had.\n const trigger: CiTriggerContext = {\n repoAddr: repo.addr,\n commit,\n ...(tagObjectIds.length > 0 ? { tagObjectIds } : {}),\n workflow: { path: wf.path, sha256: wf.sha256 },\n reason: t.reason,\n ...(t.pr ? { pr: t.pr } : t.ref !== undefined ? { ref: t.ref } : {}),\n };\n await enqueueRun(\n repo,\n trigger,\n wf,\n checkout,\n secrets,\n manualProvenance\n );\n }\n if (checkout.refs === 0 && !opts.keepCheckouts) {\n await rm(materialized.dir, { recursive: true, force: true }).catch(\n () => undefined\n );\n }\n } finally {\n inflight -= 1;\n checkIdle();\n }\n };\n\n // ── Event handling (serialized) ──────────────────────────────────────────\n const eventQueue: NostrEvent[] = [];\n let draining = false;\n\n const markRepoProcessed = (repo: RepoRuntime, ev: NostrEvent): void => {\n const cursor = repo.cursor as RepoCursor;\n cursor.processed.push(ev.id);\n if (ev.created_at > cursor.lastCreatedAt) {\n cursor.lastCreatedAt = ev.created_at;\n cursor.lastEventId = ev.id;\n }\n };\n const repoSeen = (repo: RepoRuntime, ev: NostrEvent): boolean =>\n (repo.cursor?.processed ?? []).includes(ev.id);\n\n const handleRepoState = async (\n repo: RepoRuntime,\n ev: NostrEvent\n ): Promise<void> => {\n const cursor = repo.cursor as RepoCursor;\n if (repoSeen(repo, ev) || ev.created_at < cursor.lastCreatedAt) return;\n const { refs, head } = parseRefsTags(ev);\n if (head) repo.defaultBranch = head;\n const moved = movedRefs(cursor.refs, refs);\n cursor.refs = refs;\n markRepoProcessed(repo, ev);\n await persistCursor();\n if (moved.length === 0) return;\n if (!isServing(repo)) {\n log(\n `[ci] ${repo.repoId}: push seen but no maintainer Service Request — not running`\n );\n return;\n }\n for (const { ref, sha } of moved) {\n if (!ref.startsWith('refs/heads/') && !ref.startsWith('refs/tags/'))\n continue;\n await handleTrigger({\n repo,\n reason: 'push',\n authorPubkey: ev.pubkey,\n commit: sha,\n ref,\n });\n }\n };\n\n const handlePullRequest = async (\n repo: RepoRuntime,\n ev: NostrEvent\n ): Promise<void> => {\n if (repoSeen(repo, ev)) return;\n if (ev.kind === 1617 || ev.kind === 1618) repo.prEvents.set(ev.id, ev);\n markRepoProcessed(repo, ev);\n await persistCursor();\n if (!isServing(repo)) {\n log(\n `[ci] ${repo.repoId}: pull request seen but no maintainer Service Request — not running`\n );\n return;\n }\n const author = ev.pubkey.toLowerCase();\n if (ev.kind === 1617) {\n const parents = tagValues(ev.tags, 'parent-commit');\n const commits = tagValues(ev.tags, 'commit');\n const base = parents[0];\n if (!base || ev.content.trim() === '') {\n log(\n `[ci] ${repo.repoId}: patch ${ev.id.slice(0, 8)} has no parent-commit/content — cannot run`\n );\n return;\n }\n const declaredTip = commits[commits.length - 1];\n await handleTrigger({\n repo,\n reason: 'pull_request',\n authorPubkey: author,\n commit: base,\n patch: { content: ev.content, ...(declaredTip ? { declaredTip } : {}) },\n pr: {\n prEventId: ev.id,\n prAuthor: author,\n prKind: 1617,\n sourceEventId: ev.id,\n sourceAuthor: author,\n sourceKind: 1617,\n },\n });\n return;\n }\n if (ev.kind === 1618) {\n const tip = tagValues(ev.tags, 'c')[0];\n if (!tip) {\n log(\n `[ci] ${repo.repoId}: pull request ${ev.id.slice(0, 8)} has no tip commit — cannot run`\n );\n return;\n }\n await handleTrigger({\n repo,\n reason: 'pull_request',\n authorPubkey: author,\n commit: tip,\n pr: {\n prEventId: ev.id,\n prAuthor: author,\n prKind: 1618,\n sourceEventId: ev.id,\n sourceAuthor: author,\n sourceKind: 1618,\n },\n });\n return;\n }\n // 1619: PR update — the PR it moves must be known.\n const prId = tagValues(ev.tags, 'E')[0];\n const pr = prId ? repo.prEvents.get(prId) : undefined;\n const tip = tagValues(ev.tags, 'c')[0];\n if (!pr || !tip) {\n log(\n `[ci] ${repo.repoId}: PR update ${ev.id.slice(0, 8)} references an unknown PR or no commit — ignored`\n );\n return;\n }\n await supersedePrRuns(repo, pr.id, ev.id);\n await handleTrigger({\n repo,\n reason: 'pull_request',\n authorPubkey: author,\n commit: tip,\n pr: {\n prEventId: pr.id,\n prAuthor: pr.pubkey.toLowerCase(),\n prKind: pr.kind === 1617 ? 1617 : 1618,\n sourceEventId: ev.id,\n sourceAuthor: author,\n sourceKind: 1619,\n },\n });\n };\n\n const handleManualTrigger = async (ev: NostrEvent): Promise<void> => {\n if (inboxProcessed.has(ev.id)) return;\n inboxProcessed.add(ev.id);\n inbox.lastCreatedAt = Math.max(inbox.lastCreatedAt ?? 0, ev.created_at);\n await persistCursor();\n const manual = parseCiManualTrigger(ev, me);\n if (!manual) {\n log(`[ci] malformed manual trigger ${ev.id.slice(0, 8)} ignored`);\n return;\n }\n const repo = repos.get(manual.trigger.repoAddr);\n if (!repo) {\n log(\n `[ci] manual trigger for unserved repo ${manual.trigger.repoAddr} ignored`\n );\n return;\n }\n if (!repo.authorized.has(manual.pubkey)) {\n log(\n `[ci] manual trigger from non-maintainer ${manual.pubkey.slice(0, 8)} ignored`\n );\n return;\n }\n const t = manual.trigger;\n await handleTrigger({\n repo,\n reason: 'manual',\n authorPubkey: manual.pubkey,\n commit: t.commit,\n ...(t.ref !== undefined ? { ref: t.ref } : {}),\n ...(t.pr ? { pr: t.pr } : {}),\n manual: {\n eventId: manual.eventId,\n pubkey: manual.pubkey,\n workflow: t.workflow,\n ...(t.tagObjectIds ? { tagObjectIds: t.tagObjectIds } : {}),\n },\n });\n };\n\n const handleSecretUpdate = async (ev: NostrEvent): Promise<void> => {\n if (inboxProcessed.has(ev.id)) return;\n inboxProcessed.add(ev.id);\n inbox.lastCreatedAt = Math.max(inbox.lastCreatedAt ?? 0, ev.created_at);\n await persistCursor();\n const update = parseCiSecretUpdate(ev);\n if (!update || update.coordinatorPubkey !== me) return;\n // NIP-C1: the `a` coordinate is the MAINTAINER's own repository perspective\n // (`30617:<maintainer>:<repo-id>`, signer == its pubkey). rig serves one\n // perspective per repo id, so the update lands on the served repo with\n // that id — provided the signer is one of its maintainers.\n const perspective = parseRepoAddress(update.repoAddr);\n const repo = perspective\n ? [...repos.values()].find((r) => r.repoId === perspective.repoId)\n : undefined;\n if (!repo) {\n log(`[ci] secret update for unserved repo ${update.repoAddr} ignored`);\n return;\n }\n if (!repo.authorized.has(update.pubkey)) {\n log(\n `[ci] ${repo.repoId}: secret update from non-maintainer ${update.pubkey.slice(0, 8)} ignored`\n );\n return;\n }\n const ad = liveAdvertisements.get(update.advertisementId);\n if (!ad || update.recipientPubkey !== secretsKey.pubkey) {\n log(\n `[ci] ${repo.repoId}: secret update ${ev.id.slice(0, 8)} addressed to a stale secrets key — re-send it`\n );\n return;\n }\n if (update.createdAt < ad.createdAt || update.createdAt >= ad.expiresAt) {\n log(\n `[ci] ${repo.repoId}: secret update ${ev.id.slice(0, 8)} outside its advertisement window — ignored`\n );\n return;\n }\n try {\n const plain = decryptSecretUpdate(\n update.ciphertext,\n secretsKey.secretKey,\n update.senderPubkey\n );\n validateSecretUpdate(plain, {\n pubkey: ev.pubkey,\n created_at: ev.created_at,\n });\n repo.secrets = applySecretUpdate(repo.secrets, plain, ev.id);\n await persistSecrets();\n const names = [...Object.keys(plain.set), ...plain.remove];\n log(\n `[ci] ${repo.repoId}: secrets updated by ${update.pubkey.slice(0, 8)} (${names.join(', ')})`\n );\n } catch (err) {\n log(\n `[ci] ${repo.repoId}: secret update ${ev.id.slice(0, 8)} rejected: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n };\n\n const handleEvent = async (ev: NostrEvent): Promise<void> => {\n switch (ev.kind) {\n case REPOSITORY_ANNOUNCEMENT_KIND: {\n const repoId = tagValues(ev.tags, 'd')[0];\n const repo = repoId\n ? repos.get(repoAddress(ev.pubkey, repoId))\n : undefined;\n if (repo && ev.pubkey.toLowerCase() === repo.ownerPubkey) {\n repo.authorized = authorizedStatusAuthors(repo.ownerPubkey, ev.tags);\n }\n return;\n }\n case REPOSITORY_STATE_KIND: {\n const repoId = tagValues(ev.tags, 'd')[0];\n const repo = repoId\n ? repos.get(repoAddress(ev.pubkey, repoId))\n : undefined;\n if (repo && ev.pubkey.toLowerCase() === repo.ownerPubkey)\n await handleRepoState(repo, ev);\n return;\n }\n case 1617:\n case 1618:\n case 1619: {\n for (const addr of tagValues(ev.tags, 'a')) {\n const repo = repos.get(addr);\n if (repo) {\n await handlePullRequest(repo, ev);\n return;\n }\n }\n return;\n }\n case CI_SERVICE_REQUEST_KIND:\n case CI_SERVICE_STOP_KIND:\n handleControl(ev);\n return;\n case CI_MANUAL_TRIGGER_KIND:\n await handleManualTrigger(ev);\n return;\n case CI_SECRET_UPDATE_KIND:\n await handleSecretUpdate(ev);\n return;\n default:\n return;\n }\n };\n\n const drain = async (): Promise<void> => {\n if (draining) return;\n draining = true;\n try {\n while (eventQueue.length > 0) {\n const ev = eventQueue.shift() as NostrEvent;\n try {\n await handleEvent(ev);\n } catch (err) {\n log(\n `[ci] event ${ev.id.slice(0, 8)} (kind ${ev.kind}) failed: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n }\n } finally {\n draining = false;\n checkIdle();\n }\n };\n\n // ── Subscriptions ────────────────────────────────────────────────────────\n const filters: NostrFilter[] = [controlFilter];\n const sinceFor: ((() => number | undefined) | null)[] = [null];\n for (const repo of repos.values()) {\n filters.push({\n kinds: [REPOSITORY_ANNOUNCEMENT_KIND, REPOSITORY_STATE_KIND],\n authors: [repo.ownerPubkey],\n '#d': [repo.repoId],\n });\n sinceFor.push(null);\n filters.push({ kinds: [1617, 1618, 1619], '#a': [repo.addr] });\n sinceFor.push(() => repo.cursor?.lastCreatedAt);\n }\n filters.push({\n kinds: [CI_MANUAL_TRIGGER_KIND, CI_SECRET_UPDATE_KIND],\n '#p': [me],\n });\n sinceFor.push(() => inbox.lastCreatedAt);\n\n const subscription: RelaySubscription = subscribeRelay({\n url: relayUrl,\n filters,\n webSocketFactory: opts.webSocketFactory ?? defaultWebSocketFactory,\n scheduler,\n onEvent: (ev) => {\n eventQueue.push(ev);\n void drain();\n },\n onStatus: (status, detail) => {\n if (status === 'open') log(`[ci] connected to ${relayUrl}`);\n else if (status === 'reconnecting')\n log(`[ci] relay connection lost (${detail ?? ''}) — reconnecting`);\n },\n since: (index) => sinceFor[index]?.() ?? undefined,\n });\n\n // ── Stop ─────────────────────────────────────────────────────────────────\n let stopResolve: () => void = () => undefined;\n const stopped = new Promise<void>((resolve) => {\n stopResolve = resolve;\n });\n let stopPromise: Promise<void> | null = null;\n\n const stop = (): Promise<void> => {\n if (stopPromise) return stopPromise;\n stopPromise = (async () => {\n stopping = true;\n subscription.close();\n if (adTimer !== null) scheduler.clearTimeout(adTimer);\n const dropped = queue.splice(0);\n for (const run of dropped) {\n log(\n `[ci] dropping queued run ${run.runId.slice(0, 8)} (its queued marker expires on its own)`\n );\n await releaseCheckout(run.checkout);\n }\n for (const abort of activeAborts) abort.abort();\n await new Promise<void>((resolve) => {\n if (inflight === 0 && !draining) return resolve();\n idleWaiters.push(resolve);\n });\n await persistCursor();\n stopResolve();\n })();\n return stopPromise;\n };\n\n return {\n pubkey: me,\n get secretsKeyPubkey() {\n return secretsKey.pubkey;\n },\n get advertisementId() {\n return advertisementId;\n },\n stopped,\n stop,\n idle: () =>\n new Promise<void>((resolve) => {\n if (\n inflight === 0 &&\n queue.length === 0 &&\n eventQueue.length === 0 &&\n !draining\n )\n return resolve();\n idleWaiters.push(resolve);\n }),\n serving: () =>\n [...repos.values()].map((repo) => ({\n repoAddr: repo.addr,\n serving: isServing(repo),\n })),\n };\n}\n\n/**\n * Peel every `c` tag id a Manual Trigger supplied and return the first one\n * that is missing from the checkout or peels to a different commit than the\n * run's, or null when they all agree (NIP-C1 Manual Trigger rule).\n */\nasync function findUnpeelableTagId(\n checkoutDir: string,\n tagObjectIds: readonly string[],\n commit: string\n): Promise<string | null> {\n for (const id of tagObjectIds) {\n if (!/^[0-9a-f]{40}$/i.test(id)) return id;\n let peeled: string;\n try {\n peeled = (\n await runGit(checkoutDir, [\n 'rev-parse',\n '--verify',\n '--quiet',\n `${id}^{commit}`,\n ])\n ).trim();\n } catch {\n return id;\n }\n if (peeled.toLowerCase() !== commit.toLowerCase()) return id;\n }\n return null;\n}\n","/**\n * Materialize ONE commit into a working directory for a CI run (#125), using\n * rig's free read path — the coordinator, not the runner, owns repo\n * knowledge, so `act`'s local checkout handling never needs GitHub or any git\n * server (user story 23).\n *\n * Pipeline (the same engine `rig clone` uses, ../cli/clone.ts): relay state\n * (kind:30617/30618 → sha→txId hints) → download + SHA-1-verify the object\n * closure of the commit from Arweave gateways (../read-pipeline.ts) → write\n * the objects through git plumbing into a fresh repository\n * (../materialize.ts) → detached checkout of the commit.\n *\n * The kind:1617 patch path: `rig pr create` publishes patches whose content\n * is real `git format-patch` output against a `parent-commit`. For such a PR\n * the base commit is materialized and the patch applied with `git am`; the\n * resulting HEAD is the commit the run is for (its sha differs from the\n * contributor's local sha because the committer differs — the coordinator\n * publishes both, see ./coordinator.ts).\n *\n * Everything shells out with argument ARRAYS (never a shell); refnames never\n * reach git here at all (a detached checkout by full sha).\n */\n\nimport { spawn } from 'node:child_process';\nimport { mkdir } from 'node:fs/promises';\nimport type { FetchLike } from '../object-fetch.js';\nimport { runGit, writeGitObjects } from '../materialize.js';\nimport { collectRepoObjects, missingObjectsMessage } from '../read-pipeline.js';\nimport { fetchRemoteState, type WebSocketFactory } from '../remote-state.js';\n\nconst FULL_SHA_RE = /^[0-9a-f]{40}$/;\n\n/** The commit could not be obtained — the run concludes `startup_failure`. */\nexport class MaterializeError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'MaterializeError';\n }\n}\n\nexport interface MaterializeCommitOptions {\n ownerPubkey: string;\n repoId: string;\n relayUrls: string[];\n /** The commit to check out (full sha). With `patch`, the patch's base. */\n commit: string;\n /** Destination directory (created; must not already be a repository). */\n dir: string;\n /** kind:1617 path: apply this format-patch text on top of `commit`. */\n patch?: { content: string };\n /**\n * Extra object ids to fetch alongside the commit's closure — the annotated\n * tag objects a Manual Trigger names as additional `c` values, so the\n * coordinator can peel them in the checkout. A missing one is NOT fatal\n * here (the caller decides what an unpeelable id means).\n */\n extraObjectIds?: readonly string[];\n webSocketFactory?: WebSocketFactory;\n fetchFn?: FetchLike;\n resolveSha?: (sha: string, repo: string) => Promise<string | null>;\n gateways?: readonly string[];\n /** Committer identity for `git am` (default: the coordinator's). */\n committer?: { name: string; email: string };\n}\n\nexport interface MaterializedCommit {\n dir: string;\n /** HEAD after checkout: `commit`, an annotated tag's peeled commit, or the `git am` result. */\n commit: string;\n /** Objects written into the checkout's object store. */\n objectsWritten: number;\n}\n\nexport type MaterializeCommit = (\n opts: MaterializeCommitOptions\n) => Promise<MaterializedCommit>;\n\n/** Run git with argument-array safety and a stdin payload. */\nfunction gitWithStdin(\n cwd: string,\n args: string[],\n stdin: string,\n env: NodeJS.ProcessEnv\n): Promise<string> {\n return new Promise((resolve, reject) => {\n const child = spawn('git', args, {\n cwd,\n env,\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n let stdout = '';\n let stderr = '';\n child.stdout.on('data', (chunk: Buffer) => {\n stdout += chunk.toString('utf-8');\n });\n child.stderr.on('data', (chunk: Buffer) => {\n stderr += chunk.toString('utf-8');\n });\n child.on('error', (err) =>\n reject(new Error(`failed to spawn git: ${err.message}`))\n );\n child.on('close', (code) => {\n if (code !== 0) {\n reject(\n new Error(`git ${args[0]} failed (exit ${code}): ${stderr.trim()}`)\n );\n } else {\n resolve(stdout);\n }\n });\n child.stdin.on('error', () => {\n // 'close' surfaces the failure\n });\n child.stdin.write(stdin);\n child.stdin.end();\n });\n}\n\nexport const materializeCommit: MaterializeCommit = async (opts) => {\n if (!FULL_SHA_RE.test(opts.commit)) {\n throw new MaterializeError(\n `not a full commit sha: ${JSON.stringify(opts.commit)}`\n );\n }\n\n // ── Remote state (kind:30617 + 30618) — free ────────────────────────────\n const remote = await fetchRemoteState({\n relayUrls: opts.relayUrls,\n ownerPubkey: opts.ownerPubkey,\n repoId: opts.repoId,\n ...(opts.webSocketFactory\n ? { webSocketFactory: opts.webSocketFactory }\n : {}),\n ...(opts.resolveSha ? { resolveSha: opts.resolveSha } : {}),\n });\n if (!remote.announced && remote.refsEvent === null) {\n throw new MaterializeError(\n `repository ${opts.ownerPubkey.slice(0, 8)}…/${opts.repoId} is not on the relay`\n );\n }\n\n // ── Download + verify the commit's closure — free ───────────────────────\n const collected = await collectRepoObjects({\n tips: [opts.commit, ...(opts.extraObjectIds ?? [])],\n shaToTxId: remote.shaToTxId,\n resolveMissing: (shas) => remote.resolveMissing(shas),\n ...(opts.fetchFn ? { fetchFn: opts.fetchFn } : {}),\n ...(opts.gateways ? { gateways: opts.gateways } : {}),\n });\n const extra = new Set(\n (opts.extraObjectIds ?? []).map((id) => id.toLowerCase())\n );\n const fatal = collected.missing.filter(\n (m) => !extra.has(m.sha.toLowerCase())\n );\n if (fatal.length > 0) {\n throw new MaterializeError(\n missingObjectsMessage(\n fatal,\n `cannot materialize ${opts.commit.slice(0, 7)}`\n )\n );\n }\n\n // ── Fresh repository + detached checkout ────────────────────────────────\n await mkdir(opts.dir, { recursive: true });\n await runGit(opts.dir, ['init', '--quiet', '--initial-branch=main']);\n const objectsWritten = await writeGitObjects(\n opts.dir,\n collected.objects.values()\n );\n await runGit(opts.dir, ['checkout', '--quiet', '--detach', opts.commit]);\n\n if (opts.patch) {\n const committer = opts.committer ?? {\n name: 'rig ci',\n email: 'ci@rig.invalid',\n };\n const env: NodeJS.ProcessEnv = {\n ...process.env,\n GIT_COMMITTER_NAME: committer.name,\n GIT_COMMITTER_EMAIL: committer.email,\n };\n try {\n await gitWithStdin(opts.dir, ['am', '--quiet'], opts.patch.content, env);\n } catch (err) {\n throw new MaterializeError(\n `patch does not apply on ${opts.commit.slice(0, 7)}: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n }\n // HEAD is the peeled commit: `commit` itself, the annotated tag's target\n // when a tag object was requested, or the `git am` result.\n const head = (await runGit(opts.dir, ['rev-parse', 'HEAD'])).trim();\n\n return { dir: opts.dir, commit: head, objectsWritten };\n};\n","/**\n * A long-lived NIP-01 subscription (#125): the coordinator's ears.\n *\n * `../remote-state.ts`'s `queryRelay` is a one-shot REQ → EVENT* → EOSE →\n * CLOSE read, which is exactly right for every free read rig does — and\n * exactly wrong for a coordinator that must hear a push the moment it lands.\n * This module keeps ONE socket open per relay with one REQ per filter, keeps\n * delivering EVENTs after EOSE, and reconnects with exponential backoff when\n * the relay drops the connection. On every (re)connect the REQs are re-sent\n * with `since` taken from the caller's cursor getter, so a blip replays only\n * what was missed (user story 13).\n *\n * Built on the same `WebSocketFactory` seam as the rest of rig, so tests\n * drive it with an in-process scripted relay (./ci-testkit.ts) and the\n * reconnect timers go through an injectable scheduler (no real sleeps).\n * Relay payload decoding tolerates the same three encodings queryRelay does\n * (object / double-JSON / TOON); the helper is private there, so it is\n * mirrored here rather than exported from a module this slice does not own.\n */\n\nimport { decode as decodeToon } from '@toon-format/toon';\nimport type {\n NostrEvent,\n NostrFilter,\n WebSocketFactory,\n WebSocketLike,\n} from '../remote-state.js';\n\n/** Timer seam so the coordinator and its tests share one clock. */\nexport interface Scheduler {\n setTimeout(fn: () => void, ms: number): unknown;\n clearTimeout(handle: unknown): void;\n}\n\nexport const realScheduler: Scheduler = {\n setTimeout: (fn, ms) => setTimeout(fn, ms),\n clearTimeout: (handle) =>\n clearTimeout(handle as ReturnType<typeof setTimeout>),\n};\n\nexport type SubscriptionStatus = 'open' | 'closed' | 'reconnecting' | 'error';\n\nexport interface SubscribeRelayOptions {\n url: string;\n /** One REQ per filter; `onEvent` reports which one matched by index. */\n filters: NostrFilter[];\n webSocketFactory: WebSocketFactory;\n onEvent(event: NostrEvent, filterIndex: number): void;\n /** Called once per filter per (re)connect when the relay signals EOSE. */\n onEose?(filterIndex: number): void;\n onStatus?(status: SubscriptionStatus, detail?: string): void;\n /**\n * Cursor getter, evaluated at every (re)connect: when it returns a number\n * every filter is re-sent with that `since` (a filter's own `since` wins\n * when set). Return undefined for a full replay.\n */\n since?: (filterIndex: number) => number | undefined;\n backoff?: { initialMs?: number; maxMs?: number };\n scheduler?: Scheduler;\n}\n\nexport interface RelaySubscription {\n /** Stop for good: no reconnect, socket closed, no further callbacks. */\n close(): void;\n readonly connected: boolean;\n /** How many times the subscription has (re)connected so far. */\n readonly connections: number;\n}\n\nconst WS_OPEN = 1;\n\n/** Mirror of remote-state.ts's tolerant EVENT payload decoder. */\nexport function decodeRelayEvent(payload: unknown): NostrEvent | null {\n if (payload !== null && typeof payload === 'object')\n return payload as NostrEvent;\n if (typeof payload !== 'string') return null;\n try {\n const parsed: unknown = JSON.parse(payload);\n if (parsed !== null && typeof parsed === 'object')\n return parsed as NostrEvent;\n } catch {\n // Not JSON — fall through to TOON\n }\n try {\n return decodeToon(payload) as unknown as NostrEvent;\n } catch {\n return null;\n }\n}\n\n/** Open a persistent subscription. Never throws synchronously. */\nexport function subscribeRelay(opts: SubscribeRelayOptions): RelaySubscription {\n const scheduler = opts.scheduler ?? realScheduler;\n const initialBackoff = opts.backoff?.initialMs ?? 1000;\n const maxBackoff = opts.backoff?.maxMs ?? 60_000;\n const base = `ci-${Math.random().toString(36).slice(2, 8)}`;\n\n let ws: WebSocketLike | null = null;\n let closed = false;\n let connected = false;\n let connections = 0;\n let backoffMs = initialBackoff;\n let reconnectHandle: unknown = null;\n /** subId → filter index for the CURRENT socket generation. */\n let subIds = new Map<string, number>();\n\n const status = (s: SubscriptionStatus, detail?: string): void => {\n opts.onStatus?.(s, detail);\n };\n\n const scheduleReconnect = (detail: string): void => {\n if (closed || reconnectHandle !== null) return;\n connected = false;\n status('reconnecting', detail);\n const delay = backoffMs;\n backoffMs = Math.min(backoffMs * 2, maxBackoff);\n reconnectHandle = scheduler.setTimeout(() => {\n reconnectHandle = null;\n connect();\n }, delay);\n };\n\n const connect = (): void => {\n if (closed) return;\n const generation = ++connections;\n subIds = new Map();\n let socket: WebSocketLike;\n try {\n socket = opts.webSocketFactory(opts.url);\n } catch (err) {\n scheduleReconnect(\n `connect failed: ${err instanceof Error ? err.message : String(err)}`\n );\n return;\n }\n ws = socket;\n const mine = (): boolean => ws === socket && generation === connections;\n\n socket.addEventListener('open', () => {\n if (!mine() || closed) return;\n connected = true;\n backoffMs = initialBackoff;\n status('open');\n opts.filters.forEach((filter, index) => {\n const subId = `${base}-${generation}-${index}`;\n subIds.set(subId, index);\n const since = filter.since ?? opts.since?.(index);\n const sent: NostrFilter =\n since !== undefined ? { ...filter, since } : { ...filter };\n socket.send(JSON.stringify(['REQ', subId, sent]));\n });\n });\n\n socket.addEventListener('message', (msgEvent: { data?: unknown }) => {\n if (!mine() || closed) return;\n let msg: unknown[];\n try {\n msg = JSON.parse(String(msgEvent.data)) as unknown[];\n } catch {\n return;\n }\n if (!Array.isArray(msg) || msg.length < 2) return;\n const index = subIds.get(String(msg[1]));\n if (index === undefined) return;\n if (msg[0] === 'EVENT' && msg[2] !== undefined) {\n const event = decodeRelayEvent(msg[2]);\n if (event) opts.onEvent(event, index);\n } else if (msg[0] === 'EOSE') {\n opts.onEose?.(index);\n } else if (msg[0] === 'CLOSED') {\n // The relay refused/ended this REQ; treat like a dropped socket.\n scheduleReconnect(`relay closed subscription: ${String(msg[2] ?? '')}`);\n }\n });\n\n socket.addEventListener('error', (event: { message?: unknown }) => {\n if (!mine() || closed) return;\n const detail =\n typeof event === 'object' && event !== null && 'message' in event\n ? String(event.message)\n : 'unknown';\n status('error', detail);\n try {\n socket.close();\n } catch {\n // already closing\n }\n scheduleReconnect(`socket error: ${detail}`);\n });\n\n socket.addEventListener('close', () => {\n if (!mine() || closed) return;\n scheduleReconnect('socket closed');\n });\n };\n\n connect();\n\n return {\n close() {\n if (closed) return;\n closed = true;\n connected = false;\n if (reconnectHandle !== null) {\n scheduler.clearTimeout(reconnectHandle);\n reconnectHandle = null;\n }\n const socket = ws;\n ws = null;\n if (socket) {\n try {\n if (socket.readyState === WS_OPEN) {\n for (const subId of subIds.keys()) {\n socket.send(JSON.stringify(['CLOSE', subId]));\n }\n }\n socket.close();\n } catch {\n // ignore close errors\n }\n }\n status('closed');\n },\n get connected() {\n return connected;\n },\n get connections() {\n return connections;\n },\n };\n}\n","/**\n * Coordinator state on disk (#125): what `rig ci serve` must remember across\n * restarts so a relay blip or a process restart never loses or repeats a run.\n *\n * <stateDir>/cursor.json per served repo: the last processed event, the\n * ref map the last 30618 left behind (push\n * triggers are diffs against it), and a bounded\n * list of already-processed trigger ids; plus the\n * coordinator inbox's own cursor (9840 / 29846).\n * <stateDir>/secrets.json per served repo: the accepted secret inventory\n * (../ci/secrets.ts's SecretInventory).\n *\n * Secret VALUES are stored in plaintext here, under the coordinator's own\n * config directory — the #125 spec allows exactly that (\"keeps the decrypted\n * inventory in memory and on disk under its config directory\"). What is\n * NEVER persisted is the NIP-44 recipient key (the advertised `secrets-key`):\n * it lives only in the running process and is regenerated at every start, as\n * NIP-C1 requires, so an update encrypted to a previous process cannot be\n * opened by this one — maintainers re-send after a restart, the inventory\n * already accepted survives.\n *\n * Files are written atomically (temp file + rename) so a crash mid-write\n * leaves the previous state intact. `stateDir` defaults to\n * `<TOON_CLIENT_HOME|~/.toon-client>/rig-ci/<coordinator-pubkey>`.\n */\n\nimport { mkdir, readFile, rename, writeFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport type { SecretInventory } from './secrets.js';\n\n/** Where one served repo's trigger processing stands. */\nexport interface RepoCursor {\n /** `created_at` of the newest trigger event processed for this repo. */\n lastCreatedAt: number;\n /** Id of that event (tie-breaker / audit). */\n lastEventId: string;\n /** Ref map (`refname → sha`) of the last kind:30618 seen — push diffs use it. */\n refs: Record<string, string>;\n /** Ids of trigger events already handled (bounded, newest last). */\n processed: string[];\n}\n\n/** The coordinator inbox (`#p` = coordinator) cursor: 9840 + 29846. */\nexport interface InboxCursor {\n lastCreatedAt?: number;\n processed: string[];\n}\n\n/** Repo address → its cursor. */\nexport type CoordinatorCursor = Record<string, RepoCursor>;\n\n/** Repo address → its accepted secret inventory. */\nexport type CoordinatorSecrets = Record<string, SecretInventory>;\n\nexport interface CoordinatorState {\n cursor: CoordinatorCursor;\n inbox: InboxCursor;\n secrets: CoordinatorSecrets;\n}\n\n/** Processed-id lists are capped at this many entries (newest kept). */\nexport const MAX_PROCESSED_IDS = 1000;\n\nexport function cursorPath(stateDir: string): string {\n return join(stateDir, 'cursor.json');\n}\n\nexport function secretsPath(stateDir: string): string {\n return join(stateDir, 'secrets.json');\n}\n\n/** `<TOON_CLIENT_HOME|~/.toon-client>/rig-ci/<coordinator-pubkey>` */\nexport function defaultCoordinatorStateDir(\n env: NodeJS.ProcessEnv,\n coordinatorPubkey: string\n): string {\n const home = env['TOON_CLIENT_HOME'] ?? join(homedir(), '.toon-client');\n return join(home, 'rig-ci', coordinatorPubkey.toLowerCase());\n}\n\n/** Keep the newest {@link MAX_PROCESSED_IDS} ids. */\nexport function boundProcessed(ids: string[]): string[] {\n return ids.length > MAX_PROCESSED_IDS\n ? ids.slice(ids.length - MAX_PROCESSED_IDS)\n : ids;\n}\n\ninterface CursorFile {\n repos: CoordinatorCursor;\n inbox: InboxCursor;\n}\n\nasync function readJson<T>(path: string): Promise<T | undefined> {\n let text: string;\n try {\n text = await readFile(path, 'utf-8');\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return undefined;\n throw err;\n }\n try {\n return JSON.parse(text) as T;\n } catch (err) {\n throw new Error(\n `coordinator state file ${path} is not valid JSON: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n}\n\nasync function writeJsonAtomic(path: string, value: unknown): Promise<void> {\n await mkdir(join(path, '..'), { recursive: true });\n const tmp = `${path}.tmp-${process.pid}-${Math.random().toString(36).slice(2, 8)}`;\n await writeFile(tmp, JSON.stringify(value, null, 2) + '\\n', 'utf-8');\n await rename(tmp, path);\n}\n\n/** Load both files; a missing file (or directory) is an empty state. */\nexport async function loadCoordinatorState(\n stateDir: string\n): Promise<CoordinatorState> {\n const cursorFile = await readJson<CursorFile>(cursorPath(stateDir));\n const secrets =\n (await readJson<CoordinatorSecrets>(secretsPath(stateDir))) ?? {};\n const cursor: CoordinatorCursor = {};\n for (const [addr, repo] of Object.entries(cursorFile?.repos ?? {})) {\n cursor[addr] = { ...repo, processed: boundProcessed(repo.processed ?? []) };\n }\n const inbox: InboxCursor = {\n ...(cursorFile?.inbox?.lastCreatedAt !== undefined\n ? { lastCreatedAt: cursorFile.inbox.lastCreatedAt }\n : {}),\n processed: boundProcessed(cursorFile?.inbox?.processed ?? []),\n };\n return { cursor, inbox, secrets };\n}\n\n/** Persist the repo cursors + inbox cursor (processed lists bounded). */\nexport async function saveCursor(\n stateDir: string,\n cursor: CoordinatorCursor,\n inbox: InboxCursor\n): Promise<void> {\n const repos: CoordinatorCursor = {};\n for (const [addr, repo] of Object.entries(cursor)) {\n repos[addr] = { ...repo, processed: boundProcessed(repo.processed) };\n }\n const file: CursorFile = {\n repos,\n inbox: { ...inbox, processed: boundProcessed(inbox.processed) },\n };\n await writeJsonAtomic(cursorPath(stateDir), file);\n}\n\n/** Persist the secret inventories. */\nexport async function saveSecrets(\n stateDir: string,\n secrets: CoordinatorSecrets\n): Promise<void> {\n await writeJsonAtomic(secretsPath(stateDir), secrets);\n}\n"],"mappings":"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","names":["join","conclusion","readFile","rm","join","spawn","resolve","spawn","mkdir","join","rm","conclusion","now","resolve","readFile","join","checkout","tip","best","resolve","join"]}
|
package/dist/cli/rig.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
dispatch
|
|
4
|
-
} from "../chunk-
|
|
5
|
-
import "../chunk-
|
|
6
|
-
import "../chunk-
|
|
4
|
+
} from "../chunk-UZZ2JKEC.js";
|
|
5
|
+
import "../chunk-S2BMLXAD.js";
|
|
6
|
+
import "../chunk-HMED76GU.js";
|
|
7
7
|
import "../chunk-XCLUB2CY.js";
|
|
8
8
|
import "../chunk-U5A7MHBP.js";
|
|
9
9
|
|
package/dist/index.d.ts
CHANGED
|
@@ -2098,10 +2098,22 @@ interface CiStatusJob {
|
|
|
2098
2098
|
logsUrl?: string;
|
|
2099
2099
|
artifacts: CiArtifact[];
|
|
2100
2100
|
exitCode?: number;
|
|
2101
|
+
queuedAt?: number;
|
|
2101
2102
|
startedAt?: number;
|
|
2103
|
+
/** The 9841's `created_at`: when the runner published this conclusion. */
|
|
2104
|
+
concludedAt: number;
|
|
2102
2105
|
eventId: string;
|
|
2103
2106
|
publisher: string;
|
|
2104
2107
|
}
|
|
2108
|
+
/**
|
|
2109
|
+
* A `pull_request` run's NIP-22 context (the PR as root, the PR or PR Update
|
|
2110
|
+
* that supplied the commit as parent) plus whether the PR author is the repo
|
|
2111
|
+
* owner or a declared maintainer — derived here from the 30617, never from
|
|
2112
|
+
* anything the coordinator asserts (#130).
|
|
2113
|
+
*/
|
|
2114
|
+
interface CiStatusPullRequest extends CiPrContext {
|
|
2115
|
+
authorIsMaintainer: boolean;
|
|
2116
|
+
}
|
|
2105
2117
|
interface CiStatusRun {
|
|
2106
2118
|
runId: string;
|
|
2107
2119
|
coordinator: string;
|
|
@@ -2114,6 +2126,8 @@ interface CiStatusRun {
|
|
|
2114
2126
|
startedAt?: number;
|
|
2115
2127
|
queuedAt?: number;
|
|
2116
2128
|
provenance?: CiProvenance;
|
|
2129
|
+
/** Present exactly when the run was triggered by a pull request. */
|
|
2130
|
+
pr?: CiStatusPullRequest;
|
|
2117
2131
|
jobs: CiStatusJob[];
|
|
2118
2132
|
/** Event id of the 9842 (concluded) or the latest 39842. */
|
|
2119
2133
|
eventId: string;
|
package/dist/index.js
CHANGED
|
@@ -92,7 +92,7 @@ import {
|
|
|
92
92
|
walkClosure,
|
|
93
93
|
writeGitObject,
|
|
94
94
|
writeGitObjects
|
|
95
|
-
} from "./chunk-
|
|
95
|
+
} from "./chunk-S2BMLXAD.js";
|
|
96
96
|
import {
|
|
97
97
|
COMMENT_KIND,
|
|
98
98
|
MAINTAINERS_TAG,
|
|
@@ -110,7 +110,7 @@ import {
|
|
|
110
110
|
parseMaintainers,
|
|
111
111
|
parsePayout,
|
|
112
112
|
queryRelay
|
|
113
|
-
} from "./chunk-
|
|
113
|
+
} from "./chunk-HMED76GU.js";
|
|
114
114
|
import "./chunk-U5A7MHBP.js";
|
|
115
115
|
|
|
116
116
|
// src/factory-job-gate.ts
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
import {
|
|
5
5
|
fetchRemoteState,
|
|
6
6
|
resolveIdentity
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-HMED76GU.js";
|
|
8
8
|
import {
|
|
9
9
|
ChannelMapStore,
|
|
10
10
|
RIG_CHANNEL_MAP_FILENAME
|
|
@@ -386,4 +386,4 @@ export {
|
|
|
386
386
|
resolveConnectorSettings,
|
|
387
387
|
resolveDestinations
|
|
388
388
|
};
|
|
389
|
-
//# sourceMappingURL=standalone-mode-
|
|
389
|
+
//# sourceMappingURL=standalone-mode-W3ZEKC6M.js.map
|