approval-md 0.1.0 → 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/README.md +584 -553
- package/SPEC.md +42 -13
- package/dist/src/adapters/agentmail.d.ts +426 -0
- package/dist/src/adapters/agentmail.js +2 -2
- package/dist/src/adapters/conformance.d.ts +149 -0
- package/dist/src/adapters/contract.d.ts +628 -0
- package/dist/src/adapters/contract.js +110 -16
- package/dist/src/adapters/contract.js.map +1 -1
- package/dist/src/adapters/email.d.ts +324 -0
- package/dist/src/adapters/env-passphrase.d.ts +93 -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 +2 -1
- package/dist/src/adapters/registry.js.map +1 -1
- package/dist/src/adapters/smtp.d.ts +213 -0
- package/dist/src/adapters/vault-provider.d.ts +114 -0
- package/dist/src/adapters/vault-provider.js +3 -3
- 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/cli.d.ts +193 -0
- package/dist/src/channels/conformance.d.ts +92 -0
- package/dist/src/channels/contract.d.ts +623 -0
- package/dist/src/channels/payload-view.d.ts +35 -0
- package/dist/src/channels/render-queue.d.ts +149 -0
- package/dist/src/channels/tagging.d.ts +196 -0
- package/dist/src/channels/telegram.d.ts +1832 -0
- package/dist/src/channels/web.d.ts +341 -0
- package/dist/src/cli/adapter.d.ts +90 -0
- package/dist/src/cli/adapter.js +25 -15
- package/dist/src/cli/adapter.js.map +1 -1
- package/dist/src/cli/amend.d.ts +59 -0
- package/dist/src/cli/args.d.ts +43 -0
- package/dist/src/cli/attest.d.ts +41 -0
- package/dist/src/cli/audit-card.d.ts +62 -0
- package/dist/src/cli/audit.d.ts +59 -0
- package/dist/src/cli/channel-telegram.d.ts +806 -0
- package/dist/src/cli/channel-web.d.ts +131 -0
- package/dist/src/cli/channel.d.ts +71 -0
- package/dist/src/cli/checkpoint-tap.d.ts +169 -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/daemon.d.ts +120 -0
- package/dist/src/cli/doctor.d.ts +129 -0
- package/dist/src/cli/doctor.js +119 -5
- package/dist/src/cli/doctor.js.map +1 -1
- package/dist/src/cli/env.d.ts +65 -0
- package/dist/src/cli/execute.d.ts +202 -0
- package/dist/src/cli/exit-codes.d.ts +73 -0
- package/dist/src/cli/feedback.d.ts +60 -0
- package/dist/src/cli/gate-window.d.ts +40 -0
- package/dist/src/cli/gate.d.ts +68 -0
- package/dist/src/cli/git-scope.d.ts +190 -0
- package/dist/src/cli/gloss-attach.d.ts +85 -0
- package/dist/src/cli/gloss-codex-child.d.ts +9 -0
- package/dist/src/cli/gloss-codex.d.ts +24 -0
- package/dist/src/cli/gloss-options.d.ts +42 -0
- package/dist/src/cli/gloss.d.ts +265 -0
- package/dist/src/cli/help.d.ts +103 -0
- package/dist/src/cli/help.js +173 -51
- package/dist/src/cli/help.js.map +1 -1
- 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 +186 -80
- package/dist/src/cli/hook.js.map +1 -1
- package/dist/src/cli/import.d.ts +35 -0
- package/dist/src/cli/init.d.ts +84 -0
- package/dist/src/cli/init.js +2 -2
- package/dist/src/cli/init.js.map +1 -1
- package/dist/src/cli/instructions.d.ts +23 -0
- package/dist/src/cli/journal.d.ts +41 -0
- package/dist/src/cli/log-advance.d.ts +287 -0
- package/dist/src/cli/log-advance.js +102 -11
- package/dist/src/cli/log-advance.js.map +1 -1
- package/dist/src/cli/log-anchor.d.ts +176 -0
- package/dist/src/cli/log-checkpoint.d.ts +22 -0
- package/dist/src/cli/log-sync.d.ts +243 -0
- package/dist/src/cli/log-verbs.d.ts +16 -0
- package/dist/src/cli/log-verbs.js +7 -1
- package/dist/src/cli/log-verbs.js.map +1 -1
- package/dist/src/cli/long-help.d.ts +70 -0
- package/dist/src/cli/main.d.ts +77 -0
- package/dist/src/cli/main.js +155 -5
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/cli/mcp.d.ts +52 -0
- package/dist/src/cli/paths.d.ts +56 -0
- package/dist/src/cli/payload.d.ts +58 -0
- package/dist/src/cli/policy.d.ts +43 -0
- package/dist/src/cli/preflight.d.ts +363 -0
- package/dist/src/cli/preflight.js +294 -7
- package/dist/src/cli/preflight.js.map +1 -1
- package/dist/src/cli/progress.d.ts +78 -0
- package/dist/src/cli/prompt.d.ts +209 -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/render.d.ts +22 -0
- package/dist/src/cli/sandbox.d.ts +51 -0
- package/dist/src/cli/scaffold.d.ts +79 -0
- package/dist/src/cli/setup-adapter.d.ts +137 -0
- package/dist/src/cli/setup-adapter.js +38 -4
- package/dist/src/cli/setup-adapter.js.map +1 -1
- package/dist/src/cli/setup-channel.d.ts +117 -0
- package/dist/src/cli/setup-checkpoint.d.ts +57 -0
- package/dist/src/cli/setup-common.d.ts +275 -0
- package/dist/src/cli/setup-flow.d.ts +287 -0
- package/dist/src/cli/setup-service.d.ts +96 -0
- package/dist/src/cli/setup.d.ts +202 -0
- package/dist/src/cli/style.d.ts +320 -0
- package/dist/src/cli/token.d.ts +39 -0
- package/dist/src/cli/up.d.ts +155 -0
- package/dist/src/cli/up.js +4 -2
- package/dist/src/cli/up.js.map +1 -1
- package/dist/src/cli/usage.d.ts +37 -0
- package/dist/src/cli/values.d.ts +40 -0
- package/dist/src/cli/vault.d.ts +59 -0
- package/dist/src/cli/vault.js +2 -2
- package/dist/src/cli/vault.js.map +1 -1
- package/dist/src/cli/verb-registry.d.ts +76 -0
- package/dist/src/cli/verb-registry.js +176 -8
- package/dist/src/cli/verb-registry.js.map +1 -1
- package/dist/src/cli/wordmark.d.ts +31 -0
- package/dist/src/cli/wordmark.js +2 -2
- 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/agents-md.d.ts +276 -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 +13 -1
- package/dist/src/core/attest.js.map +1 -1
- package/dist/src/core/audit.d.ts +492 -0
- package/dist/src/core/budgets.d.ts +238 -0
- package/dist/src/core/checkpoint.d.ts +500 -0
- package/dist/src/core/child-env.d.ts +88 -0
- package/dist/src/core/clock.d.ts +52 -0
- package/dist/src/core/command-class.d.ts +543 -0
- package/dist/src/core/command-class.js +43 -8
- package/dist/src/core/command-class.js.map +1 -1
- package/dist/src/core/coverage-sources/adapter.d.ts +40 -0
- package/dist/src/core/coverage-sources/gh.d.ts +48 -0
- package/dist/src/core/coverage-sources/git.d.ts +101 -0
- package/dist/src/core/coverage.d.ts +217 -0
- package/dist/src/core/credential-spec.d.ts +72 -0
- package/dist/src/core/dark-session.d.ts +331 -0
- package/dist/src/core/decision-refusal.d.ts +185 -0
- package/dist/src/core/env-file.d.ts +450 -0
- package/dist/src/core/execute.d.ts +858 -0
- package/dist/src/core/execute.js +44 -6
- package/dist/src/core/execute.js.map +1 -1
- package/dist/src/core/frontmatter.d.ts +78 -0
- package/dist/src/core/gate-window.d.ts +312 -0
- package/dist/src/core/gate.d.ts +1364 -0
- package/dist/src/core/gate.js +68 -13
- package/dist/src/core/gate.js.map +1 -1
- package/dist/src/core/git-run.d.ts +73 -0
- package/dist/src/core/harness-version.d.ts +157 -0
- package/dist/src/core/harness-version.js +2 -1
- package/dist/src/core/harness-version.js.map +1 -1
- package/dist/src/core/harness-wait.d.ts +55 -0
- package/dist/src/core/head-retry.d.ts +107 -0
- package/dist/src/core/instance.d.ts +253 -0
- package/dist/src/core/intake-limits.d.ts +247 -0
- package/dist/src/core/jcs.d.ts +52 -0
- package/dist/src/core/journal.d.ts +144 -0
- package/dist/src/core/live-draw.d.ts +436 -0
- package/dist/src/core/log-reconcile.d.ts +89 -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/loop.d.ts +274 -0
- package/dist/src/core/loop.js +11 -0
- package/dist/src/core/loop.js.map +1 -1
- package/dist/src/core/md-fence.d.ts +41 -0
- package/dist/src/core/money.d.ts +147 -0
- package/dist/src/core/payload-census.d.ts +74 -0
- package/dist/src/core/payload-store.d.ts +175 -0
- package/dist/src/core/payload.d.ts +71 -0
- package/dist/src/core/policy-diff.d.ts +292 -0
- package/dist/src/core/policy-diff.js +27 -4
- package/dist/src/core/policy-diff.js.map +1 -1
- package/dist/src/core/policy-expectations.d.ts +199 -0
- package/dist/src/core/policy-explain.d.ts +150 -0
- package/dist/src/core/policy-explain.js +31 -3
- package/dist/src/core/policy-explain.js.map +1 -1
- package/dist/src/core/policy-load.d.ts +527 -0
- package/dist/src/core/policy-load.js +15 -3
- package/dist/src/core/policy-load.js.map +1 -1
- package/dist/src/core/policy-match.d.ts +281 -0
- package/dist/src/core/policy-match.js +20 -9
- package/dist/src/core/policy-match.js.map +1 -1
- package/dist/src/core/policy-proposal.d.ts +265 -0
- package/dist/src/core/prompt-layout.d.ts +221 -0
- package/dist/src/core/protected-path-guard.d.ts +453 -0
- package/dist/src/core/protected-path-guard.js +514 -35
- package/dist/src/core/protected-path-guard.js.map +1 -1
- package/dist/src/core/registration.d.ts +25 -0
- package/dist/src/core/reindex.d.ts +99 -0
- package/dist/src/core/sampler.d.ts +313 -0
- package/dist/src/core/sandbox.d.ts +290 -0
- package/dist/src/core/seal.d.ts +165 -0
- package/dist/src/core/state.d.ts +505 -0
- package/dist/src/core/task-file.d.ts +185 -0
- package/dist/src/core/telegram-config.d.ts +93 -0
- package/dist/src/core/token.d.ts +409 -0
- package/dist/src/core/token.js +21 -38
- package/dist/src/core/token.js.map +1 -1
- package/dist/src/core/validate.d.ts +138 -0
- package/dist/src/core/values.d.ts +137 -0
- package/dist/src/core/vault.d.ts +291 -0
- package/dist/src/core/verified-snapshot.d.ts +204 -0
- package/dist/src/core/verify.d.ts +336 -0
- package/dist/src/core/version.d.ts +8 -0
- package/dist/src/core/wysiwys.d.ts +370 -0
- package/dist/src/daemon/advance-child.d.ts +39 -0
- package/dist/src/daemon/advance.d.ts +466 -0
- package/dist/src/daemon/audit.d.ts +87 -0
- package/dist/src/daemon/daemon.d.ts +1180 -0
- package/dist/src/daemon/dark-session.d.ts +64 -0
- package/dist/src/daemon/draw-child.d.ts +36 -0
- package/dist/src/daemon/draw.d.ts +154 -0
- package/dist/src/daemon/git-evidence.d.ts +173 -0
- package/dist/src/daemon/git-evidence.js +1 -1
- package/dist/src/daemon/projection.d.ts +180 -0
- package/dist/src/daemon/prune.d.ts +207 -0
- package/dist/src/mcp/http.d.ts +113 -0
- package/dist/src/mcp/server.d.ts +265 -0
- package/dist/src/mcp/server.js +9 -1
- package/dist/src/mcp/server.js.map +1 -1
- package/docs/adapter-api.md +106 -0
- package/docs/cli-reference.md +389 -36
- package/docs/codex-enforced-session.md +30 -0
- package/package.json +12 -2
- package/schema/codex-instance.schema.json +82 -0
- package/schema/event.schema.json +2 -1
- package/schema/fixtures/codex-instance/invalid/unpinned-codex-version.json +40 -0
- package/schema/fixtures/codex-instance/valid/canonical.json +40 -0
- package/schema/policy.schema.json +21 -1
- package/templates/codex/README.md +9 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `approval setup` — the interactive configuration family (SPEC.md §5.2, §10.1).
|
|
3
|
+
*
|
|
4
|
+
* APRV-73 gave `.approval/env` a format and `approval env` a reader. This verb
|
|
5
|
+
* is the writer, and it is the only one: it establishes the things an operator
|
|
6
|
+
* must have before any gate operation works — a declared human identity, a
|
|
7
|
+
* vault passphrase, a sampling secret, a live Telegram bot and chat, and an
|
|
8
|
+
* adapter's credentials — by putting each VALUE where that kind of value
|
|
9
|
+
* belongs and each SOURCE in `.approval/env`.
|
|
10
|
+
*
|
|
11
|
+
* ## Who lives where
|
|
12
|
+
*
|
|
13
|
+
* This file holds the three subcommands whose subject is a value this runtime
|
|
14
|
+
* MINTS or a name it records, plus the dispatch:
|
|
15
|
+
*
|
|
16
|
+
* ```
|
|
17
|
+
* setup identity # APPROVAL_HUMAN, in this file
|
|
18
|
+
* setup vault # the vault passphrase, in this file
|
|
19
|
+
* setup sampling # the audit sampling secret, in this file
|
|
20
|
+
* setup checkpoint # cli/setup-checkpoint.ts (the vault + a print)
|
|
21
|
+
* setup channel <name> # cli/setup-channel.ts (keystore + .approval/env)
|
|
22
|
+
* setup adapter <name> # cli/setup-adapter.ts (the vault)
|
|
23
|
+
* setup service # cli/setup-service.ts (a launchd/systemd unit)
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* The last two are **two nouns and not one list**, and the split is SPEC.md
|
|
27
|
+
* §4's: a CHANNEL surfaces requests and collects decisions and holds no state,
|
|
28
|
+
* so its setup fills the keystore and `.approval/env`; an ADAPTER executes side
|
|
29
|
+
* effects and holds credentials, so its setup fills the vault.
|
|
30
|
+
* An older build spelled the Telegram one without the `channel` noun, and that
|
|
31
|
+
* form is gone (APRV-79): the dispatch answers it with the new one and exits 2
|
|
32
|
+
* rather than aliasing it, because an alias would leave two spellings of a
|
|
33
|
+
* distinction the SPEC draws on purpose.
|
|
34
|
+
*
|
|
35
|
+
* Everything the three files share — the dependency bag, the keystore seam, the
|
|
36
|
+
* front matter, the human-only gate, the service names, the plaintext-literal
|
|
37
|
+
* offer — is `cli/setup-common.ts`, which imports from none of them. The
|
|
38
|
+
* conversation `setup channel|adapter` both run is `cli/setup-flow.ts`.
|
|
39
|
+
*
|
|
40
|
+
* ## The order these run in
|
|
41
|
+
*
|
|
42
|
+
* Nothing enforced it and nothing said it, which APRV-76 noticed the hard way.
|
|
43
|
+
* It is:
|
|
44
|
+
*
|
|
45
|
+
* ```
|
|
46
|
+
* approval init # the directory and the .gitignore
|
|
47
|
+
* (write APPROVAL.md) # the policy NAMES every variable below
|
|
48
|
+
* approval setup identity # APPROVAL_HUMAN
|
|
49
|
+
* approval setup vault # the passphrase, into the keystore
|
|
50
|
+
* eval "$(approval env)" # the ONLY thing that puts them in a shell
|
|
51
|
+
* approval setup adapter <name> # the adapter's credentials, into the vault
|
|
52
|
+
* ```
|
|
53
|
+
*
|
|
54
|
+
* The policy comes before every `setup`, because each of them reads variable
|
|
55
|
+
* NAMES out of it. The `eval` comes before `setup adapter`, because that
|
|
56
|
+
* subcommand needs the passphrase's VALUE in the environment and will not read
|
|
57
|
+
* `.approval/env` to get it (§11.1 invariant 7). `setup sampling` and
|
|
58
|
+
* `setup channel telegram` slot in anywhere after the policy.
|
|
59
|
+
*
|
|
60
|
+
* ## What this verb is not allowed to do
|
|
61
|
+
*
|
|
62
|
+
* **It never appends to the log, never attests, and never edits `APPROVAL.md`.**
|
|
63
|
+
* Configuration is not an authorized action, and the log is the record of
|
|
64
|
+
* authorized actions; a "telegram configured" event would be a line in the one
|
|
65
|
+
* file this project promises never to rewrite, saying something the log has no
|
|
66
|
+
* business knowing. `tests/cli-setup.test.ts` byte-compares `events.jsonl`
|
|
67
|
+
* across a complete run of every subcommand to keep that true by assertion.
|
|
68
|
+
* When a policy line is needed (the sampling secret's name), this verb PRINTS
|
|
69
|
+
* the `approval policy amend` invocation and stops: an amendment is a human
|
|
70
|
+
* ceremony with an attestation at the end of it, and a setup wizard that
|
|
71
|
+
* silently edited an attested policy would be forging the sign-off.
|
|
72
|
+
*
|
|
73
|
+
* It writes exactly three things: lines in `.approval/env`, through a writer
|
|
74
|
+
* that preserves every other line and comment (`core/env-file.ts`), items in
|
|
75
|
+
* the OS keystore, and entries in the vault (`setup adapter` only).
|
|
76
|
+
*
|
|
77
|
+
* ## Interactive or nothing
|
|
78
|
+
*
|
|
79
|
+
* Every subcommand refuses when stdin is not a terminal, or when `--json` was
|
|
80
|
+
* given, and exits 2 printing THE EXACT NON-INTERACTIVE ALTERNATIVE — the
|
|
81
|
+
* `security add-generic-password` line to run, or the `.approval/env` line to
|
|
82
|
+
* add, or the `export` to put in a shell profile. This is not a wizard being
|
|
83
|
+
* precious about its terminal. A `setup` that could be driven from a pipe would
|
|
84
|
+
* be a way for a CI job or an agent to write `APPROVAL_HUMAN` and a keystore
|
|
85
|
+
* item, which is precisely the boundary §11 draws: identity is config-declared,
|
|
86
|
+
* so establishing it must be an act of the human at the machine. The refusal
|
|
87
|
+
* text is the documented scripted path, so nobody has to reverse-engineer one.
|
|
88
|
+
*
|
|
89
|
+
* `setup identity` is EXEMPT from the human-only `--as` gate that every other
|
|
90
|
+
* subcommand carries, and the exemption is not a hole: identity is what that
|
|
91
|
+
* gate reads. A verb that demanded `APPROVAL_HUMAN` before it would let you set
|
|
92
|
+
* `APPROVAL_HUMAN` could only ever be run by someone who did not need it. The
|
|
93
|
+
* control on this path is the terminal itself.
|
|
94
|
+
*
|
|
95
|
+
* ## Answers, and answering again (APRV-90)
|
|
96
|
+
*
|
|
97
|
+
* Every typed question in this family runs through `prompt.ts`'s `askUntil`: a
|
|
98
|
+
* wrong answer is one line saying what was wrong and the same question again,
|
|
99
|
+
* never an exit code with a help page under it. `setup identity` also
|
|
100
|
+
* NORMALISES what it is given — `carter` is recorded as `human:carter`, and
|
|
101
|
+
* `human:carter` is taken as it stands ({@link identityFromAnswer}). The prompt
|
|
102
|
+
* still prints the `human:` prefix, because the prefix is what distinguishes
|
|
103
|
+
* the actor kinds the human-only verbs refuse, but nobody has to retype a
|
|
104
|
+
* prefix the question already showed them. `agent:` and `system:` are refused
|
|
105
|
+
* with the sentence that names why, as a reason to answer again.
|
|
106
|
+
*
|
|
107
|
+
* ## Where a secret goes, and how it gets there
|
|
108
|
+
*
|
|
109
|
+
* Three service names, one per secret, documented so an operator can find them
|
|
110
|
+
* with `security find-generic-password` or `secret-tool lookup` by hand:
|
|
111
|
+
* `approval-tg-token`, `approval-vault-passphrase`, `approval-sampling-secret`.
|
|
112
|
+
*
|
|
113
|
+
* - **macOS** (`darwin` and `security` on PATH) → `keychain:<service>`;
|
|
114
|
+
* - **Linux with `secret-tool`** → `secret-service:<service>` (the same string
|
|
115
|
+
* is the label, so the two platforms name one secret one way);
|
|
116
|
+
* - **neither** → the operator is OFFERED a plaintext literal in
|
|
117
|
+
* `.approval/env`, and must type `yes` in full to take it, having been shown
|
|
118
|
+
* the same warning `approval env --check` will print at them forever after.
|
|
119
|
+
* §5.2 permits literals for a stated reason, and refusing here would only
|
|
120
|
+
* move the value into a shell profile where nothing can see it to report it.
|
|
121
|
+
*
|
|
122
|
+
* **A value the operator already holds is never handled by this process.** The
|
|
123
|
+
* Telegram token on macOS is collected by `security`'s OWN no-echo prompt: we
|
|
124
|
+
* spawn `security add-generic-password … -w` WITH NO VALUE and with inherited
|
|
125
|
+
* stdio, Apple's prompt reads it from the terminal, and the token reaches this
|
|
126
|
+
* runtime only afterwards, on the stdout of a `find-generic-password -w` read
|
|
127
|
+
* that puts nothing in an argv either. Off macOS it comes through
|
|
128
|
+
* `Prompter.readSecret`, which at least keeps it off the screen.
|
|
129
|
+
*
|
|
130
|
+
* **A value we generate ourselves is a different question**, and it is the one
|
|
131
|
+
* place this family makes a trade rather than following a rule. The vault
|
|
132
|
+
* passphrase and the sampling secret are `randomBytes(32)`, minted in this
|
|
133
|
+
* process, so they are already in this process and there is nobody to prompt.
|
|
134
|
+
* They reach the keystore by STDIN first: `security add-generic-password -w`
|
|
135
|
+
* with the value written to its stdin twice (the prompt asks for confirmation),
|
|
136
|
+
* and `secret-tool store`, which documents stdin as its input. Only if the
|
|
137
|
+
* stdin form FAILS does the fallback put the value in an argv (`-w <value>`),
|
|
138
|
+
* and then the outcome says so out loud. That residual exposure is a value
|
|
139
|
+
* minted one millisecond earlier, never used, visible in `ps` to the same user
|
|
140
|
+
* who is running the command and to root — which is the boundary §11 already
|
|
141
|
+
* declares undefended. It is accepted for generated values and for nothing
|
|
142
|
+
* else: no path in this family ever puts an operator's own token in an argv.
|
|
143
|
+
*
|
|
144
|
+
* **And there is one standing exception to the rule above, which
|
|
145
|
+
* `setup adapter <name>` takes.** A credential bound for the VAULT must pass
|
|
146
|
+
* through this process, because the vault is not a helper with a prompt: it is
|
|
147
|
+
* a file this runtime encrypts, so `setCredential` needs the bytes. There is
|
|
148
|
+
* nothing to delegate the typing to and no third party to hold the value. The
|
|
149
|
+
* secret is read with `Prompter.readSecret` (no echo), handed straight to the
|
|
150
|
+
* cipher, and never printed, logged, or placed in an argv — which is exactly
|
|
151
|
+
* what `approval vault set` already does when a human pastes a credential onto
|
|
152
|
+
* its stdin. The rule is "never handle a value someone else can hold for you";
|
|
153
|
+
* for the vault nobody can, so it is stated here rather than left to look like
|
|
154
|
+
* an oversight.
|
|
155
|
+
*
|
|
156
|
+
* ## Seams
|
|
157
|
+
*
|
|
158
|
+
* The prompter, the keystore, and `fetch` are injected. The alternative is a
|
|
159
|
+
* test suite that needs a terminal, writes to the developer's real Keychain,
|
|
160
|
+
* and talks to the real Bot API — and the third of those would put a real bot
|
|
161
|
+
* token in a test run. `tests/cli-setup.test.ts` drives all three through fakes
|
|
162
|
+
* and the mock Bot API on loopback, and the spawned-CLI cases never get past
|
|
163
|
+
* the terminal check, so nothing under `npm test` can reach a keystore at all.
|
|
164
|
+
*/
|
|
165
|
+
import { type SetupDeps } from "./setup-common.js";
|
|
166
|
+
import type { Streams } from "./main.js";
|
|
167
|
+
/**
|
|
168
|
+
* One typed answer to `human identity (human:<id>):`, as an identity (APRV-90).
|
|
169
|
+
*
|
|
170
|
+
* **A bare id is accepted and normalised.** The prompt prints the `human:`
|
|
171
|
+
* prefix because the prefix is load-bearing — actors are `human:`, `agent:` or
|
|
172
|
+
* `system:`, and the human-only verbs refuse the other two — so an operator who
|
|
173
|
+
* has never read SPEC.md §11 learns the shape from the question itself. Making
|
|
174
|
+
* them retype a prefix the prompt just printed adds a failure path and teaches
|
|
175
|
+
* nothing further, so `carter` becomes `human:carter` and `human:carter` is
|
|
176
|
+
* taken as it stands.
|
|
177
|
+
*
|
|
178
|
+
* An answer with a colon in it is taken as a FULL actor and validated as one,
|
|
179
|
+
* which is what makes `agent:claude` a refusal rather than `human:agent:claude`.
|
|
180
|
+
* That refusal is a reason handed back to {@link askUntil}, so the operator is
|
|
181
|
+
* asked again rather than dropped out of the verb.
|
|
182
|
+
*/
|
|
183
|
+
export declare function identityFromAnswer(answer: string): {
|
|
184
|
+
ok: true;
|
|
185
|
+
value: string;
|
|
186
|
+
} | {
|
|
187
|
+
ok: false;
|
|
188
|
+
reason: string;
|
|
189
|
+
};
|
|
190
|
+
/**
|
|
191
|
+
* `approval setup identity` — declare who the human is.
|
|
192
|
+
*
|
|
193
|
+
* EXEMPT from the human-only gate, and the module doc says why: this is the
|
|
194
|
+
* verb that creates the thing the gate reads.
|
|
195
|
+
*/
|
|
196
|
+
export declare function commandSetupIdentity(argv: string[], streams: Streams, cwd: string, deps?: SetupDeps): number;
|
|
197
|
+
/** `approval setup vault` — mint and store the vault passphrase. HUMAN-ONLY. */
|
|
198
|
+
export declare function commandSetupVault(argv: string[], streams: Streams, cwd: string, deps?: SetupDeps): number;
|
|
199
|
+
/** `approval setup sampling` — mint and store the audit sampling secret. HUMAN-ONLY. */
|
|
200
|
+
export declare function commandSetupSampling(argv: string[], streams: Streams, cwd: string, deps?: SetupDeps): number;
|
|
201
|
+
/** `approval setup <identity|vault|sampling|channel <name>|adapter <name>>`. */
|
|
202
|
+
export declare function commandSetup(argv: string[], streams: Streams, cwd: string, deps?: SetupDeps): number | Promise<number>;
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Terminal presentation: colour, glyphs, headings, tables (APRV-91, APRV-93).
|
|
3
|
+
*
|
|
4
|
+
* The observation behind this file was a real `examples/email-demo.md` run and
|
|
5
|
+
* a `policy amend --dry-run` the operator called dense: two 64-hex hashes on one
|
|
6
|
+
* line, the same absolute path three times, every line the same visual weight,
|
|
7
|
+
* no colour anywhere. Nothing was WRONG; nothing was scannable either.
|
|
8
|
+
*
|
|
9
|
+
* The design rules, from APRV-91's brief:
|
|
10
|
+
*
|
|
11
|
+
* 1. ROLES, NOT COLOURS. Callers ask for `ok` or `key`, never for green or
|
|
12
|
+
* bold, so a theme is one table and a colour-blind reader loses nothing.
|
|
13
|
+
* 2. COLOUR IS REDUNDANT, ALWAYS. Every coloured thing is also carried by a
|
|
14
|
+
* glyph or a word, so the plain-text degradation is lossless. That is not
|
|
15
|
+
* politeness: piped output IS the tested output, and a pipe gets no colour.
|
|
16
|
+
* 3. NEVER COLOUR A COPYABLE VALUE. Hashes, tokens, paths and commands are
|
|
17
|
+
* printed raw so a triple-click yields clean bytes. Colour the LABEL.
|
|
18
|
+
* 4. DECIDED ONCE. {@link style} memoizes the process-wide answer; tests build
|
|
19
|
+
* their own with {@link makeStyle} rather than mutating the environment.
|
|
20
|
+
*
|
|
21
|
+
* NO NEW DEPENDENCY, deliberately: a colour library is thousands of lines to
|
|
22
|
+
* buy the eleven escape sequences below, and this repo justifies every package.
|
|
23
|
+
*
|
|
24
|
+
* `--json` IS AN ABSOLUTE VETO here, above even FORCE_COLOR. The brief lists
|
|
25
|
+
* `--json` alongside the other conditions, but the machine-readable shapes are
|
|
26
|
+
* frozen public API; one escape byte in a JSON stream is a parse error, not a
|
|
27
|
+
* cosmetic regression. So a caller that passes `json: true` cannot get colour by
|
|
28
|
+
* any combination of environment variables.
|
|
29
|
+
*/
|
|
30
|
+
/** The presentation roles. A theme is this list mapped to SGR parameters. */
|
|
31
|
+
export type Role = "brand" | "ok" | "warn" | "fail" | "key" | "value" | "muted" | "rule" | "secret";
|
|
32
|
+
/** The glyph vocabulary, and its ASCII degradation. */
|
|
33
|
+
declare const GLYPHS: {
|
|
34
|
+
readonly ok: readonly ["✓", "[ok]"];
|
|
35
|
+
readonly fail: readonly ["✗", "[x]"];
|
|
36
|
+
readonly skip: readonly ["–", "[-]"];
|
|
37
|
+
readonly point: readonly ["▸", ">"];
|
|
38
|
+
readonly bar: readonly ["│", "|"];
|
|
39
|
+
readonly rule: readonly ["─", "-"];
|
|
40
|
+
};
|
|
41
|
+
export type Glyph = keyof typeof GLYPHS;
|
|
42
|
+
/** How many characters of a 64-hex digest a human is shown. */
|
|
43
|
+
export declare const SHORT_HASH_LENGTH = 12;
|
|
44
|
+
export interface StyleInput {
|
|
45
|
+
/**
|
|
46
|
+
* Whether stdout is a terminal. Omitted means "ask this process".
|
|
47
|
+
*
|
|
48
|
+
* Tests pass an explicit `true` to render coloured output into a captured
|
|
49
|
+
* string, which is the only way to assert BOTH modes without a pty.
|
|
50
|
+
*/
|
|
51
|
+
tty?: boolean;
|
|
52
|
+
/** Environment to read. Omitted means `process.env`. */
|
|
53
|
+
env?: Record<string, string | undefined>;
|
|
54
|
+
/** Whether this invocation is answering in JSON. An absolute veto on colour. */
|
|
55
|
+
json?: boolean;
|
|
56
|
+
/** The `--no-color` flag. An explicit off, above FORCE_COLOR. */
|
|
57
|
+
noColor?: boolean;
|
|
58
|
+
}
|
|
59
|
+
export interface Style {
|
|
60
|
+
/** Whether escape sequences are emitted at all. */
|
|
61
|
+
readonly enabled: boolean;
|
|
62
|
+
/** Whether glyphs degrade to their ASCII spellings. */
|
|
63
|
+
readonly ascii: boolean;
|
|
64
|
+
/** Wrap `text` in `role`. A no-op when colour is off, or when text is empty. */
|
|
65
|
+
paint(role: Role, text: string): string;
|
|
66
|
+
brand(text: string): string;
|
|
67
|
+
ok(text: string): string;
|
|
68
|
+
warn(text: string): string;
|
|
69
|
+
fail(text: string): string;
|
|
70
|
+
key(text: string): string;
|
|
71
|
+
/** Identity by contract: a value is never dressed. Here so call sites can say so. */
|
|
72
|
+
value(text: string): string;
|
|
73
|
+
muted(text: string): string;
|
|
74
|
+
secret(text: string): string;
|
|
75
|
+
/** One glyph, already coloured by its natural role where it has one. */
|
|
76
|
+
glyph(name: Glyph): string;
|
|
77
|
+
/** The bare glyph, uncoloured. */
|
|
78
|
+
rawGlyph(name: Glyph): string;
|
|
79
|
+
/** A section heading: the label in `key`, nothing else. */
|
|
80
|
+
heading(text: string): string;
|
|
81
|
+
/** A horizontal rule of `width` characters, in `rule`. */
|
|
82
|
+
rule(width?: number): string;
|
|
83
|
+
/** Aligned two-column rows. Column one in `key` unless the row says otherwise. */
|
|
84
|
+
table(rows: readonly TableRow[], options?: TableOptions): string;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* One row of a two-column table.
|
|
88
|
+
*
|
|
89
|
+
* `left` is the label column and `right` the detail. `glyph` puts a coloured
|
|
90
|
+
* status mark ahead of the label (doctor's column), and `plainLeft` opts a row
|
|
91
|
+
* out of `key` styling for a left cell that is itself a copyable value.
|
|
92
|
+
*/
|
|
93
|
+
export interface TableRow {
|
|
94
|
+
left: string;
|
|
95
|
+
right?: string;
|
|
96
|
+
glyph?: Glyph;
|
|
97
|
+
/** Role for the glyph and, when set, nothing else. Defaults by glyph name. */
|
|
98
|
+
role?: Role;
|
|
99
|
+
plainLeft?: boolean;
|
|
100
|
+
/** Extra lines printed under the row, indented to the detail column. */
|
|
101
|
+
under?: readonly string[];
|
|
102
|
+
}
|
|
103
|
+
export interface TableOptions {
|
|
104
|
+
/** Spaces before every row. Default 0. */
|
|
105
|
+
indent?: number;
|
|
106
|
+
/** Spaces between the columns. Default 2. */
|
|
107
|
+
gap?: number;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* One cell. A bare string is an UNDRESSED cell, which is the common case and
|
|
111
|
+
* the safe default: rule 3 above says a value is never painted, so a caller has
|
|
112
|
+
* to ask for a role before anything is.
|
|
113
|
+
*/
|
|
114
|
+
export interface Cell {
|
|
115
|
+
text: string;
|
|
116
|
+
role?: Role;
|
|
117
|
+
}
|
|
118
|
+
export type GridCell = string | Cell;
|
|
119
|
+
/** A row, with the lines that hang beneath it when it has any. */
|
|
120
|
+
export interface GridRow {
|
|
121
|
+
cells: readonly GridCell[];
|
|
122
|
+
under?: readonly string[];
|
|
123
|
+
}
|
|
124
|
+
export type GridInput = readonly GridCell[] | GridRow;
|
|
125
|
+
export interface GridOptions {
|
|
126
|
+
/** A header row, rendered in `key` unless a cell asks for its own role. */
|
|
127
|
+
header?: readonly GridCell[];
|
|
128
|
+
/** Per-column alignment; missing entries are `left`. */
|
|
129
|
+
align?: readonly ("left" | "right")[];
|
|
130
|
+
/** Spaces before every row. Default 0. */
|
|
131
|
+
indent?: number;
|
|
132
|
+
/** Spaces between columns: one number for all, or one per boundary. */
|
|
133
|
+
gap?: number;
|
|
134
|
+
gaps?: readonly number[];
|
|
135
|
+
/** Spaces (after `indent`) before an `under` line. Default 2. */
|
|
136
|
+
underHang?: number;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* The one aligned-columns renderer in this CLI (APRV-102).
|
|
140
|
+
*
|
|
141
|
+
* Three hand-rolled versions of this arithmetic existed — `style.table`,
|
|
142
|
+
* `execute.ts`'s queue and `hook.ts`'s classify — and they had already drifted
|
|
143
|
+
* on the question that matters: WIDTH IS MEASURED ON THE UNDRESSED TEXT.
|
|
144
|
+
* Escape sequences occupy no terminal columns, so padding computed after
|
|
145
|
+
* painting is wrong by exactly the length of the escapes and the table lines up
|
|
146
|
+
* only in a pipe. Here the cell text is padded and painted separately, so the
|
|
147
|
+
* coloured render is the plain one with escapes inserted and nothing else.
|
|
148
|
+
*
|
|
149
|
+
* Every line is `trimEnd`ed: trailing spaces are invisible in review and very
|
|
150
|
+
* visible in the diff of a pinned transcript.
|
|
151
|
+
*
|
|
152
|
+
* Returned with no trailing newline; the caller owns the stream.
|
|
153
|
+
*/
|
|
154
|
+
export declare function table(st: Style, rows: readonly GridInput[], options?: GridOptions): string;
|
|
155
|
+
/**
|
|
156
|
+
* The one shape a refusal is printed in (APRV-91 #8/#13).
|
|
157
|
+
*
|
|
158
|
+
* ✗ payload-mismatch message.json does not hash to the registered hash
|
|
159
|
+
* fix: approval payload hash message.json
|
|
160
|
+
*
|
|
161
|
+
* Glyph and machine-readable code in `fail`, message plain, and an optional
|
|
162
|
+
* repair on a second line. NEVER followed by a help page: the operator did not
|
|
163
|
+
* mistype anything, so the flags are not the answer and printing them buries
|
|
164
|
+
* the one line that is.
|
|
165
|
+
*
|
|
166
|
+
* The `fix:` LABEL is dressed and the command after it is not, which departs
|
|
167
|
+
* from the brief's "the command in `key`" on purpose. A fix line exists to be
|
|
168
|
+
* copied and run, and rule 3 (never dress a copyable value) is the rule that
|
|
169
|
+
* makes the whole palette safe to trust. The label alone carries the emphasis.
|
|
170
|
+
*
|
|
171
|
+
* Returned as text with no trailing newline, so callers keep control of the
|
|
172
|
+
* stream and of whether a blank line follows.
|
|
173
|
+
*/
|
|
174
|
+
export declare function refusal(style: Style, code: string, message: string, fix?: string): string;
|
|
175
|
+
/**
|
|
176
|
+
* The one shape a legitimate skip is printed in (APRV-153).
|
|
177
|
+
*
|
|
178
|
+
* – web-port this policy declares no channels.web.port, so no queue page is served
|
|
179
|
+
* to enable: approval policy amend
|
|
180
|
+
*
|
|
181
|
+
* The counterpart of {@link refusal} for a part that is not wrong. The fact it
|
|
182
|
+
* reports is doctor's fact (an optional part, absent by configuration), so it
|
|
183
|
+
* borrows doctor's vocabulary: the skip glyph, the check name in `warn`, the
|
|
184
|
+
* detail plain. `approval up` used to print these through {@link refusal}, and
|
|
185
|
+
* the red ✗ taught an operator that a healthy startup had failed; this shape
|
|
186
|
+
* exists to prevent that mis-reading.
|
|
187
|
+
*
|
|
188
|
+
* The second line is labelled `to enable:` rather than `fix:` because nothing
|
|
189
|
+
* is broken and there is nothing to repair; it is the path to turning the part
|
|
190
|
+
* on. Like a fix line it exists to be copied and run, so the label is dressed
|
|
191
|
+
* and the command after it is not (rule 3).
|
|
192
|
+
*
|
|
193
|
+
* Returned as text with no trailing newline, like {@link refusal}.
|
|
194
|
+
*/
|
|
195
|
+
export declare function skipNotice(style: Style, check: string, detail: string, enable?: string): string;
|
|
196
|
+
/**
|
|
197
|
+
* The notice under a printed execution token, on a surface that is not Telegram.
|
|
198
|
+
*
|
|
199
|
+
* Three facts and an instruction, in the order a reader needs them: it works
|
|
200
|
+
* once, nothing anywhere can give it back, so the copy has to happen now.
|
|
201
|
+
*/
|
|
202
|
+
export declare const TOKEN_NOTICE = "single-use \u00B7 stored nowhere \u00B7 copy it now";
|
|
203
|
+
/** The same, for the Telegram listener, where the extra clause is load-bearing. */
|
|
204
|
+
export declare const TOKEN_NOTICE_TELEGRAM = "single-use \u00B7 stored nowhere \u00B7 not sent to Telegram \u00B7 copy it now";
|
|
205
|
+
/**
|
|
206
|
+
* The execution token, in a rule-boxed panel (APRV-91's brief, APRV-102).
|
|
207
|
+
*
|
|
208
|
+
* ─────────────────────────────────────────────────────────────
|
|
209
|
+
* execution token task-042:chaser
|
|
210
|
+
* 729a25b06567ccc0aed356f3423e39bf12b6252056b7890acde455603010fb11
|
|
211
|
+
* single-use · stored nowhere · copy it now
|
|
212
|
+
* ─────────────────────────────────────────────────────────────
|
|
213
|
+
*
|
|
214
|
+
* Trust surfaces look different from chatter: this is the one value in the whole
|
|
215
|
+
* CLI that exists for exactly as long as the terminal keeps it, so it gets a box
|
|
216
|
+
* and whitespace rather than a prefix on a line of prose.
|
|
217
|
+
*
|
|
218
|
+
* THE TOKEN LINE IS UNCOLOURED AND ALONE. Rule 3 in the header is not a
|
|
219
|
+
* preference here: a triple-click on the token must yield the token, and an
|
|
220
|
+
* escape sequence in the middle of it yields something that cannot be spent.
|
|
221
|
+
* The label carries the emphasis, the notice wears `secret` (bold yellow, never
|
|
222
|
+
* red: red is failure and this is a success), and the rules wear `rule`.
|
|
223
|
+
*
|
|
224
|
+
* One helper rather than one per surface, because `grant`, the Telegram listener
|
|
225
|
+
* and the CLI channel each print this and three copies is three chances for the
|
|
226
|
+
* one that matters to lose its warning.
|
|
227
|
+
*/
|
|
228
|
+
export declare function tokenPanel(st: Style, actionKey: string, token: string, notice?: string): string;
|
|
229
|
+
/** Build a style. Tests use this; the CLI uses {@link style}. */
|
|
230
|
+
export declare function makeStyle(input?: StyleInput): Style;
|
|
231
|
+
/**
|
|
232
|
+
* The process-wide style, decided once.
|
|
233
|
+
*
|
|
234
|
+
* `json` and `noColor` are known only after a command line is parsed, so the
|
|
235
|
+
* first caller passes them and later callers get that same answer. A verb that
|
|
236
|
+
* answers in JSON therefore has to ask FIRST, which every JSON branch does by
|
|
237
|
+
* construction: it asks for the style before it prints.
|
|
238
|
+
*/
|
|
239
|
+
export declare function style(input?: StyleInput): Style;
|
|
240
|
+
/** Forget the memoized answer. For tests, and for nothing else. */
|
|
241
|
+
export declare function resetStyle(): void;
|
|
242
|
+
/**
|
|
243
|
+
* The first 12 characters of a digest, for human output only.
|
|
244
|
+
*
|
|
245
|
+
* 12 hex characters is 48 bits: ample to tell two hashes in one report apart,
|
|
246
|
+
* which is the entire job here, and short enough that two of them fit on a line
|
|
247
|
+
* with their labels. The FULL value stays in `--json`, which is what anything
|
|
248
|
+
* comparing hashes should be reading. Anything that is not a 64-hex digest is
|
|
249
|
+
* returned untouched, so this is safe to apply to a field that may be null or
|
|
250
|
+
* already short.
|
|
251
|
+
*/
|
|
252
|
+
export declare function shortHash(hash: string): string;
|
|
253
|
+
/**
|
|
254
|
+
* `path` relative to `cwd` when it is inside it, absolute otherwise.
|
|
255
|
+
*
|
|
256
|
+
* The dense-output complaint was largely this: the same 70-character absolute
|
|
257
|
+
* path three times in one report. A path INSIDE the working directory is
|
|
258
|
+
* printed relative because that is how the operator would type it. A path
|
|
259
|
+
* outside stays absolute, because `../../../etc/approval/APPROVAL.md` is worse
|
|
260
|
+
* than the truth in every way.
|
|
261
|
+
*/
|
|
262
|
+
export declare function relPath(path: string, cwd: string): string;
|
|
263
|
+
/** One numbered step of a {@link runbook}: a command, and at most a comment. */
|
|
264
|
+
export interface RunbookStep {
|
|
265
|
+
/** The runnable command, printed raw so a triple-click yields clean bytes. */
|
|
266
|
+
command: string;
|
|
267
|
+
/** A short trailing comment. Rendered after the command as `# …`. */
|
|
268
|
+
note?: string;
|
|
269
|
+
}
|
|
270
|
+
/** The body of a {@link runbook}, in the order a reader needs it. */
|
|
271
|
+
export interface RunbookInput {
|
|
272
|
+
/**
|
|
273
|
+
* Verbatim output from whatever refused (git, gh), indented under the
|
|
274
|
+
* headline. It goes here rather than inline in the headline because a
|
|
275
|
+
* remote's four-line rejection swallowed into a sentence is the exact
|
|
276
|
+
* failure APRV-129 exists to undo.
|
|
277
|
+
*/
|
|
278
|
+
quote?: readonly string[];
|
|
279
|
+
/** YOUR STATE: three or four short lines, one fact each, no commands. */
|
|
280
|
+
state: readonly string[];
|
|
281
|
+
/** NEXT STEPS: numbered, ONE runnable command per line. */
|
|
282
|
+
steps: readonly RunbookStep[];
|
|
283
|
+
/** Closing pointers: one line each, rationale compressed, no commands. */
|
|
284
|
+
footer?: readonly string[];
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* A refusal the operator has to READ AND ACT ON, rendered as a runbook
|
|
288
|
+
* (APRV-129).
|
|
289
|
+
*
|
|
290
|
+
* ✗ push-rejected the remote refused the push
|
|
291
|
+
* ! [remote rejected] main -> main (protected branch hook declined)
|
|
292
|
+
*
|
|
293
|
+
* YOUR STATE
|
|
294
|
+
* attestation appended at seq 2
|
|
295
|
+
* committed LOCALLY on main, NOT on origin
|
|
296
|
+
*
|
|
297
|
+
* NEXT STEPS
|
|
298
|
+
* 1. git branch policy-amend-2
|
|
299
|
+
* 2. git push -u origin policy-amend-2
|
|
300
|
+
*
|
|
301
|
+
* why a merge commit: … see docs/cli-reference.md
|
|
302
|
+
*
|
|
303
|
+
* The shape comes from the first live `push-rejected`, which said all of this
|
|
304
|
+
* correctly as one paragraph and was read as the single word REJECTED. The
|
|
305
|
+
* register already existed in this file (the token panel, doctor's
|
|
306
|
+
* line-per-check), and this is that register applied to recovery.
|
|
307
|
+
*
|
|
308
|
+
* ONE RUNNABLE COMMAND PER LINE is the load-bearing rule. A step is a command
|
|
309
|
+
* and at most a trailing comment, so the reader can copy down the list without
|
|
310
|
+
* parsing prose for the part that is typed. Rationale is not deleted: it is
|
|
311
|
+
* compressed into `footer` as one line plus a documentation pointer.
|
|
312
|
+
*
|
|
313
|
+
* Structure survives with styling stripped: the headings are their own words,
|
|
314
|
+
* the numbers are literal digits, and the indentation is spaces. NO_COLOR and
|
|
315
|
+
* ASCII mode lose the colour and the glyph spelling, nothing else.
|
|
316
|
+
*
|
|
317
|
+
* Returned as text with no trailing newline, like {@link refusal}.
|
|
318
|
+
*/
|
|
319
|
+
export declare function runbook(st: Style, code: string, headline: string, input: RunbookInput): string;
|
|
320
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The token verbs: `approval token` (status) and `approval consume` (plumbing).
|
|
3
|
+
*
|
|
4
|
+
* As everywhere else in this CLI, **no logic lives here.** Minting, verification,
|
|
5
|
+
* the death conditions, and the append are all `core/token.ts`; this file
|
|
6
|
+
* resolves paths and identity, chooses an exit code, and formats output.
|
|
7
|
+
*
|
|
8
|
+
* ## `approval token` reports status; it does not print the token
|
|
9
|
+
*
|
|
10
|
+
* SPEC.md §10.1 lists `approval token <action-key> # print single-use execution
|
|
11
|
+
* token if granted`. Under the settled hash-only design (2026-08-06) the log
|
|
12
|
+
* carries `sha256(token)` and the raw token is returned by the grant call and
|
|
13
|
+
* kept nowhere else — so there is nothing for this verb to fetch. It would have
|
|
14
|
+
* to *store* the secret to print it, which is precisely the property the design
|
|
15
|
+
* exists to avoid.
|
|
16
|
+
*
|
|
17
|
+
* The honest reading, and the one implemented here: the token is printed **by
|
|
18
|
+
* `approval grant`**, once; `approval token` answers "is a live, unspent token
|
|
19
|
+
* outstanding for this action, and what is its digest?". That interpretation is
|
|
20
|
+
* flagged in the task notes for human review — it is a reading of the spec, not
|
|
21
|
+
* a silent amendment of it, and §10.4's normative sentence (adapters MUST
|
|
22
|
+
* require a valid, unexpired, single-use token bound to the idempotency key) is
|
|
23
|
+
* unaffected either way.
|
|
24
|
+
*
|
|
25
|
+
* ## `approval consume` is internal
|
|
26
|
+
*
|
|
27
|
+
* It is the seam APRV-18's `approval run` will wrap: verify, append
|
|
28
|
+
* `execution.started`, hand control to the command. It ships now so the token
|
|
29
|
+
* boundary is testable end to end and so an adapter integration can be driven by
|
|
30
|
+
* hand, and its help text says INTERNAL in the first line.
|
|
31
|
+
*
|
|
32
|
+
* Exit codes are the frozen table, mapped exactly as the gate verbs map them: a
|
|
33
|
+
* refusal ("that token will not execute") is 1, because the command was
|
|
34
|
+
* well-formed and the answer is no; only filesystem facts are 4 and only a
|
|
35
|
+
* crashed write is 3.
|
|
36
|
+
*/
|
|
37
|
+
import type { Streams } from "./main.js";
|
|
38
|
+
export declare function commandToken(argv: string[], streams: Streams, cwd: string): number;
|
|
39
|
+
export declare function commandConsume(argv: string[], streams: Streams, cwd: string): number;
|