approval-md 0.0.1 → 0.2.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/LICENSE +176 -0
- package/NOTICE +5 -0
- package/README.md +940 -4
- package/SPEC.md +476 -34
- package/cli.js +29 -3
- package/dist/src/adapters/agentmail.d.ts +426 -0
- package/dist/src/adapters/agentmail.js +1200 -0
- package/dist/src/adapters/agentmail.js.map +1 -0
- package/dist/src/adapters/conformance.d.ts +149 -0
- package/dist/src/adapters/conformance.js +461 -0
- package/dist/src/adapters/conformance.js.map +1 -0
- package/dist/src/adapters/contract.d.ts +628 -0
- package/dist/src/adapters/contract.js +1035 -0
- package/dist/src/adapters/contract.js.map +1 -0
- package/dist/src/adapters/email.d.ts +324 -0
- package/dist/src/adapters/email.js +749 -0
- package/dist/src/adapters/email.js.map +1 -0
- package/dist/src/adapters/env-passphrase.d.ts +93 -0
- package/dist/src/adapters/env-passphrase.js +132 -0
- package/dist/src/adapters/env-passphrase.js.map +1 -0
- package/dist/src/adapters/public.d.ts +11 -0
- package/dist/src/adapters/public.js +11 -0
- package/dist/src/adapters/public.js.map +1 -0
- package/dist/src/adapters/registry.d.ts +59 -0
- package/dist/src/adapters/registry.js +77 -0
- package/dist/src/adapters/registry.js.map +1 -0
- package/dist/src/adapters/smtp.d.ts +213 -0
- package/dist/src/adapters/smtp.js +499 -0
- package/dist/src/adapters/smtp.js.map +1 -0
- package/dist/src/adapters/vault-provider.d.ts +114 -0
- package/dist/src/adapters/vault-provider.js +161 -0
- package/dist/src/adapters/vault-provider.js.map +1 -0
- package/dist/src/adapters/zzz.d.ts +66 -0
- package/dist/src/adapters/zzz.js +299 -0
- package/dist/src/adapters/zzz.js.map +1 -0
- package/dist/src/channels/batch.d.ts +109 -0
- package/dist/src/channels/batch.js +121 -0
- package/dist/src/channels/batch.js.map +1 -0
- package/dist/src/channels/cli.d.ts +193 -0
- package/dist/src/channels/cli.js +468 -0
- package/dist/src/channels/cli.js.map +1 -0
- package/dist/src/channels/conformance.d.ts +92 -0
- package/dist/src/channels/conformance.js +445 -0
- package/dist/src/channels/conformance.js.map +1 -0
- package/dist/src/channels/contract.d.ts +623 -0
- package/dist/src/channels/contract.js +494 -0
- package/dist/src/channels/contract.js.map +1 -0
- package/dist/src/channels/payload-view.d.ts +35 -0
- package/dist/src/channels/payload-view.js +43 -0
- package/dist/src/channels/payload-view.js.map +1 -0
- package/dist/src/channels/render-queue.d.ts +149 -0
- package/dist/src/channels/render-queue.js +564 -0
- package/dist/src/channels/render-queue.js.map +1 -0
- package/dist/src/channels/tagging.d.ts +196 -0
- package/dist/src/channels/tagging.js +723 -0
- package/dist/src/channels/tagging.js.map +1 -0
- package/dist/src/channels/telegram.d.ts +1832 -0
- package/dist/src/channels/telegram.js +3190 -0
- package/dist/src/channels/telegram.js.map +1 -0
- package/dist/src/channels/web.d.ts +341 -0
- package/dist/src/channels/web.js +903 -0
- package/dist/src/channels/web.js.map +1 -0
- package/dist/src/cli/adapter.d.ts +90 -0
- package/dist/src/cli/adapter.js +288 -0
- package/dist/src/cli/adapter.js.map +1 -0
- package/dist/src/cli/amend.d.ts +59 -0
- package/dist/src/cli/amend.js +2171 -0
- package/dist/src/cli/amend.js.map +1 -0
- package/dist/src/cli/args.d.ts +43 -0
- package/dist/src/cli/args.js +86 -0
- package/dist/src/cli/args.js.map +1 -0
- package/dist/src/cli/attest.d.ts +41 -0
- package/dist/src/cli/attest.js +307 -0
- package/dist/src/cli/attest.js.map +1 -0
- package/dist/src/cli/audit-card.d.ts +62 -0
- package/dist/src/cli/audit-card.js +201 -0
- package/dist/src/cli/audit-card.js.map +1 -0
- package/dist/src/cli/audit.d.ts +59 -0
- package/dist/src/cli/audit.js +460 -0
- package/dist/src/cli/audit.js.map +1 -0
- package/dist/src/cli/channel-telegram.d.ts +806 -0
- package/dist/src/cli/channel-telegram.js +2063 -0
- package/dist/src/cli/channel-telegram.js.map +1 -0
- package/dist/src/cli/channel-web.d.ts +131 -0
- package/dist/src/cli/channel-web.js +357 -0
- package/dist/src/cli/channel-web.js.map +1 -0
- package/dist/src/cli/channel.d.ts +71 -0
- package/dist/src/cli/channel.js +438 -0
- package/dist/src/cli/channel.js.map +1 -0
- package/dist/src/cli/checkpoint-tap.d.ts +169 -0
- package/dist/src/cli/checkpoint-tap.js +238 -0
- package/dist/src/cli/checkpoint-tap.js.map +1 -0
- package/dist/src/cli/codex.d.ts +2 -0
- package/dist/src/cli/codex.js +172 -0
- package/dist/src/cli/codex.js.map +1 -0
- package/dist/src/cli/coverage.d.ts +61 -0
- package/dist/src/cli/coverage.js +343 -0
- package/dist/src/cli/coverage.js.map +1 -0
- package/dist/src/cli/daemon.d.ts +120 -0
- package/dist/src/cli/daemon.js +631 -0
- package/dist/src/cli/daemon.js.map +1 -0
- package/dist/src/cli/doctor.d.ts +129 -0
- package/dist/src/cli/doctor.js +2762 -0
- package/dist/src/cli/doctor.js.map +1 -0
- package/dist/src/cli/env.d.ts +65 -0
- package/dist/src/cli/env.js +302 -0
- package/dist/src/cli/env.js.map +1 -0
- package/dist/src/cli/execute.d.ts +202 -0
- package/dist/src/cli/execute.js +1682 -0
- package/dist/src/cli/execute.js.map +1 -0
- package/dist/src/cli/exit-codes.d.ts +73 -0
- package/dist/src/cli/exit-codes.js +82 -0
- package/dist/src/cli/exit-codes.js.map +1 -0
- package/dist/src/cli/feedback.d.ts +60 -0
- package/dist/src/cli/feedback.js +205 -0
- package/dist/src/cli/feedback.js.map +1 -0
- package/dist/src/cli/gate-window.d.ts +40 -0
- package/dist/src/cli/gate-window.js +294 -0
- package/dist/src/cli/gate-window.js.map +1 -0
- package/dist/src/cli/gate.d.ts +68 -0
- package/dist/src/cli/gate.js +557 -0
- package/dist/src/cli/gate.js.map +1 -0
- package/dist/src/cli/git-scope.d.ts +190 -0
- package/dist/src/cli/git-scope.js +295 -0
- package/dist/src/cli/git-scope.js.map +1 -0
- package/dist/src/cli/gloss-attach.d.ts +85 -0
- package/dist/src/cli/gloss-attach.js +107 -0
- package/dist/src/cli/gloss-attach.js.map +1 -0
- package/dist/src/cli/gloss-codex-child.d.ts +9 -0
- package/dist/src/cli/gloss-codex-child.js +149 -0
- package/dist/src/cli/gloss-codex-child.js.map +1 -0
- package/dist/src/cli/gloss-codex.d.ts +24 -0
- package/dist/src/cli/gloss-codex.js +255 -0
- package/dist/src/cli/gloss-codex.js.map +1 -0
- package/dist/src/cli/gloss-options.d.ts +42 -0
- package/dist/src/cli/gloss-options.js +79 -0
- package/dist/src/cli/gloss-options.js.map +1 -0
- package/dist/src/cli/gloss.d.ts +265 -0
- package/dist/src/cli/gloss.js +362 -0
- package/dist/src/cli/gloss.js.map +1 -0
- package/dist/src/cli/help.d.ts +103 -0
- package/dist/src/cli/help.js +2339 -0
- package/dist/src/cli/help.js.map +1 -0
- package/dist/src/cli/hook-codex.d.ts +78 -0
- package/dist/src/cli/hook-codex.js +167 -0
- package/dist/src/cli/hook-codex.js.map +1 -0
- package/dist/src/cli/hook.d.ts +331 -0
- package/dist/src/cli/hook.js +2849 -0
- package/dist/src/cli/hook.js.map +1 -0
- package/dist/src/cli/import.d.ts +35 -0
- package/dist/src/cli/import.js +175 -0
- package/dist/src/cli/import.js.map +1 -0
- package/dist/src/cli/init.d.ts +84 -0
- package/dist/src/cli/init.js +336 -0
- package/dist/src/cli/init.js.map +1 -0
- package/dist/src/cli/instructions.d.ts +23 -0
- package/dist/src/cli/instructions.js +262 -0
- package/dist/src/cli/instructions.js.map +1 -0
- package/dist/src/cli/journal.d.ts +41 -0
- package/dist/src/cli/journal.js +238 -0
- package/dist/src/cli/journal.js.map +1 -0
- package/dist/src/cli/log-advance.d.ts +287 -0
- package/dist/src/cli/log-advance.js +840 -0
- package/dist/src/cli/log-advance.js.map +1 -0
- package/dist/src/cli/log-anchor.d.ts +176 -0
- package/dist/src/cli/log-anchor.js +387 -0
- package/dist/src/cli/log-anchor.js.map +1 -0
- package/dist/src/cli/log-checkpoint.d.ts +22 -0
- package/dist/src/cli/log-checkpoint.js +128 -0
- package/dist/src/cli/log-checkpoint.js.map +1 -0
- package/dist/src/cli/log-sync.d.ts +243 -0
- package/dist/src/cli/log-sync.js +849 -0
- package/dist/src/cli/log-sync.js.map +1 -0
- package/dist/src/cli/log-verbs.d.ts +16 -0
- package/dist/src/cli/log-verbs.js +360 -0
- package/dist/src/cli/log-verbs.js.map +1 -0
- package/dist/src/cli/long-help.d.ts +70 -0
- package/dist/src/cli/long-help.js +148 -0
- package/dist/src/cli/long-help.js.map +1 -0
- package/dist/src/cli/main.d.ts +77 -0
- package/dist/src/cli/main.js +1206 -0
- package/dist/src/cli/main.js.map +1 -0
- package/dist/src/cli/mcp.d.ts +52 -0
- package/dist/src/cli/mcp.js +306 -0
- package/dist/src/cli/mcp.js.map +1 -0
- package/dist/src/cli/paths.d.ts +56 -0
- package/dist/src/cli/paths.js +79 -0
- package/dist/src/cli/paths.js.map +1 -0
- package/dist/src/cli/payload.d.ts +58 -0
- package/dist/src/cli/payload.js +253 -0
- package/dist/src/cli/payload.js.map +1 -0
- package/dist/src/cli/policy.d.ts +43 -0
- package/dist/src/cli/policy.js +229 -0
- package/dist/src/cli/policy.js.map +1 -0
- package/dist/src/cli/preflight.d.ts +363 -0
- package/dist/src/cli/preflight.js +1175 -0
- package/dist/src/cli/preflight.js.map +1 -0
- package/dist/src/cli/progress.d.ts +78 -0
- package/dist/src/cli/progress.js +112 -0
- package/dist/src/cli/progress.js.map +1 -0
- package/dist/src/cli/prompt.d.ts +209 -0
- package/dist/src/cli/prompt.js +312 -0
- package/dist/src/cli/prompt.js.map +1 -0
- package/dist/src/cli/quickstart.d.ts +46 -0
- package/dist/src/cli/quickstart.js +297 -0
- package/dist/src/cli/quickstart.js.map +1 -0
- package/dist/src/cli/records.d.ts +34 -0
- package/dist/src/cli/records.js +66 -0
- package/dist/src/cli/records.js.map +1 -0
- package/dist/src/cli/render.d.ts +22 -0
- package/dist/src/cli/render.js +132 -0
- package/dist/src/cli/render.js.map +1 -0
- package/dist/src/cli/sandbox.d.ts +51 -0
- package/dist/src/cli/sandbox.js +150 -0
- package/dist/src/cli/sandbox.js.map +1 -0
- package/dist/src/cli/scaffold.d.ts +79 -0
- package/dist/src/cli/scaffold.js +137 -0
- package/dist/src/cli/scaffold.js.map +1 -0
- package/dist/src/cli/setup-adapter.d.ts +137 -0
- package/dist/src/cli/setup-adapter.js +509 -0
- package/dist/src/cli/setup-adapter.js.map +1 -0
- package/dist/src/cli/setup-channel.d.ts +117 -0
- package/dist/src/cli/setup-channel.js +635 -0
- package/dist/src/cli/setup-channel.js.map +1 -0
- package/dist/src/cli/setup-checkpoint.d.ts +57 -0
- package/dist/src/cli/setup-checkpoint.js +196 -0
- package/dist/src/cli/setup-checkpoint.js.map +1 -0
- package/dist/src/cli/setup-common.d.ts +275 -0
- package/dist/src/cli/setup-common.js +376 -0
- package/dist/src/cli/setup-common.js.map +1 -0
- package/dist/src/cli/setup-flow.d.ts +287 -0
- package/dist/src/cli/setup-flow.js +476 -0
- package/dist/src/cli/setup-flow.js.map +1 -0
- package/dist/src/cli/setup-service.d.ts +96 -0
- package/dist/src/cli/setup-service.js +308 -0
- package/dist/src/cli/setup-service.js.map +1 -0
- package/dist/src/cli/setup.d.ts +202 -0
- package/dist/src/cli/setup.js +473 -0
- package/dist/src/cli/setup.js.map +1 -0
- package/dist/src/cli/style.d.ts +320 -0
- package/dist/src/cli/style.js +469 -0
- package/dist/src/cli/style.js.map +1 -0
- package/dist/src/cli/token.d.ts +39 -0
- package/dist/src/cli/token.js +274 -0
- package/dist/src/cli/token.js.map +1 -0
- package/dist/src/cli/up.d.ts +155 -0
- package/dist/src/cli/up.js +849 -0
- package/dist/src/cli/up.js.map +1 -0
- package/dist/src/cli/usage.d.ts +37 -0
- package/dist/src/cli/usage.js +91 -0
- package/dist/src/cli/usage.js.map +1 -0
- package/dist/src/cli/values.d.ts +40 -0
- package/dist/src/cli/values.js +189 -0
- package/dist/src/cli/values.js.map +1 -0
- package/dist/src/cli/vault.d.ts +59 -0
- package/dist/src/cli/vault.js +362 -0
- package/dist/src/cli/vault.js.map +1 -0
- package/dist/src/cli/verb-registry.d.ts +76 -0
- package/dist/src/cli/verb-registry.js +2341 -0
- package/dist/src/cli/verb-registry.js.map +1 -0
- package/dist/src/cli/wordmark.d.ts +31 -0
- package/dist/src/cli/wordmark.js +52 -0
- package/dist/src/cli/wordmark.js.map +1 -0
- package/dist/src/codex/doctor.d.ts +13 -0
- package/dist/src/codex/doctor.js +41 -0
- package/dist/src/codex/doctor.js.map +1 -0
- package/dist/src/codex/manifest.d.ts +49 -0
- package/dist/src/codex/manifest.js +103 -0
- package/dist/src/codex/manifest.js.map +1 -0
- package/dist/src/codex/templates.d.ts +41 -0
- package/dist/src/codex/templates.js +319 -0
- package/dist/src/codex/templates.js.map +1 -0
- package/dist/src/codex/trust.d.ts +19 -0
- package/dist/src/codex/trust.js +183 -0
- package/dist/src/codex/trust.js.map +1 -0
- package/dist/src/codex/workspace-plan.d.ts +131 -0
- package/dist/src/codex/workspace-plan.js +561 -0
- package/dist/src/codex/workspace-plan.js.map +1 -0
- package/dist/src/core/actor.d.ts +2 -0
- package/dist/src/core/actor.js +5 -0
- package/dist/src/core/actor.js.map +1 -0
- package/dist/src/core/advance-cycle.d.ts +170 -0
- package/dist/src/core/advance-cycle.js +200 -0
- package/dist/src/core/advance-cycle.js.map +1 -0
- package/dist/src/core/agents-md.d.ts +276 -0
- package/dist/src/core/agents-md.js +747 -0
- package/dist/src/core/agents-md.js.map +1 -0
- package/dist/src/core/apply-patch.d.ts +49 -0
- package/dist/src/core/apply-patch.js +266 -0
- package/dist/src/core/apply-patch.js.map +1 -0
- package/dist/src/core/attest.d.ts +420 -0
- package/dist/src/core/attest.js +589 -0
- package/dist/src/core/attest.js.map +1 -0
- package/dist/src/core/audit.d.ts +492 -0
- package/dist/src/core/audit.js +882 -0
- package/dist/src/core/audit.js.map +1 -0
- package/dist/src/core/budgets.d.ts +238 -0
- package/dist/src/core/budgets.js +449 -0
- package/dist/src/core/budgets.js.map +1 -0
- package/dist/src/core/checkpoint.d.ts +500 -0
- package/dist/src/core/checkpoint.js +738 -0
- package/dist/src/core/checkpoint.js.map +1 -0
- package/dist/src/core/child-env.d.ts +88 -0
- package/dist/src/core/child-env.js +86 -0
- package/dist/src/core/child-env.js.map +1 -0
- package/dist/src/core/clock.d.ts +52 -0
- package/dist/src/core/clock.js +43 -0
- package/dist/src/core/clock.js.map +1 -0
- package/dist/src/core/command-class.d.ts +543 -0
- package/dist/src/core/command-class.js +2356 -0
- package/dist/src/core/command-class.js.map +1 -0
- package/dist/src/core/coverage-sources/adapter.d.ts +40 -0
- package/dist/src/core/coverage-sources/adapter.js +71 -0
- package/dist/src/core/coverage-sources/adapter.js.map +1 -0
- package/dist/src/core/coverage-sources/gh.d.ts +48 -0
- package/dist/src/core/coverage-sources/gh.js +136 -0
- package/dist/src/core/coverage-sources/gh.js.map +1 -0
- package/dist/src/core/coverage-sources/git.d.ts +101 -0
- package/dist/src/core/coverage-sources/git.js +269 -0
- package/dist/src/core/coverage-sources/git.js.map +1 -0
- package/dist/src/core/coverage.d.ts +217 -0
- package/dist/src/core/coverage.js +337 -0
- package/dist/src/core/coverage.js.map +1 -0
- package/dist/src/core/credential-spec.d.ts +72 -0
- package/dist/src/core/credential-spec.js +23 -0
- package/dist/src/core/credential-spec.js.map +1 -0
- package/dist/src/core/dark-session.d.ts +331 -0
- package/dist/src/core/dark-session.js +714 -0
- package/dist/src/core/dark-session.js.map +1 -0
- package/dist/src/core/decision-refusal.d.ts +185 -0
- package/dist/src/core/decision-refusal.js +265 -0
- package/dist/src/core/decision-refusal.js.map +1 -0
- package/dist/src/core/env-file.d.ts +450 -0
- package/dist/src/core/env-file.js +837 -0
- package/dist/src/core/env-file.js.map +1 -0
- package/dist/src/core/execute.d.ts +858 -0
- package/dist/src/core/execute.js +1271 -0
- package/dist/src/core/execute.js.map +1 -0
- package/dist/src/core/frontmatter.d.ts +78 -0
- package/dist/src/core/frontmatter.js +100 -0
- package/dist/src/core/frontmatter.js.map +1 -0
- package/dist/src/core/gate-window.d.ts +312 -0
- package/dist/src/core/gate-window.js +506 -0
- package/dist/src/core/gate-window.js.map +1 -0
- package/dist/src/core/gate.d.ts +1364 -0
- package/dist/src/core/gate.js +3002 -0
- package/dist/src/core/gate.js.map +1 -0
- package/dist/src/core/git-run.d.ts +73 -0
- package/dist/src/core/git-run.js +93 -0
- package/dist/src/core/git-run.js.map +1 -0
- package/dist/src/core/harness-version.d.ts +157 -0
- package/dist/src/core/harness-version.js +211 -0
- package/dist/src/core/harness-version.js.map +1 -0
- package/dist/src/core/harness-wait.d.ts +55 -0
- package/dist/src/core/harness-wait.js +58 -0
- package/dist/src/core/harness-wait.js.map +1 -0
- package/dist/src/core/head-retry.d.ts +107 -0
- package/dist/src/core/head-retry.js +121 -0
- package/dist/src/core/head-retry.js.map +1 -0
- package/dist/src/core/instance.d.ts +253 -0
- package/dist/src/core/instance.js +319 -0
- package/dist/src/core/instance.js.map +1 -0
- package/dist/src/core/intake-limits.d.ts +247 -0
- package/dist/src/core/intake-limits.js +350 -0
- package/dist/src/core/intake-limits.js.map +1 -0
- package/dist/src/core/jcs.d.ts +52 -0
- package/dist/src/core/jcs.js +132 -0
- package/dist/src/core/jcs.js.map +1 -0
- package/dist/src/core/journal.d.ts +144 -0
- package/dist/src/core/journal.js +200 -0
- package/dist/src/core/journal.js.map +1 -0
- package/dist/src/core/live-draw.d.ts +436 -0
- package/dist/src/core/live-draw.js +703 -0
- package/dist/src/core/live-draw.js.map +1 -0
- package/dist/src/core/log-reconcile.d.ts +89 -0
- package/dist/src/core/log-reconcile.js +136 -0
- package/dist/src/core/log-reconcile.js.map +1 -0
- package/dist/src/core/log-subscribe.d.ts +36 -0
- package/dist/src/core/log-subscribe.js +162 -0
- package/dist/src/core/log-subscribe.js.map +1 -0
- package/dist/src/core/log.d.ts +278 -0
- package/dist/src/core/log.js +546 -0
- package/dist/src/core/log.js.map +1 -0
- package/dist/src/core/loop.d.ts +274 -0
- package/dist/src/core/loop.js +487 -0
- package/dist/src/core/loop.js.map +1 -0
- package/dist/src/core/md-fence.d.ts +41 -0
- package/dist/src/core/md-fence.js +74 -0
- package/dist/src/core/md-fence.js.map +1 -0
- package/dist/src/core/money.d.ts +147 -0
- package/dist/src/core/money.js +195 -0
- package/dist/src/core/money.js.map +1 -0
- package/dist/src/core/payload-census.d.ts +74 -0
- package/dist/src/core/payload-census.js +146 -0
- package/dist/src/core/payload-census.js.map +1 -0
- package/dist/src/core/payload-store.d.ts +175 -0
- package/dist/src/core/payload-store.js +340 -0
- package/dist/src/core/payload-store.js.map +1 -0
- package/dist/src/core/payload.d.ts +71 -0
- package/dist/src/core/payload.js +80 -0
- package/dist/src/core/payload.js.map +1 -0
- package/dist/src/core/policy-diff.d.ts +292 -0
- package/dist/src/core/policy-diff.js +588 -0
- package/dist/src/core/policy-diff.js.map +1 -0
- package/dist/src/core/policy-expectations.d.ts +199 -0
- package/dist/src/core/policy-expectations.js +394 -0
- package/dist/src/core/policy-expectations.js.map +1 -0
- package/dist/src/core/policy-explain.d.ts +150 -0
- package/dist/src/core/policy-explain.js +258 -0
- package/dist/src/core/policy-explain.js.map +1 -0
- package/dist/src/core/policy-load.d.ts +527 -0
- package/dist/src/core/policy-load.js +536 -0
- package/dist/src/core/policy-load.js.map +1 -0
- package/dist/src/core/policy-match.d.ts +281 -0
- package/dist/src/core/policy-match.js +478 -0
- package/dist/src/core/policy-match.js.map +1 -0
- package/dist/src/core/policy-proposal.d.ts +265 -0
- package/dist/src/core/policy-proposal.js +458 -0
- package/dist/src/core/policy-proposal.js.map +1 -0
- package/dist/src/core/prompt-layout.d.ts +221 -0
- package/dist/src/core/prompt-layout.js +422 -0
- package/dist/src/core/prompt-layout.js.map +1 -0
- package/dist/src/core/protected-path-guard.d.ts +453 -0
- package/dist/src/core/protected-path-guard.js +1566 -0
- package/dist/src/core/protected-path-guard.js.map +1 -0
- package/dist/src/core/registration.d.ts +25 -0
- package/dist/src/core/registration.js +39 -0
- package/dist/src/core/registration.js.map +1 -0
- package/dist/src/core/reindex.d.ts +99 -0
- package/dist/src/core/reindex.js +336 -0
- package/dist/src/core/reindex.js.map +1 -0
- package/dist/src/core/sampler.d.ts +313 -0
- package/dist/src/core/sampler.js +388 -0
- package/dist/src/core/sampler.js.map +1 -0
- package/dist/src/core/sandbox.d.ts +290 -0
- package/dist/src/core/sandbox.js +424 -0
- package/dist/src/core/sandbox.js.map +1 -0
- package/dist/src/core/seal.d.ts +165 -0
- package/dist/src/core/seal.js +290 -0
- package/dist/src/core/seal.js.map +1 -0
- package/dist/src/core/state.d.ts +505 -0
- package/dist/src/core/state.js +1009 -0
- package/dist/src/core/state.js.map +1 -0
- package/dist/src/core/task-file.d.ts +185 -0
- package/dist/src/core/task-file.js +464 -0
- package/dist/src/core/task-file.js.map +1 -0
- package/dist/src/core/telegram-config.d.ts +93 -0
- package/dist/src/core/telegram-config.js +114 -0
- package/dist/src/core/telegram-config.js.map +1 -0
- package/dist/src/core/token.d.ts +409 -0
- package/dist/src/core/token.js +561 -0
- package/dist/src/core/token.js.map +1 -0
- package/dist/src/core/validate.d.ts +138 -0
- package/dist/src/core/validate.js +0 -0
- package/dist/src/core/validate.js.map +1 -0
- package/dist/src/core/values.d.ts +137 -0
- package/dist/src/core/values.js +153 -0
- package/dist/src/core/values.js.map +1 -0
- package/dist/src/core/vault.d.ts +291 -0
- package/dist/src/core/vault.js +612 -0
- package/dist/src/core/vault.js.map +1 -0
- package/dist/src/core/verified-snapshot.d.ts +204 -0
- package/dist/src/core/verified-snapshot.js +506 -0
- package/dist/src/core/verified-snapshot.js.map +1 -0
- package/dist/src/core/verify.d.ts +336 -0
- package/dist/src/core/verify.js +549 -0
- package/dist/src/core/verify.js.map +1 -0
- package/dist/src/core/version.d.ts +8 -0
- package/dist/src/core/version.js +9 -0
- package/dist/src/core/version.js.map +1 -0
- package/dist/src/core/wysiwys.d.ts +370 -0
- package/dist/src/core/wysiwys.js +728 -0
- package/dist/src/core/wysiwys.js.map +1 -0
- package/dist/src/daemon/advance-child.d.ts +39 -0
- package/dist/src/daemon/advance-child.js +78 -0
- package/dist/src/daemon/advance-child.js.map +1 -0
- package/dist/src/daemon/advance.d.ts +466 -0
- package/dist/src/daemon/advance.js +849 -0
- package/dist/src/daemon/advance.js.map +1 -0
- package/dist/src/daemon/audit.d.ts +87 -0
- package/dist/src/daemon/audit.js +90 -0
- package/dist/src/daemon/audit.js.map +1 -0
- package/dist/src/daemon/daemon.d.ts +1180 -0
- package/dist/src/daemon/daemon.js +1988 -0
- package/dist/src/daemon/daemon.js.map +1 -0
- package/dist/src/daemon/dark-session.d.ts +64 -0
- package/dist/src/daemon/dark-session.js +119 -0
- package/dist/src/daemon/dark-session.js.map +1 -0
- package/dist/src/daemon/draw-child.d.ts +36 -0
- package/dist/src/daemon/draw-child.js +132 -0
- package/dist/src/daemon/draw-child.js.map +1 -0
- package/dist/src/daemon/draw.d.ts +154 -0
- package/dist/src/daemon/draw.js +458 -0
- package/dist/src/daemon/draw.js.map +1 -0
- package/dist/src/daemon/git-evidence.d.ts +173 -0
- package/dist/src/daemon/git-evidence.js +345 -0
- package/dist/src/daemon/git-evidence.js.map +1 -0
- package/dist/src/daemon/projection.d.ts +180 -0
- package/dist/src/daemon/projection.js +233 -0
- package/dist/src/daemon/projection.js.map +1 -0
- package/dist/src/daemon/prune.d.ts +207 -0
- package/dist/src/daemon/prune.js +376 -0
- package/dist/src/daemon/prune.js.map +1 -0
- package/dist/src/mcp/http.d.ts +113 -0
- package/dist/src/mcp/http.js +343 -0
- package/dist/src/mcp/http.js.map +1 -0
- package/dist/src/mcp/server.d.ts +265 -0
- package/dist/src/mcp/server.js +602 -0
- package/dist/src/mcp/server.js.map +1 -0
- package/docs/adapter-api.md +106 -0
- package/docs/cli-reference.md +5716 -0
- package/docs/codex-enforced-session.md +30 -0
- package/package.json +53 -4
- package/schema/.gitkeep +0 -0
- package/schema/LICENSE +117 -0
- package/schema/codex-instance.schema.json +82 -0
- package/schema/envelope.schema.json +137 -0
- package/schema/event.schema.json +1811 -0
- package/schema/fixtures/codex-instance/invalid/unpinned-codex-version.json +40 -0
- package/schema/fixtures/codex-instance/valid/canonical.json +40 -0
- package/schema/fixtures/envelope/invalid/action-missing-idempotency-key.json +15 -0
- package/schema/fixtures/envelope/invalid/action-unknown-class-format.json +14 -0
- package/schema/fixtures/envelope/invalid/confidence-out-of-range.json +11 -0
- package/schema/fixtures/envelope/invalid/est-cost-bare-number.json +14 -0
- package/schema/fixtures/envelope/invalid/est-cost-noncanonical-string.json +14 -0
- package/schema/fixtures/envelope/invalid/malformed-assignee.json +10 -0
- package/schema/fixtures/envelope/invalid/malformed-created-by.json +7 -0
- package/schema/fixtures/envelope/invalid/malformed-max-latency.json +11 -0
- package/schema/fixtures/envelope/invalid/malformed-payload-hash.json +14 -0
- package/schema/fixtures/envelope/invalid/max-cost-bare-number.json +11 -0
- package/schema/fixtures/envelope/invalid/missing-origin.json +3 -0
- package/schema/fixtures/envelope/invalid/negative-est-cost.json +14 -0
- package/schema/fixtures/envelope/invalid/unknown-state.json +7 -0
- package/schema/fixtures/envelope/invalid/unknown-top-level-field.json +8 -0
- package/schema/fixtures/envelope/valid/action-payload-hash.json +17 -0
- package/schema/fixtures/envelope/valid/actions-without-budget.json +18 -0
- package/schema/fixtures/envelope/valid/canonical.json +25 -0
- package/schema/fixtures/envelope/valid/minimal.json +7 -0
- package/schema/fixtures/envelope/valid/multi-action-executed.json +30 -0
- package/schema/fixtures/envelope/valid/record-write-stage.json +22 -0
- package/schema/fixtures/event/invalid/approval-granted-agent-actor.json +15 -0
- package/schema/fixtures/event/invalid/approval-granted-empty-batch-delivery-id.json +17 -0
- package/schema/fixtures/event/invalid/approval-granted-fifth-reaction.json +16 -0
- package/schema/fixtures/event/invalid/approval-granted-missing-actor.json +14 -0
- package/schema/fixtures/event/invalid/approval-requested-missing-action-key.json +14 -0
- package/schema/fixtures/event/invalid/approval-withdrawn-agent-policy-drift.json +15 -0
- package/schema/fixtures/event/invalid/approval-withdrawn-missing-reason.json +15 -0
- package/schema/fixtures/event/invalid/approval-withdrawn-system-actor.json +15 -0
- package/schema/fixtures/event/invalid/audit-decision-refused-human-actor.json +17 -0
- package/schema/fixtures/event/invalid/audit-decision-refused-missing-code.json +16 -0
- package/schema/fixtures/event/invalid/audit-reviewed-agent-actor.json +15 -0
- package/schema/fixtures/event/invalid/audit-reviewed-loved-no-note.json +16 -0
- package/schema/fixtures/event/invalid/audit-reviewed-system-actor.json +15 -0
- package/schema/fixtures/event/invalid/bad-actor-prefix.json +15 -0
- package/schema/fixtures/event/invalid/est-cost-bare-number.json +17 -0
- package/schema/fixtures/event/invalid/execution-completed-fabricated-exit-code.json +16 -0
- package/schema/fixtures/event/invalid/execution-completed-provider-ref-empty-id.json +18 -0
- package/schema/fixtures/event/invalid/execution-completed-provider-ref-extra-field.json +19 -0
- package/schema/fixtures/event/invalid/execution-completed-provider-ref-id-not-string.json +18 -0
- package/schema/fixtures/event/invalid/execution-completed-provider-ref-missing-adapter.json +17 -0
- package/schema/fixtures/event/invalid/execution-failed-open-reported-by.json +16 -0
- package/schema/fixtures/event/invalid/execution-indeterminate-open-reason.json +14 -0
- package/schema/fixtures/event/invalid/execution-reconciled-agent-actor.json +17 -0
- package/schema/fixtures/event/invalid/execution-started-negative-env-stripped.json +16 -0
- package/schema/fixtures/event/invalid/gate-bypassed-missing-opened-seq.json +15 -0
- package/schema/fixtures/event/invalid/gate-closed-non-integer-opened-seq.json +13 -0
- package/schema/fixtures/event/invalid/gate-opened-agent-actor.json +16 -0
- package/schema/fixtures/event/invalid/gate-organ-attested-absolute-path.json +14 -0
- package/schema/fixtures/event/invalid/gate-organ-attested-agent-actor.json +14 -0
- package/schema/fixtures/event/invalid/gate-organ-attested-missing-organ-path.json +13 -0
- package/schema/fixtures/event/invalid/harness-unknown-kind.json +18 -0
- package/schema/fixtures/event/invalid/harness-version-multiline.json +16 -0
- package/schema/fixtures/event/invalid/log-checkpoint-agent-actor.json +17 -0
- package/schema/fixtures/event/invalid/log-checkpoint-missing-signature.json +16 -0
- package/schema/fixtures/event/invalid/log-checkpoint-short-signed-hash.json +17 -0
- package/schema/fixtures/event/invalid/log-checkpoint-unknown-signature-alg.json +17 -0
- package/schema/fixtures/event/invalid/malformed-ts.json +15 -0
- package/schema/fixtures/event/invalid/missing-alg.json +14 -0
- package/schema/fixtures/event/invalid/missing-hash.json +14 -0
- package/schema/fixtures/event/invalid/non-integer-seq.json +15 -0
- package/schema/fixtures/event/invalid/payload-pruned-human-actor.json +14 -0
- package/schema/fixtures/event/invalid/payload-pruned-missing-hash.json +14 -0
- package/schema/fixtures/event/invalid/policy-declined-agent-actor.json +15 -0
- package/schema/fixtures/event/invalid/policy-proposed-missing-diff.json +19 -0
- package/schema/fixtures/event/invalid/policy-proposed-system-actor.json +26 -0
- package/schema/fixtures/event/invalid/short-hash.json +15 -0
- package/schema/fixtures/event/invalid/unknown-alg.json +15 -0
- package/schema/fixtures/event/invalid/unknown-event-type.json +15 -0
- package/schema/fixtures/event/invalid/unknown-top-level-field.json +16 -0
- package/schema/fixtures/event/valid/approval-expired.json +15 -0
- package/schema/fixtures/event/valid/approval-granted-batch.json +19 -0
- package/schema/fixtures/event/valid/approval-granted-reaction.json +16 -0
- package/schema/fixtures/event/valid/approval-granted.json +15 -0
- package/schema/fixtures/event/valid/approval-rejected.json +15 -0
- package/schema/fixtures/event/valid/approval-requested.json +19 -0
- package/schema/fixtures/event/valid/approval-revoked.json +15 -0
- package/schema/fixtures/event/valid/approval-withdrawn-policy-drift.json +17 -0
- package/schema/fixtures/event/valid/approval-withdrawn.json +16 -0
- package/schema/fixtures/event/valid/audit-decision-refused.json +20 -0
- package/schema/fixtures/event/valid/audit-reviewed-reaction.json +17 -0
- package/schema/fixtures/event/valid/audit-reviewed.json +15 -0
- package/schema/fixtures/event/valid/audit-sampled.json +14 -0
- package/schema/fixtures/event/valid/budget-exceeded.json +21 -0
- package/schema/fixtures/event/valid/envelope-drift.json +16 -0
- package/schema/fixtures/event/valid/execution-completed-harness-report.json +16 -0
- package/schema/fixtures/event/valid/execution-completed-provider-ref.json +18 -0
- package/schema/fixtures/event/valid/execution-completed.json +15 -0
- package/schema/fixtures/event/valid/execution-failed-harness-report.json +16 -0
- package/schema/fixtures/event/valid/execution-failed.json +15 -0
- package/schema/fixtures/event/valid/execution-indeterminate.json +15 -0
- package/schema/fixtures/event/valid/execution-reconciled.json +17 -0
- package/schema/fixtures/event/valid/execution-started-env-stripped.json +17 -0
- package/schema/fixtures/event/valid/execution-started.json +14 -0
- package/schema/fixtures/event/valid/gate-bypassed-harness-version.json +18 -0
- package/schema/fixtures/event/valid/gate-bypassed.json +19 -0
- package/schema/fixtures/event/valid/gate-closed.json +14 -0
- package/schema/fixtures/event/valid/gate-opened.json +16 -0
- package/schema/fixtures/event/valid/gate-organ-attested.json +14 -0
- package/schema/fixtures/event/valid/genesis-null-prev.json +14 -0
- package/schema/fixtures/event/valid/log-checkpoint.json +17 -0
- package/schema/fixtures/event/valid/payload-pruned-orphan.json +13 -0
- package/schema/fixtures/event/valid/payload-pruned.json +17 -0
- package/schema/fixtures/event/valid/policy-declined.json +16 -0
- package/schema/fixtures/event/valid/policy-proposed.json +35 -0
- package/schema/fixtures/event/valid/policy-updated.json +14 -0
- package/schema/fixtures/event/valid/reconciliation-required.json +18 -0
- package/schema/fixtures/event/valid/reconciliation-satisfied.json +17 -0
- package/schema/fixtures/event/valid/route-accepted.json +15 -0
- package/schema/fixtures/event/valid/route-proposed.json +16 -0
- package/schema/fixtures/event/valid/spec-example.json +15 -0
- package/schema/fixtures/event/valid/task-registered-harness-version.json +23 -0
- package/schema/fixtures/event/valid/task-registered.json +14 -0
- package/schema/fixtures/hash/known-answer-pre-121.json +74 -0
- package/schema/fixtures/hash/known-answer.json +74 -0
- package/schema/fixtures/policy/invalid/bad-approval-ttl.json +7 -0
- package/schema/fixtures/policy/invalid/bad-web-port.json +4 -0
- package/schema/fixtures/policy/invalid/checkpoint-key-not-base64.json +6 -0
- package/schema/fixtures/policy/invalid/class-rule-missing-autonomy.json +9 -0
- package/schema/fixtures/policy/invalid/empty-class-key.json +6 -0
- package/schema/fixtures/policy/invalid/live-rate-on-human-only.json +7 -0
- package/schema/fixtures/policy/invalid/malformed-class-key.json +6 -0
- package/schema/fixtures/policy/invalid/missing-version.json +8 -0
- package/schema/fixtures/policy/invalid/negative-limit.json +9 -0
- package/schema/fixtures/policy/invalid/non-numeric-limit.json +9 -0
- package/schema/fixtures/policy/invalid/non-positive-max-pending.json +9 -0
- package/schema/fixtures/policy/invalid/on-expiry-grant.json +8 -0
- package/schema/fixtures/policy/invalid/payload-retention-bare-number.json +4 -0
- package/schema/fixtures/policy/invalid/payload-retention-compound.json +4 -0
- package/schema/fixtures/policy/invalid/payload-retention-fractional.json +4 -0
- package/schema/fixtures/policy/invalid/payload-retention-zero.json +4 -0
- package/schema/fixtures/policy/invalid/protected-paths-escape.json +4 -0
- package/schema/fixtures/policy/invalid/protected-paths-glob.json +4 -0
- package/schema/fixtures/policy/invalid/retro-rate-on-human-only.json +7 -0
- package/schema/fixtures/policy/invalid/retro-rate-on-manual.json +7 -0
- package/schema/fixtures/policy/invalid/retro-rate-zero.json +7 -0
- package/schema/fixtures/policy/invalid/sample-rate-too-high.json +5 -0
- package/schema/fixtures/policy/invalid/sampling-secret-env-empty.json +7 -0
- package/schema/fixtures/policy/invalid/sampling-secret-env-not-string.json +6 -0
- package/schema/fixtures/policy/invalid/skew-tolerance-compound.json +6 -0
- package/schema/fixtures/policy/invalid/unknown-autonomy.json +7 -0
- package/schema/fixtures/policy/invalid/unknown-class-rule-key.json +6 -0
- package/schema/fixtures/policy/invalid/unknown-top-level-key.json +7 -0
- package/schema/fixtures/policy/invalid/vault-passphrase-env-empty.json +6 -0
- package/schema/fixtures/policy/invalid/vault-passphrase-literal.json +6 -0
- package/schema/fixtures/policy/invalid/version-not-string.json +4 -0
- package/schema/fixtures/policy/valid/canonical.json +47 -0
- package/schema/fixtures/policy/valid/checkpoint-keys.json +18 -0
- package/schema/fixtures/policy/valid/class-approvers-limits.json +25 -0
- package/schema/fixtures/policy/valid/class-retro-rate.json +17 -0
- package/schema/fixtures/policy/valid/global-budgets.json +19 -0
- package/schema/fixtures/policy/valid/human-only.json +9 -0
- package/schema/fixtures/policy/valid/minimal.json +6 -0
- package/schema/fixtures/policy/valid/protected-paths.json +10 -0
- package/schema/fixtures/policy/valid/record-namespace.json +13 -0
- package/schema/fixtures/policy/valid/request-volume-limits.json +26 -0
- package/schema/fixtures/policy/valid/retention-and-sampling-secret.json +16 -0
- package/schema/fixtures/policy/valid/skew-tolerance.json +15 -0
- package/schema/fixtures/policy/valid/vault-passphrase-env.json +14 -0
- package/schema/fixtures/policy/valid/wildcards.json +15 -0
- package/schema/fixtures/policy-md/invalid/alias-bomb.md +15 -0
- package/schema/fixtures/policy-md/invalid/no-fence.md +7 -0
- package/schema/fixtures/policy-md/invalid/protected-route-not-a-subclass.md +16 -0
- package/schema/fixtures/policy-md/invalid/schema-invalid-autonomy.md +16 -0
- package/schema/fixtures/policy-md/invalid/schema-invalid-read-proof.md +17 -0
- package/schema/fixtures/policy-md/invalid/two-fences.md +19 -0
- package/schema/fixtures/policy-md/invalid/unclosed-fence.md +11 -0
- package/schema/fixtures/policy-md/invalid/wrong-info-string.md +11 -0
- package/schema/fixtures/policy-md/invalid/yaml-syntax-error.md +13 -0
- package/schema/fixtures/policy-md/precedence/both/APPROVAL.md +7 -0
- package/schema/fixtures/policy-md/precedence/both/APPROVALS.md +7 -0
- package/schema/fixtures/policy-md/precedence/fallback-only/APPROVALS.md +7 -0
- package/schema/fixtures/policy-md/valid/canonical.md +50 -0
- package/schema/fixtures/policy-md/valid/daemon-read-proof.md +18 -0
- package/schema/fixtures/policy-md/valid/minimal.md +3 -0
- package/schema/fixtures/policy-md/valid/prose-lookalikes.md +54 -0
- package/schema/fixtures/policy-md/valid/routed-protected-paths.md +49 -0
- package/schema/fixtures/policy-md/valid/with-values.md +79 -0
- package/schema/fixtures/sample-record/invalid/bad-date-time.json +4 -0
- package/schema/fixtures/sample-record/invalid/missing-required-field.json +3 -0
- package/schema/fixtures/sample-record/invalid/unknown-top-level-field.json +5 -0
- package/schema/fixtures/sample-record/invalid/wrong-type.json +4 -0
- package/schema/fixtures/sample-record/valid/minimal.json +4 -0
- package/schema/fixtures/sample-record/valid/with-note.json +5 -0
- package/schema/fixtures/values/invalid/class-shaped.json +9 -0
- package/schema/fixtures/values/invalid/duplicate-entry.json +4 -0
- package/schema/fixtures/values/invalid/non-string-item.json +4 -0
- package/schema/fixtures/values/invalid/over-cap.json +26 -0
- package/schema/fixtures/values/invalid/unknown-key.json +5 -0
- package/schema/fixtures/values/invalid/version-string.json +1 -0
- package/schema/fixtures/values/valid/empty-lists.json +7 -0
- package/schema/fixtures/values/valid/full.json +20 -0
- package/schema/fixtures/values/valid/minimal.json +1 -0
- package/schema/fixtures/values-md/invalid/schema-invalid.md +62 -0
- package/schema/fixtures/values-md/invalid/two-blocks.md +69 -0
- package/schema/fixtures/values-md/invalid/unterminated.md +61 -0
- package/schema/fixtures/values-md/invalid/yaml-error.md +63 -0
- package/schema/fixtures/values-md/valid/absent.md +50 -0
- package/schema/fixtures/values-md/valid/with-values.md +79 -0
- package/schema/policy.schema.json +501 -0
- package/schema/sample-record.schema.json +26 -0
- package/schema/values.schema.json +55 -0
- package/templates/codex/README.md +9 -0
|
@@ -0,0 +1,849 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `approval up` — the ambient runtime: one process for the daemon and every
|
|
3
|
+
* channel the policy configures (SPEC.md §10.2/§10.3, APRV-110).
|
|
4
|
+
*
|
|
5
|
+
* Until now a working gate was two foreground processes an operator had to
|
|
6
|
+
* remember: `approval daemon run` to watch the files and expire what lapsed, and
|
|
7
|
+
* `approval channel telegram listen` to put requests in front of a human and
|
|
8
|
+
* collect the answer. Two terminals, two lifetimes, and a system that is half up
|
|
9
|
+
* whenever one of them is not. This verb runs both, plus the local queue page,
|
|
10
|
+
* under one supervisor, one set of signal handlers, and one event stream.
|
|
11
|
+
*
|
|
12
|
+
* **No logic lives here either.** The loop is still `daemon/daemon.ts`, the
|
|
13
|
+
* dispatch cycle is still `cli/channel-telegram.ts`'s `dispatchPending`, and the
|
|
14
|
+
* queue page is still `channel-web.ts`'s `startWebChannel` — which has said
|
|
15
|
+
* "exported for the tests and for the M5 daemon" since APRV-25. What this file
|
|
16
|
+
* adds is supervision: which parts to start, what to do when one falls over, and
|
|
17
|
+
* how to stop them all at once.
|
|
18
|
+
*
|
|
19
|
+
* ## Per-part crash isolation
|
|
20
|
+
*
|
|
21
|
+
* A channel is a network client and the daemon is not. A Bot API that starts
|
|
22
|
+
* refusing sends must not stop TTL expiry, write-back or the queue projection —
|
|
23
|
+
* that inversion is exactly what the split into two processes bought, and it is
|
|
24
|
+
* what this file has to buy back. So:
|
|
25
|
+
*
|
|
26
|
+
* - the DAEMON settles the verb. Its outcome chooses the exit code, exactly as
|
|
27
|
+
* `approval daemon run` maps it, and its failure stops everything;
|
|
28
|
+
* - a CHANNEL that falls over is reported and restarted after a doubling
|
|
29
|
+
* backoff, forever. There is no attempt limit for the same reason
|
|
30
|
+
* `dispatchPending` has no send limit: giving up would turn an outage into a
|
|
31
|
+
* pending request no human ever sees;
|
|
32
|
+
* - a channel that cannot start AT ALL — a credential the policy names is unset,
|
|
33
|
+
* no declared human identity — is not started, and the refusal is reported in
|
|
34
|
+
* `approval doctor`'s vocabulary (a `skip`, a detail, a fix). Fail closed, and
|
|
35
|
+
* then carry on with the parts that can run: a half-armed runtime that says
|
|
36
|
+
* nothing is the failure this project exists to prevent, and a runtime that
|
|
37
|
+
* refused to start the daemon because Telegram was unconfigured would be
|
|
38
|
+
* withholding the projection over a channel nobody asked for.
|
|
39
|
+
*
|
|
40
|
+
* ## The credentials come from the launch environment, and only from there
|
|
41
|
+
*
|
|
42
|
+
* SPEC.md §11.1 invariant 7: no verb loads `.approval/env` implicitly. This one
|
|
43
|
+
* does not either. The bot token, the chat id and the approver identity are read
|
|
44
|
+
* from `process.env` — the environment the operator launched this process with —
|
|
45
|
+
* through exactly the same resolvers the separate listener uses. `approval setup
|
|
46
|
+
* service` writes a unit that names those variables, or runs a wrapper that
|
|
47
|
+
* evaluates `approval env`, and either way a human wrote the line that does it.
|
|
48
|
+
*
|
|
49
|
+
* ## The event stream is an additive union
|
|
50
|
+
*
|
|
51
|
+
* `--json` prints one object per line, and every object is one of three kinds:
|
|
52
|
+
* a `DaemonEvent` verbatim, a listener line verbatim (`notified`, `decision`,
|
|
53
|
+
* `annotated`, `listening`, `stopped`), or one of this file's own supervision
|
|
54
|
+
* lines ({@link UpEvent}). Nothing is rewritten and no field is added to a shape
|
|
55
|
+
* that already existed: the decision object is byte-identical to the one
|
|
56
|
+
* `channel telegram listen` prints, because it is printed by the same function.
|
|
57
|
+
* That is what makes the composed test suite an assertion about equivalence
|
|
58
|
+
* rather than about a new format.
|
|
59
|
+
*
|
|
60
|
+
* The one shape that appears twice is `stopped`: the daemon's carries its tick
|
|
61
|
+
* counters and a channel's carries its delivery counters, and a supervisor that
|
|
62
|
+
* branches on `event` alone can tell them apart by their fields. Renaming either
|
|
63
|
+
* would have broken a stream an operator already parses.
|
|
64
|
+
*
|
|
65
|
+
* ## What it does not do
|
|
66
|
+
*
|
|
67
|
+
* It does not fork, write a pidfile, or manage its own lifecycle, for the reason
|
|
68
|
+
* `approval daemon run` gives: `launchd` and `systemd` do that better, and
|
|
69
|
+
* `approval setup service` is the verb that hands them the unit.
|
|
70
|
+
*/
|
|
71
|
+
import { isAbsolute, resolve as resolvePathSegments } from "node:path";
|
|
72
|
+
import { HUMAN_ACTOR_ENV, resolveHumanActor } from "../core/attest.js";
|
|
73
|
+
import { instanceFindings } from "../core/instance.js";
|
|
74
|
+
import { loadPolicy } from "../core/policy-load.js";
|
|
75
|
+
import { passphraseEnvFor } from "../core/vault.js";
|
|
76
|
+
import { DEFAULT_DEBOUNCE_MS, DEFAULT_INTERVAL_MS, DEFAULT_TASKS_DIR, Daemon, isDirectory, } from "../daemon/daemon.js";
|
|
77
|
+
import { drawServerFor } from "../daemon/draw.js";
|
|
78
|
+
import { enableGitEvidence } from "../daemon/git-evidence.js";
|
|
79
|
+
import { boolFlag, parseFlags, stringFlag } from "./args.js";
|
|
80
|
+
import { prepareListen, startListener, } from "./channel-telegram.js";
|
|
81
|
+
import { payloadSource as webPayloadSource, policyWebPort, resolveWebPort, startWebChannel, } from "./channel-web.js";
|
|
82
|
+
import { advanceFlags, describeDaemonEvent, describeGitEvidence, durationFlag, exitForDaemonOutcome, readProofFlags, } from "./daemon.js";
|
|
83
|
+
import { DEFAULT_DARK_INTERVAL_MS, DEFAULT_DARK_WINDOW_MS, } from "../daemon/dark-session.js";
|
|
84
|
+
import { useReadProof } from "../core/state.js";
|
|
85
|
+
import { EXIT_INTEGRITY, EXIT_IO, EXIT_OK, EXIT_USAGE } from "./exit-codes.js";
|
|
86
|
+
import { glossRunnerFromOptions, parseGlossOptions } from "./gloss-options.js";
|
|
87
|
+
import { UP_HELP } from "./help.js";
|
|
88
|
+
import { DEFAULT_LOG_PATH, preflightLog, resolvePath } from "./paths.js";
|
|
89
|
+
import { describePreflightEvent, reexecFreshBuild, startupPreflight, } from "./preflight.js";
|
|
90
|
+
import { DEFAULT_QUEUE_PATH } from "./render.js";
|
|
91
|
+
import { skipNotice, style } from "./style.js";
|
|
92
|
+
import { usageErrorText } from "./usage.js";
|
|
93
|
+
/** The first backoff after a part falls over. Doubles to {@link UP_RESTART_MAX_MS}. */
|
|
94
|
+
export const UP_RESTART_BASE_MS = 1_000;
|
|
95
|
+
/** The ceiling the doubling stops at, so a long outage retries once a minute. */
|
|
96
|
+
export const UP_RESTART_MAX_MS = 60_000;
|
|
97
|
+
/**
|
|
98
|
+
* How long a part must have been running before a failure is treated as a fresh
|
|
99
|
+
* one rather than as the next step of a crash loop. A channel that ran for an
|
|
100
|
+
* hour and then hit a network blip should retry in a second, not in a minute.
|
|
101
|
+
*/
|
|
102
|
+
export const UP_STABLE_MS = 60_000;
|
|
103
|
+
/**
|
|
104
|
+
* One supervision line as a human sentence, and where it belongs.
|
|
105
|
+
*
|
|
106
|
+
* `st` is injectable for the render tests, exactly as doctor's renderer takes
|
|
107
|
+
* one; the default asks the process, which is what the verb wants.
|
|
108
|
+
*/
|
|
109
|
+
export function describeUpEvent(event, st = style()) {
|
|
110
|
+
switch (event.event) {
|
|
111
|
+
// Delegated, so `approval daemon run` and `approval up` cannot drift apart
|
|
112
|
+
// in the words they use for the same fact.
|
|
113
|
+
case "preflight":
|
|
114
|
+
case "preflight_warning":
|
|
115
|
+
return describePreflightEvent(event);
|
|
116
|
+
case "up_started":
|
|
117
|
+
return {
|
|
118
|
+
text: `up: ${event.parts.join(", ")} in one process against ${event.log}; ctrl-c stops all of them`,
|
|
119
|
+
stderr: false,
|
|
120
|
+
};
|
|
121
|
+
case "part_started":
|
|
122
|
+
return { text: `up: ${event.part} started — ${event.detail}`, stderr: false };
|
|
123
|
+
case "part_unavailable":
|
|
124
|
+
// Doctor's skip, not a refusal: the part is absent by configuration, and
|
|
125
|
+
// nothing failed (APRV-153). The JSON emit path is untouched.
|
|
126
|
+
return {
|
|
127
|
+
text: skipNotice(st, event.check, event.detail, event.fix ?? undefined),
|
|
128
|
+
stderr: true,
|
|
129
|
+
};
|
|
130
|
+
case "part_failed":
|
|
131
|
+
return {
|
|
132
|
+
text: `approval: the ${event.part} channel stopped (attempt ${String(event.attempt)}): ${event.message} — restarting in ${String(event.restart_in_ms)}ms; the daemon loop is unaffected`,
|
|
133
|
+
stderr: true,
|
|
134
|
+
};
|
|
135
|
+
case "part_restarted":
|
|
136
|
+
return {
|
|
137
|
+
text: `up: ${event.part} restarted (attempt ${String(event.attempt)}); everything still pending is re-sent`,
|
|
138
|
+
stderr: false,
|
|
139
|
+
};
|
|
140
|
+
case "part_stopped":
|
|
141
|
+
return { text: `up: ${event.part} stopped (${event.reason})`, stderr: false };
|
|
142
|
+
case "up_stopped":
|
|
143
|
+
return { text: `up: stopped (${event.reason})`, stderr: false };
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
// ---------------------------------------------------------------------------
|
|
147
|
+
// Flags
|
|
148
|
+
// ---------------------------------------------------------------------------
|
|
149
|
+
const UP_FLAGS = {
|
|
150
|
+
// The daemon's own, spelled identically.
|
|
151
|
+
"--log": "string",
|
|
152
|
+
"--tasks": "string",
|
|
153
|
+
"--out": "string",
|
|
154
|
+
"--policy": "string",
|
|
155
|
+
"--dir": "string",
|
|
156
|
+
"--interval": "string",
|
|
157
|
+
"--debounce": "string",
|
|
158
|
+
"--once": "boolean",
|
|
159
|
+
// The watcher trace (APRV-230), spelled identically to `daemon run`'s: the
|
|
160
|
+
// ambient runtime is the process an operator actually runs, so the diagnostic
|
|
161
|
+
// for what is waking it has to be reachable from this spelling too.
|
|
162
|
+
"--trace-watch": "boolean",
|
|
163
|
+
"--git-evidence": "boolean",
|
|
164
|
+
// The cadence advance (APRV-204), spelled identically to `daemon run`'s.
|
|
165
|
+
"--advance": "boolean",
|
|
166
|
+
"--advance-interval": "string",
|
|
167
|
+
"--advance-after": "string",
|
|
168
|
+
"--advance-remote": "string",
|
|
169
|
+
"--advance-base": "string",
|
|
170
|
+
"--no-advance-pr": "boolean",
|
|
171
|
+
"--no-advance-auto-merge": "boolean",
|
|
172
|
+
// The live draw (APRV-208), spelled identically to `daemon run`'s.
|
|
173
|
+
"--no-draw": "boolean",
|
|
174
|
+
// The dark-session sweep (APRV-192), spelled identically to `daemon run`'s.
|
|
175
|
+
"--dark-sessions": "boolean",
|
|
176
|
+
"--dark-window": "string",
|
|
177
|
+
"--dark-interval": "string",
|
|
178
|
+
// The prefix proof (APRV-217), spelled identically to `daemon run`'s.
|
|
179
|
+
"--read-proof": "string",
|
|
180
|
+
"--full-reproof-every": "string",
|
|
181
|
+
"--full-reproof-after": "string",
|
|
182
|
+
// The channels'.
|
|
183
|
+
"--as": "string",
|
|
184
|
+
"--payloads": "string",
|
|
185
|
+
"--payload-dir": "string",
|
|
186
|
+
"--api-base": "string",
|
|
187
|
+
"--poll-timeout": "string",
|
|
188
|
+
"--port": "string",
|
|
189
|
+
"--no-telegram": "boolean",
|
|
190
|
+
"--no-web": "boolean",
|
|
191
|
+
"--gloss": "boolean",
|
|
192
|
+
"--no-gloss": "boolean",
|
|
193
|
+
"--gloss-provider": "string",
|
|
194
|
+
"--gloss-model": "string",
|
|
195
|
+
// The supervisor's.
|
|
196
|
+
"--restart-backoff": "string",
|
|
197
|
+
// The startup preflight (APRV-215).
|
|
198
|
+
"--no-preflight": "boolean",
|
|
199
|
+
"--preflight-remote": "string",
|
|
200
|
+
"--preflight-base": "string",
|
|
201
|
+
// The rebuild half of it, alone (APRV-301): fetch and fast-forward as usual,
|
|
202
|
+
// but leave a stale `dist/` where it is. The preflight then says so on stderr
|
|
203
|
+
// and reports `"action":"build-skipped"`, because starting on a build older
|
|
204
|
+
// than the sources is a thing an operator may mean and must never be told is
|
|
205
|
+
// fine.
|
|
206
|
+
"--no-build": "boolean",
|
|
207
|
+
// Test-only, spelled exactly as doctor's: retarget the build-freshness half of
|
|
208
|
+
// the preflight at a fixture tree. It moves nothing else, and a wrong value
|
|
209
|
+
// can only make the rebuild decision wrong — never the fast-forward.
|
|
210
|
+
"--root": "string",
|
|
211
|
+
"--json": "boolean",
|
|
212
|
+
"--help": "boolean",
|
|
213
|
+
"-h": "boolean",
|
|
214
|
+
};
|
|
215
|
+
function absolute(value, cwd) {
|
|
216
|
+
return isAbsolute(value) ? value : resolvePathSegments(cwd, value);
|
|
217
|
+
}
|
|
218
|
+
function usageError(streams, json, message) {
|
|
219
|
+
if (json)
|
|
220
|
+
streams.err(`${JSON.stringify({ error: { code: "usage", message } })}\n`);
|
|
221
|
+
else
|
|
222
|
+
streams.err(usageErrorText(message, UP_HELP));
|
|
223
|
+
return EXIT_USAGE;
|
|
224
|
+
}
|
|
225
|
+
function ioError(streams, json, message) {
|
|
226
|
+
if (json)
|
|
227
|
+
streams.err(`${JSON.stringify({ error: { code: "io", message } })}\n`);
|
|
228
|
+
else
|
|
229
|
+
streams.err(`approval: ${message}\n`);
|
|
230
|
+
return EXIT_IO;
|
|
231
|
+
}
|
|
232
|
+
// ---------------------------------------------------------------------------
|
|
233
|
+
// A cancellable wait
|
|
234
|
+
// ---------------------------------------------------------------------------
|
|
235
|
+
/**
|
|
236
|
+
* A backoff that a shutdown can cut short.
|
|
237
|
+
*
|
|
238
|
+
* Without this, SIGTERM during a sixty-second restart wait would be a minute of
|
|
239
|
+
* a process that has been asked to stop still being alive, which is exactly the
|
|
240
|
+
* behaviour an operator reads as "it hung".
|
|
241
|
+
*/
|
|
242
|
+
class Wait {
|
|
243
|
+
timer = null;
|
|
244
|
+
settle = null;
|
|
245
|
+
for(ms) {
|
|
246
|
+
return new Promise((resolve) => {
|
|
247
|
+
this.settle = resolve;
|
|
248
|
+
this.timer = setTimeout(() => {
|
|
249
|
+
this.timer = null;
|
|
250
|
+
this.settle = null;
|
|
251
|
+
resolve();
|
|
252
|
+
}, ms);
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
cancel() {
|
|
256
|
+
if (this.timer !== null)
|
|
257
|
+
clearTimeout(this.timer);
|
|
258
|
+
this.timer = null;
|
|
259
|
+
const settle = this.settle;
|
|
260
|
+
this.settle = null;
|
|
261
|
+
if (settle !== null)
|
|
262
|
+
settle();
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
// ---------------------------------------------------------------------------
|
|
266
|
+
// The verb
|
|
267
|
+
// ---------------------------------------------------------------------------
|
|
268
|
+
/**
|
|
269
|
+
* `approval up` (and `approval daemon run --with-channels`, which is the same
|
|
270
|
+
* function reached from the other spelling).
|
|
271
|
+
*/
|
|
272
|
+
export function commandUp(argv, streams, cwd) {
|
|
273
|
+
const json = argv.includes("--json");
|
|
274
|
+
const parsed = parseFlags(argv, UP_FLAGS);
|
|
275
|
+
if (!parsed.ok)
|
|
276
|
+
return usageError(streams, json, parsed.message);
|
|
277
|
+
if (boolFlag(parsed.flags, "--help") || boolFlag(parsed.flags, "-h")) {
|
|
278
|
+
streams.out(`${UP_HELP}\n`);
|
|
279
|
+
return EXIT_OK;
|
|
280
|
+
}
|
|
281
|
+
const extra = parsed.positionals[0];
|
|
282
|
+
if (extra !== undefined) {
|
|
283
|
+
return usageError(streams, json, `unexpected argument ${JSON.stringify(extra)}`);
|
|
284
|
+
}
|
|
285
|
+
const flags = parsed.flags;
|
|
286
|
+
const selectedGloss = parseGlossOptions(flags, true);
|
|
287
|
+
if (!selectedGloss.ok)
|
|
288
|
+
return usageError(streams, json, selectedGloss.message);
|
|
289
|
+
const interval = durationFlag(flags, "--interval", DEFAULT_INTERVAL_MS);
|
|
290
|
+
if (!interval.ok)
|
|
291
|
+
return usageError(streams, json, interval.message);
|
|
292
|
+
const debounce = durationFlag(flags, "--debounce", DEFAULT_DEBOUNCE_MS);
|
|
293
|
+
if (!debounce.ok)
|
|
294
|
+
return usageError(streams, json, debounce.message);
|
|
295
|
+
const backoff = durationFlag(flags, "--restart-backoff", UP_RESTART_BASE_MS);
|
|
296
|
+
if (!backoff.ok)
|
|
297
|
+
return usageError(streams, json, backoff.message);
|
|
298
|
+
const logPath = resolvePath(stringFlag(flags, "--log"), DEFAULT_LOG_PATH, cwd);
|
|
299
|
+
const queuePath = resolvePath(stringFlag(flags, "--out"), DEFAULT_QUEUE_PATH, cwd);
|
|
300
|
+
const tasksFlag = stringFlag(flags, "--tasks");
|
|
301
|
+
const tasksDir = resolvePath(tasksFlag, DEFAULT_TASKS_DIR, cwd);
|
|
302
|
+
// -------------------------------------------------------------------------
|
|
303
|
+
// The startup preflight (APRV-215)
|
|
304
|
+
// -------------------------------------------------------------------------
|
|
305
|
+
//
|
|
306
|
+
// First, before the log is even opened, because its whole subject is which
|
|
307
|
+
// code and which log this process is about to run against. It reads git, and
|
|
308
|
+
// at most fast-forwards, rebuilds, and clears an untracked `backlog/tasks/`
|
|
309
|
+
// file the incoming commit already contains out of the merge's way
|
|
310
|
+
// (APRV-300); it never resets, never stashes, and never touches the working
|
|
311
|
+
// log. A refusal exits without starting anything.
|
|
312
|
+
//
|
|
313
|
+
// `--json` emission goes through the same `UpEvent` union as everything else,
|
|
314
|
+
// so the stream stays one union of additive shapes. The refusal does NOT: it
|
|
315
|
+
// is an error object on stderr like every other refusal in this file, plus
|
|
316
|
+
// the facts, so a machine caller can branch on the code without parsing a
|
|
317
|
+
// runbook.
|
|
318
|
+
if (!boolFlag(flags, "--no-preflight")) {
|
|
319
|
+
const rootFlag = stringFlag(flags, "--root");
|
|
320
|
+
const cleared = startupPreflight({
|
|
321
|
+
logPath,
|
|
322
|
+
queuePath,
|
|
323
|
+
root: rootFlag === null ? null : absolute(rootFlag, cwd),
|
|
324
|
+
remote: stringFlag(flags, "--preflight-remote"),
|
|
325
|
+
branch: stringFlag(flags, "--preflight-base"),
|
|
326
|
+
build: !boolFlag(flags, "--no-build"),
|
|
327
|
+
json,
|
|
328
|
+
emit: (event) => {
|
|
329
|
+
if (json) {
|
|
330
|
+
const line = `${JSON.stringify(event)}\n`;
|
|
331
|
+
if (event.event === "preflight_warning")
|
|
332
|
+
streams.err(line);
|
|
333
|
+
else
|
|
334
|
+
streams.out(line);
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
const rendered = describePreflightEvent(event);
|
|
338
|
+
if (rendered.stderr)
|
|
339
|
+
streams.err(`${rendered.text}\n`);
|
|
340
|
+
else
|
|
341
|
+
streams.out(`${rendered.text}\n`);
|
|
342
|
+
},
|
|
343
|
+
refuse: (text) => streams.err(text),
|
|
344
|
+
});
|
|
345
|
+
// Exit 1, "the runtime decided", rather than 4: nothing failed to read or
|
|
346
|
+
// write, and a supervisor that read this as an I/O fault would retry a
|
|
347
|
+
// checkout state only a human can resolve.
|
|
348
|
+
if (!cleared.ok)
|
|
349
|
+
return EXIT_INTEGRITY;
|
|
350
|
+
// A rebuild happened, so THIS process is the stale build the preflight just
|
|
351
|
+
// replaced: Node loaded it at startup and no amount of new bytes on disk
|
|
352
|
+
// changes that. Carrying on here would start the writer on exactly the code
|
|
353
|
+
// the operator was trying to leave behind, which is the defect this
|
|
354
|
+
// preflight exists to remove. Become the fresh build, and exit as it exits.
|
|
355
|
+
if (cleared.reexec !== null)
|
|
356
|
+
return reexecFreshBuild(cleared.reexec, cwd);
|
|
357
|
+
}
|
|
358
|
+
const check = preflightLog(logPath);
|
|
359
|
+
if (!check.ok)
|
|
360
|
+
return ioError(streams, json, check.message);
|
|
361
|
+
// Same judgment `approval daemon run` makes: an explicitly named task folder
|
|
362
|
+
// that does not exist is a typo, and the default folder's absence is a
|
|
363
|
+
// legitimate log-only deployment that is warned about rather than refused.
|
|
364
|
+
if (!isDirectory(tasksDir)) {
|
|
365
|
+
if (tasksFlag !== null) {
|
|
366
|
+
return ioError(streams, json, `--tasks ${tasksDir} is not a directory; nothing would be watched for envelope drift`);
|
|
367
|
+
}
|
|
368
|
+
streams.err(`approval: task folder ${tasksDir} does not exist, so no envelope drift can be detected; the TTL sweep and the queue projection run anyway (pass --tasks <dir> if your task files live elsewhere)\n`);
|
|
369
|
+
}
|
|
370
|
+
const policyFlag = stringFlag(flags, "--policy");
|
|
371
|
+
const dirFlag = stringFlag(flags, "--dir");
|
|
372
|
+
const policy = policyFlag !== null
|
|
373
|
+
? { file: absolute(policyFlag, cwd) }
|
|
374
|
+
: { dir: dirFlag === null ? cwd : absolute(dirFlag, cwd) };
|
|
375
|
+
const load = loadPolicy(policy);
|
|
376
|
+
const once = boolFlag(flags, "--once");
|
|
377
|
+
const emit = (event) => {
|
|
378
|
+
if (json) {
|
|
379
|
+
const line = `${JSON.stringify(event)}\n`;
|
|
380
|
+
if (event.event === "part_unavailable" || event.event === "part_failed")
|
|
381
|
+
streams.err(line);
|
|
382
|
+
else
|
|
383
|
+
streams.out(line);
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
const rendered = describeUpEvent(event);
|
|
387
|
+
if (rendered.stderr)
|
|
388
|
+
streams.err(`${rendered.text}\n`);
|
|
389
|
+
else
|
|
390
|
+
streams.out(`${rendered.text}\n`);
|
|
391
|
+
};
|
|
392
|
+
// -------------------------------------------------------------------------
|
|
393
|
+
// Which parts can run
|
|
394
|
+
// -------------------------------------------------------------------------
|
|
395
|
+
const wantTelegram = !boolFlag(flags, "--no-telegram");
|
|
396
|
+
const wantWeb = !boolFlag(flags, "--no-web");
|
|
397
|
+
const portFlag = stringFlag(flags, "--port");
|
|
398
|
+
const payloadsFlag = stringFlag(flags, "--payloads");
|
|
399
|
+
const payloadDirFlag = stringFlag(flags, "--payload-dir");
|
|
400
|
+
const asFlag = stringFlag(flags, "--as");
|
|
401
|
+
/** Deferred so nothing is printed before the daemon's own `started` line. */
|
|
402
|
+
const unavailable = [];
|
|
403
|
+
const parts = ["daemon"];
|
|
404
|
+
let telegram = null;
|
|
405
|
+
if (wantTelegram) {
|
|
406
|
+
const gloss = glossRunnerFromOptions(selectedGloss.options, {
|
|
407
|
+
passphraseEnv: passphraseEnvFor(load),
|
|
408
|
+
diagnostic: (reason) => streams.err(`approval: Codex gloss unavailable (${reason}); continuing without it\n`),
|
|
409
|
+
});
|
|
410
|
+
const prepared = prepareListen({
|
|
411
|
+
logPath,
|
|
412
|
+
policy,
|
|
413
|
+
as: asFlag,
|
|
414
|
+
payloads: payloadsFlag === null ? null : absolute(payloadsFlag, cwd),
|
|
415
|
+
apiBase: stringFlag(flags, "--api-base"),
|
|
416
|
+
pollTimeout: stringFlag(flags, "--poll-timeout"),
|
|
417
|
+
once,
|
|
418
|
+
json,
|
|
419
|
+
log: (message) => streams.err(`${message}\n`),
|
|
420
|
+
// APRV-197: on by default, `--no-gloss` to turn it off, exactly as on
|
|
421
|
+
// `channel telegram listen` — this IS that listener, and a flag that
|
|
422
|
+
// meant different things on the two verbs that start it would be a trap.
|
|
423
|
+
// The reasoning is at `glossWiring` there.
|
|
424
|
+
//
|
|
425
|
+
// APRV-207: the subprocess is spawned starved, and the policy already
|
|
426
|
+
// loaded above names the passphrase variable the scrub must remove.
|
|
427
|
+
...(gloss === undefined ? {} : { gloss }),
|
|
428
|
+
});
|
|
429
|
+
if (prepared.ok) {
|
|
430
|
+
telegram = prepared.setup;
|
|
431
|
+
parts.push("telegram");
|
|
432
|
+
// APRV-178. The channel's credentials come from the launch environment
|
|
433
|
+
// and only from there, which is invariant 7 and stays true — but an
|
|
434
|
+
// exported value that this instance's own `.approval/env` disagrees with
|
|
435
|
+
// is how a demo gate spent an evening sending through the production bot.
|
|
436
|
+
// Said once, on stderr, before anything starts: a long-running process
|
|
437
|
+
// that is quietly holding another instance's bot token should say so at
|
|
438
|
+
// the moment it picks it up, not in a postmortem. It is a warning and not
|
|
439
|
+
// a refusal, because an operator who feeds the primary daemon from a
|
|
440
|
+
// shell profile is doing something deliberate and supported.
|
|
441
|
+
for (const finding of instanceFindings(logPath, load)) {
|
|
442
|
+
streams.err(`approval: cross-instance: ${finding.detail}\n`);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
else if (prepared.code === "poll-timeout" || prepared.code === "payloads-unreadable") {
|
|
446
|
+
// A mistyped command line, not an unconfigured machine. Refused here, the
|
|
447
|
+
// way every verb refuses one, rather than degraded into a missing channel
|
|
448
|
+
// an operator would have to notice in a log.
|
|
449
|
+
return prepared.code === "poll-timeout"
|
|
450
|
+
? usageError(streams, json, prepared.message)
|
|
451
|
+
: ioError(streams, json, prepared.message);
|
|
452
|
+
}
|
|
453
|
+
else {
|
|
454
|
+
unavailable.push({
|
|
455
|
+
event: "part_unavailable",
|
|
456
|
+
part: "telegram",
|
|
457
|
+
check: "telegram",
|
|
458
|
+
status: "skip",
|
|
459
|
+
detail: prepared.message,
|
|
460
|
+
fix: prepared.code === "no-identity"
|
|
461
|
+
? `approval setup identity — declare who the human is, then evaluate \`approval env\` in the environment this process is launched from (or export ${HUMAN_ACTOR_ENV} there yourself)`
|
|
462
|
+
: "approval setup channel telegram — store the bot token and discover the approver chat, then evaluate `approval env` in the environment this process is launched from",
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
const webPort = resolveWebPort(portFlag, policyWebPort(load));
|
|
467
|
+
if (!webPort.ok)
|
|
468
|
+
return usageError(streams, json, webPort.message);
|
|
469
|
+
const webActor = resolveHumanActor(asFlag === null ? {} : { actor: asFlag });
|
|
470
|
+
const webDeclared = portFlag !== null || policyWebPort(load) !== null;
|
|
471
|
+
let web = null;
|
|
472
|
+
if (wantWeb) {
|
|
473
|
+
if (once) {
|
|
474
|
+
// `--once` is one daemon tick and one poll cycle. A pull channel has no
|
|
475
|
+
// cycle: its page is fetched when a human chooses to look, which is why
|
|
476
|
+
// `approval channel web` has no `--once` either.
|
|
477
|
+
unavailable.push({
|
|
478
|
+
event: "part_unavailable",
|
|
479
|
+
part: "web",
|
|
480
|
+
check: "web-port",
|
|
481
|
+
status: "skip",
|
|
482
|
+
detail: "--once runs one daemon tick and one poll cycle, and the queue page is a pull channel with no cycle to run, so it is not served",
|
|
483
|
+
fix: null,
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
else if (!webDeclared) {
|
|
487
|
+
unavailable.push({
|
|
488
|
+
event: "part_unavailable",
|
|
489
|
+
part: "web",
|
|
490
|
+
check: "web-port",
|
|
491
|
+
status: "skip",
|
|
492
|
+
detail: "this policy declares no channels.web.port, so no queue page is served — which is a legitimate configuration and not a fault",
|
|
493
|
+
fix: "pass --port <n> to serve it for this run, or add channels.web.port to APPROVAL.md via approval policy amend (an edited policy is inoperative until it is re-attested)",
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
else if (webActor === null) {
|
|
497
|
+
unavailable.push({
|
|
498
|
+
event: "part_unavailable",
|
|
499
|
+
part: "web",
|
|
500
|
+
check: "web-port",
|
|
501
|
+
status: "skip",
|
|
502
|
+
detail: `no human identity: the queue page collects decisions and every one is recorded against ${HUMAN_ACTOR_ENV}, so a page whose buttons could record nothing is not served`,
|
|
503
|
+
fix: `approval setup identity — declare who the human is, then evaluate \`approval env\` in the environment this process is launched from (or export ${HUMAN_ACTOR_ENV} there yourself)`,
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
else {
|
|
507
|
+
web = { port: webPort.port, actor: webActor };
|
|
508
|
+
parts.push("web");
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
// -------------------------------------------------------------------------
|
|
512
|
+
// The daemon
|
|
513
|
+
// -------------------------------------------------------------------------
|
|
514
|
+
const options = {
|
|
515
|
+
logPath,
|
|
516
|
+
tasksDir,
|
|
517
|
+
queuePath,
|
|
518
|
+
policy,
|
|
519
|
+
cwd,
|
|
520
|
+
intervalMs: interval.ms,
|
|
521
|
+
debounceMs: debounce.ms,
|
|
522
|
+
once,
|
|
523
|
+
traceWatch: boolFlag(flags, "--trace-watch"),
|
|
524
|
+
sink: {
|
|
525
|
+
emit: (event) => {
|
|
526
|
+
if (json) {
|
|
527
|
+
const line = `${JSON.stringify(event)}\n`;
|
|
528
|
+
if (event.event === "warning")
|
|
529
|
+
streams.err(line);
|
|
530
|
+
else
|
|
531
|
+
streams.out(line);
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
const rendered = describeDaemonEvent(event);
|
|
535
|
+
if (rendered.stderr)
|
|
536
|
+
streams.err(`${rendered.text}\n`);
|
|
537
|
+
else
|
|
538
|
+
streams.out(`${rendered.text}\n`);
|
|
539
|
+
},
|
|
540
|
+
},
|
|
541
|
+
};
|
|
542
|
+
// The cadence advance (APRV-204), parsed by `daemon run`'s own function so a
|
|
543
|
+
// typo is refused in the same words on both spellings of this verb.
|
|
544
|
+
const cadence = advanceFlags(flags);
|
|
545
|
+
if (!cadence.ok)
|
|
546
|
+
return usageError(streams, json, cadence.message);
|
|
547
|
+
if (cadence.cadence !== null)
|
|
548
|
+
options.advance = cadence.cadence;
|
|
549
|
+
// The dark-session sweep (APRV-192), parsed by the same duration function for
|
|
550
|
+
// the same reason: one typo, one sentence, on both spellings of this verb.
|
|
551
|
+
const darkWindow = durationFlag(flags, "--dark-window", DEFAULT_DARK_WINDOW_MS);
|
|
552
|
+
if (!darkWindow.ok)
|
|
553
|
+
return usageError(streams, json, darkWindow.message);
|
|
554
|
+
const darkInterval = durationFlag(flags, "--dark-interval", DEFAULT_DARK_INTERVAL_MS);
|
|
555
|
+
if (!darkInterval.ok)
|
|
556
|
+
return usageError(streams, json, darkInterval.message);
|
|
557
|
+
if (boolFlag(flags, "--dark-sessions")) {
|
|
558
|
+
options.darkSessions = { windowMs: darkWindow.ms, intervalMs: darkInterval.ms };
|
|
559
|
+
}
|
|
560
|
+
// The prefix proof (APRV-217), parsed by `daemon run`'s own function so the
|
|
561
|
+
// flag beats the policy identically on both spellings of this verb.
|
|
562
|
+
const proof = readProofFlags(flags, policy);
|
|
563
|
+
if (!proof.ok)
|
|
564
|
+
return usageError(streams, json, proof.message);
|
|
565
|
+
options.readProof = proof.readProof;
|
|
566
|
+
// Process-wide too, for the reason `daemon run` sets it: the channels and the
|
|
567
|
+
// queue renderer in this process read the same log through paths that thread
|
|
568
|
+
// no options of their own.
|
|
569
|
+
useReadProof(proof.readProof);
|
|
570
|
+
// SPEC.md §8's optional git hardening, judged before the first tick exactly as
|
|
571
|
+
// `daemon run` judges it: an operator who asked for a second evidence layer and
|
|
572
|
+
// silently did not get one is worse off than one who was refused at startup.
|
|
573
|
+
if (boolFlag(flags, "--git-evidence")) {
|
|
574
|
+
const enabled = enableGitEvidence(logPath, (event) => {
|
|
575
|
+
if (json) {
|
|
576
|
+
const line = `${JSON.stringify(event)}\n`;
|
|
577
|
+
if (event.event === "git_evidence_failed")
|
|
578
|
+
streams.err(line);
|
|
579
|
+
else
|
|
580
|
+
streams.out(line);
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
const rendered = describeGitEvidence(event);
|
|
584
|
+
if (rendered.stderr)
|
|
585
|
+
streams.err(`${rendered.text}\n`);
|
|
586
|
+
else
|
|
587
|
+
streams.out(`${rendered.text}\n`);
|
|
588
|
+
});
|
|
589
|
+
if (!enabled.ok) {
|
|
590
|
+
if (json) {
|
|
591
|
+
streams.err(`${JSON.stringify({ error: { code: enabled.code, message: enabled.message } })}\n`);
|
|
592
|
+
}
|
|
593
|
+
else {
|
|
594
|
+
streams.err(`approval: ${enabled.message}\n`);
|
|
595
|
+
}
|
|
596
|
+
return enabled.code === "git-unavailable" || enabled.code === "log-dir-missing"
|
|
597
|
+
? EXIT_IO
|
|
598
|
+
: EXIT_USAGE;
|
|
599
|
+
}
|
|
600
|
+
options.gitEvidence = enabled.recorder;
|
|
601
|
+
}
|
|
602
|
+
// APRV-208. `approval up` is the ambient runtime an operator actually leaves
|
|
603
|
+
// running, so in practice it is the process that serves live draws. Same rule
|
|
604
|
+
// as `approval daemon run`: attempted when the sampling secret resolves in
|
|
605
|
+
// this process's environment, skipped with a line when it does not.
|
|
606
|
+
if (!boolFlag(flags, "--no-draw")) {
|
|
607
|
+
const draw = drawServerFor({ logPath, policy });
|
|
608
|
+
if (draw.ok)
|
|
609
|
+
options.draw = draw.server;
|
|
610
|
+
// Silent on `no-live-class`, for the reason `approval daemon run` states:
|
|
611
|
+
// a policy that declares nothing live has nothing to be told about.
|
|
612
|
+
else if (draw.reason !== "no-live-class") {
|
|
613
|
+
streams.err(`approval: live draws will not be served (${draw.reason}): ${draw.message} Every supervised-live action gates to a human until the sampling secret resolves in this process's own environment.\n`);
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
emit({ event: "up_started", parts, log: logPath });
|
|
617
|
+
for (const event of unavailable)
|
|
618
|
+
emit(event);
|
|
619
|
+
const daemon = new Daemon(options);
|
|
620
|
+
// -------------------------------------------------------------------------
|
|
621
|
+
// Supervision
|
|
622
|
+
// -------------------------------------------------------------------------
|
|
623
|
+
let shuttingDown = false;
|
|
624
|
+
let stopReason = "once";
|
|
625
|
+
let listener = null;
|
|
626
|
+
/** Set while a web server is up: resolving it lets that part close and return. */
|
|
627
|
+
let stopWeb = null;
|
|
628
|
+
const waits = new Set();
|
|
629
|
+
/** Wait, unless a shutdown has already begun or begins while waiting. */
|
|
630
|
+
const pause = async (ms) => {
|
|
631
|
+
const wait = new Wait();
|
|
632
|
+
waits.add(wait);
|
|
633
|
+
try {
|
|
634
|
+
if (shuttingDown)
|
|
635
|
+
return;
|
|
636
|
+
await wait.for(ms);
|
|
637
|
+
}
|
|
638
|
+
finally {
|
|
639
|
+
waits.delete(wait);
|
|
640
|
+
}
|
|
641
|
+
};
|
|
642
|
+
const telegramPart = async (setup) => {
|
|
643
|
+
// `detail` is optional on the contract, and a health report that declines to
|
|
644
|
+
// elaborate is not a fault. The fallback keeps `part_started` a fixed shape.
|
|
645
|
+
emit({
|
|
646
|
+
event: "part_started",
|
|
647
|
+
part: "telegram",
|
|
648
|
+
detail: setup.channel.health().detail ?? "configured",
|
|
649
|
+
});
|
|
650
|
+
let attempts = 0;
|
|
651
|
+
for (;;) {
|
|
652
|
+
const startedAt = Date.now();
|
|
653
|
+
const running = startListener(setup, streams);
|
|
654
|
+
listener = running;
|
|
655
|
+
let message;
|
|
656
|
+
try {
|
|
657
|
+
const outcome = await running.done;
|
|
658
|
+
if (outcome.kind === "stopped") {
|
|
659
|
+
emit({
|
|
660
|
+
event: "part_stopped",
|
|
661
|
+
part: "telegram",
|
|
662
|
+
reason: shuttingDown ? stopReason : setup.once ? "once" : "listener stopped",
|
|
663
|
+
});
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
666
|
+
message =
|
|
667
|
+
outcome.kind === "send-failed"
|
|
668
|
+
? `telegram sendMessage failed: ${outcome.message}`
|
|
669
|
+
: `the pending queue could not be derived (${outcome.code}): ${outcome.message}`;
|
|
670
|
+
}
|
|
671
|
+
catch (cause) {
|
|
672
|
+
message = cause instanceof Error ? cause.message : String(cause);
|
|
673
|
+
}
|
|
674
|
+
finally {
|
|
675
|
+
listener = null;
|
|
676
|
+
}
|
|
677
|
+
if (shuttingDown || setup.once) {
|
|
678
|
+
emit({
|
|
679
|
+
event: "part_stopped",
|
|
680
|
+
part: "telegram",
|
|
681
|
+
reason: shuttingDown ? stopReason : "once",
|
|
682
|
+
});
|
|
683
|
+
return;
|
|
684
|
+
}
|
|
685
|
+
// A part that ran for a while and then fell over is a fresh failure, not
|
|
686
|
+
// the next rung of a crash loop.
|
|
687
|
+
attempts = Date.now() - startedAt >= UP_STABLE_MS ? 1 : attempts + 1;
|
|
688
|
+
const delay = Math.min(backoff.ms * 2 ** (attempts - 1), UP_RESTART_MAX_MS);
|
|
689
|
+
emit({
|
|
690
|
+
event: "part_failed",
|
|
691
|
+
part: "telegram",
|
|
692
|
+
attempt: attempts,
|
|
693
|
+
message,
|
|
694
|
+
restart_in_ms: delay,
|
|
695
|
+
});
|
|
696
|
+
await pause(delay);
|
|
697
|
+
if (shuttingDown) {
|
|
698
|
+
emit({ event: "part_stopped", part: "telegram", reason: stopReason });
|
|
699
|
+
return;
|
|
700
|
+
}
|
|
701
|
+
emit({ event: "part_restarted", part: "telegram", attempt: attempts });
|
|
702
|
+
}
|
|
703
|
+
};
|
|
704
|
+
const webPart = async (settings) => {
|
|
705
|
+
let attempts = 0;
|
|
706
|
+
for (;;) {
|
|
707
|
+
let running;
|
|
708
|
+
try {
|
|
709
|
+
running = await startWebChannel({
|
|
710
|
+
logPath,
|
|
711
|
+
actor: settings.actor,
|
|
712
|
+
policy,
|
|
713
|
+
port: settings.port,
|
|
714
|
+
...(payloadDirFlag === null
|
|
715
|
+
? {}
|
|
716
|
+
: {
|
|
717
|
+
payload: webPayloadSource(absolute(payloadDirFlag, cwd), (message) => streams.err(`${message}\n`)),
|
|
718
|
+
}),
|
|
719
|
+
log: (message) => streams.err(`${message}\n`),
|
|
720
|
+
});
|
|
721
|
+
}
|
|
722
|
+
catch (cause) {
|
|
723
|
+
if (shuttingDown)
|
|
724
|
+
return;
|
|
725
|
+
attempts += 1;
|
|
726
|
+
const delay = Math.min(backoff.ms * 2 ** (attempts - 1), UP_RESTART_MAX_MS);
|
|
727
|
+
emit({
|
|
728
|
+
event: "part_failed",
|
|
729
|
+
part: "web",
|
|
730
|
+
attempt: attempts,
|
|
731
|
+
message: `the web channel could not bind 127.0.0.1:${String(settings.port)}: ${cause instanceof Error ? cause.message : String(cause)}`,
|
|
732
|
+
restart_in_ms: delay,
|
|
733
|
+
});
|
|
734
|
+
await pause(delay);
|
|
735
|
+
if (shuttingDown) {
|
|
736
|
+
emit({ event: "part_stopped", part: "web", reason: stopReason });
|
|
737
|
+
return;
|
|
738
|
+
}
|
|
739
|
+
emit({ event: "part_restarted", part: "web", attempt: attempts });
|
|
740
|
+
continue;
|
|
741
|
+
}
|
|
742
|
+
const url = `http://127.0.0.1:${String(running.port)}/`;
|
|
743
|
+
emit({
|
|
744
|
+
event: "part_started",
|
|
745
|
+
part: "web",
|
|
746
|
+
detail: `the pending queue is at ${url} (loopback only, NO authentication: anyone with access to this machine can decide as ${settings.actor})`,
|
|
747
|
+
});
|
|
748
|
+
// `channel web`'s own startup line, verbatim, so a supervisor already
|
|
749
|
+
// parsing that stream reads the same object out of this one.
|
|
750
|
+
if (json) {
|
|
751
|
+
streams.out(`${JSON.stringify({
|
|
752
|
+
event: "listening",
|
|
753
|
+
channel: "web",
|
|
754
|
+
url,
|
|
755
|
+
host: "127.0.0.1",
|
|
756
|
+
port: running.port,
|
|
757
|
+
actor: settings.actor,
|
|
758
|
+
})}\n`);
|
|
759
|
+
}
|
|
760
|
+
// The server outlives this await only through a shutdown or its own
|
|
761
|
+
// error: an HTTP server that is listening does not stop on its own.
|
|
762
|
+
const failure = await new Promise((resolve) => {
|
|
763
|
+
const onError = (cause) => resolve(cause);
|
|
764
|
+
running.server.once("error", onError);
|
|
765
|
+
const settle = () => {
|
|
766
|
+
running.server.off("error", onError);
|
|
767
|
+
resolve(null);
|
|
768
|
+
};
|
|
769
|
+
if (shuttingDown)
|
|
770
|
+
settle();
|
|
771
|
+
else
|
|
772
|
+
stopWeb = settle;
|
|
773
|
+
});
|
|
774
|
+
stopWeb = null;
|
|
775
|
+
if (failure === null || shuttingDown) {
|
|
776
|
+
if (json)
|
|
777
|
+
streams.out(`${JSON.stringify({ event: "stopped", ...running.channel.stats() })}\n`);
|
|
778
|
+
emit({ event: "part_stopped", part: "web", reason: shuttingDown ? stopReason : "closed" });
|
|
779
|
+
await running.close().catch(() => undefined);
|
|
780
|
+
return;
|
|
781
|
+
}
|
|
782
|
+
await running.close().catch(() => undefined);
|
|
783
|
+
attempts += 1;
|
|
784
|
+
const delay = Math.min(backoff.ms * 2 ** (attempts - 1), UP_RESTART_MAX_MS);
|
|
785
|
+
emit({
|
|
786
|
+
event: "part_failed",
|
|
787
|
+
part: "web",
|
|
788
|
+
attempt: attempts,
|
|
789
|
+
message: failure.message,
|
|
790
|
+
restart_in_ms: delay,
|
|
791
|
+
});
|
|
792
|
+
await pause(delay);
|
|
793
|
+
if (shuttingDown) {
|
|
794
|
+
emit({ event: "part_stopped", part: "web", reason: stopReason });
|
|
795
|
+
return;
|
|
796
|
+
}
|
|
797
|
+
emit({ event: "part_restarted", part: "web", attempt: attempts });
|
|
798
|
+
}
|
|
799
|
+
};
|
|
800
|
+
const stopAll = (reason) => {
|
|
801
|
+
if (shuttingDown)
|
|
802
|
+
return;
|
|
803
|
+
shuttingDown = true;
|
|
804
|
+
stopReason = reason;
|
|
805
|
+
daemon.stop(reason);
|
|
806
|
+
listener?.stop();
|
|
807
|
+
stopWeb?.();
|
|
808
|
+
for (const wait of waits)
|
|
809
|
+
wait.cancel();
|
|
810
|
+
};
|
|
811
|
+
const onSignal = (signal) => stopAll(signal);
|
|
812
|
+
process.on("SIGINT", onSignal);
|
|
813
|
+
process.on("SIGTERM", onSignal);
|
|
814
|
+
const channelParts = [];
|
|
815
|
+
if (telegram !== null)
|
|
816
|
+
channelParts.push(telegramPart(telegram));
|
|
817
|
+
if (web !== null)
|
|
818
|
+
channelParts.push(webPart(web));
|
|
819
|
+
return daemon
|
|
820
|
+
.run()
|
|
821
|
+
.then(async (outcome) => {
|
|
822
|
+
// `--once` is one tick AND one poll cycle: the daemon's tick finishes in
|
|
823
|
+
// milliseconds and the channel's poll takes as long as the long poll does,
|
|
824
|
+
// so the fast part waits for the slow one rather than cutting it short.
|
|
825
|
+
if (once && outcome.kind === "stopped")
|
|
826
|
+
await Promise.all(channelParts);
|
|
827
|
+
// The daemon settles the verb. A failure of ITS OWN — an unreadable log, a
|
|
828
|
+
// torn tail, a chain that does not verify — stops the channels too: a
|
|
829
|
+
// queue page and a chat prompt derived from a log nobody could verify
|
|
830
|
+
// would be a statement to a human about facts the runtime disowns.
|
|
831
|
+
stopAll(outcome.kind === "stopped" ? outcome.reason : outcome.kind);
|
|
832
|
+
await Promise.all(channelParts);
|
|
833
|
+
emit({ event: "up_stopped", reason: stopReason });
|
|
834
|
+
if (outcome.kind !== "stopped") {
|
|
835
|
+
if (json) {
|
|
836
|
+
streams.err(`${JSON.stringify({ error: { code: outcome.kind, message: outcome.message } })}\n`);
|
|
837
|
+
}
|
|
838
|
+
else {
|
|
839
|
+
streams.err(`approval: ${outcome.message}\n`);
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
return exitForDaemonOutcome(outcome);
|
|
843
|
+
})
|
|
844
|
+
.finally(() => {
|
|
845
|
+
process.off("SIGINT", onSignal);
|
|
846
|
+
process.off("SIGTERM", onSignal);
|
|
847
|
+
});
|
|
848
|
+
}
|
|
849
|
+
//# sourceMappingURL=up.js.map
|