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,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Terminal prompts — the one place this CLI reads from a human (APRV-74).
|
|
3
|
+
*
|
|
4
|
+
* Two of these three functions existed already, privately, inside
|
|
5
|
+
* `cli/amend.ts`: a synchronous line reader and the `[y/N]` question built on
|
|
6
|
+
* it. `approval setup` needs both, plus a third that `amend` never needed — a
|
|
7
|
+
* reader that does not echo — and three copies of a raw-mode byte loop is two
|
|
8
|
+
* too many. So they live here, `amend` imports the line reader, and the
|
|
9
|
+
* no-echo one is written once, in the open, where it can be read.
|
|
10
|
+
*
|
|
11
|
+
* ## Synchronous, on purpose
|
|
12
|
+
*
|
|
13
|
+
* `readSync(0, …)` rather than `readline/promises`. Every verb in this CLI
|
|
14
|
+
* returns an exit code from a synchronous function, and the two that do not
|
|
15
|
+
* (`channel`, `daemon`) are asynchronous because they hold a socket open, not
|
|
16
|
+
* because they ask a question. A prompt that forced `setup identity` to become
|
|
17
|
+
* a promise would be an async CLI shape adopted for the sake of one line of
|
|
18
|
+
* input.
|
|
19
|
+
*
|
|
20
|
+
* ## The no-echo reader, and what it is worth
|
|
21
|
+
*
|
|
22
|
+
* {@link readSecret} puts the terminal in raw mode and reads one byte at a
|
|
23
|
+
* time, printing nothing — not even asterisks, which leak a length. It restores
|
|
24
|
+
* the terminal in a `finally`, including on the abort paths, because a shell
|
|
25
|
+
* left in raw mode after a Ctrl-C is a broken session and the operator's next
|
|
26
|
+
* act would be to close the window.
|
|
27
|
+
*
|
|
28
|
+
* What it defends is a secret in a scrollback buffer, a `script` capture, or a
|
|
29
|
+
* screen share. What it does not defend is a compromised terminal, a keylogger,
|
|
30
|
+
* or the process itself: the value is a `string` in this process's heap the
|
|
31
|
+
* instant it is complete, exactly as `approval vault set`'s stdin value is. The
|
|
32
|
+
* design point that matters more is upstream of this file — wherever a secret
|
|
33
|
+
* can be collected by a helper's OWN prompt, `setup` delegates to that helper
|
|
34
|
+
* and this function is never called (see `cli/setup.ts`, "the token never
|
|
35
|
+
* enters our process").
|
|
36
|
+
*
|
|
37
|
+
* **Ctrl-C is a distinct result, not an exception and not an empty string.** An
|
|
38
|
+
* aborted prompt must be distinguishable from an empty answer by the caller, so
|
|
39
|
+
* that "the human changed their mind" stores nothing and exits, while "the
|
|
40
|
+
* human pressed Enter" is an ordinary validation failure. Collapsing the two is
|
|
41
|
+
* how a half-configured keystore entry gets written.
|
|
42
|
+
*
|
|
43
|
+
* Nothing here throws, and nothing here ever writes the value it read to any
|
|
44
|
+
* stream.
|
|
45
|
+
*
|
|
46
|
+
* ## A bad answer is a question asked again, not a mangled command line (APRV-90)
|
|
47
|
+
*
|
|
48
|
+
* {@link askUntil} is the loop every typed question in `setup` runs through. It
|
|
49
|
+
* exists because the first shipped build treated a wrong answer to a PROMPT the
|
|
50
|
+
* way it treats a wrong flag on an argv: exit 2 with the whole help page under
|
|
51
|
+
* it. Observed on 2026-08-18, `setup identity` printed `human identity
|
|
52
|
+
* (human:<id>):`, the operator typed `carter`, and forty lines of help came
|
|
53
|
+
* back. A prompt has already told the human what it wants; when the answer does
|
|
54
|
+
* not fit, the useful reply is one line saying which part did not fit, followed
|
|
55
|
+
* by the same question.
|
|
56
|
+
*
|
|
57
|
+
* Three ends, and they are different on purpose:
|
|
58
|
+
*
|
|
59
|
+
* - **accepted** — the validator's parsed value, which may be a NORMALISED form
|
|
60
|
+
* of what was typed (`carter` → `human:carter`) rather than the bytes;
|
|
61
|
+
* - **aborted** — `readLine` returned `null`, which is Ctrl-D (and, for the
|
|
62
|
+
* scripted prompter, the modelled Ctrl-C). Nothing is stored, and the caller
|
|
63
|
+
* exits with a one-line reason and NO help page, because there was no command
|
|
64
|
+
* line for a help page to be about;
|
|
65
|
+
* - **exhausted** — {@link DEFAULT_MAX_ATTEMPTS} answers in a row failed
|
|
66
|
+
* validation. Bounded rather than infinite so that a prompt driven by
|
|
67
|
+
* something that is not a person (a stuck script, a terminal replaying bytes)
|
|
68
|
+
* terminates instead of looping forever.
|
|
69
|
+
*
|
|
70
|
+
* ## EAGAIN is "not yet", never "end of input" (APRV-84)
|
|
71
|
+
*
|
|
72
|
+
* Every caller checks `process.stdin.isTTY` before it prompts, and merely
|
|
73
|
+
* touching `process.stdin` on a terminal has libuv put fd 0 into non-blocking
|
|
74
|
+
* mode. From then on a `readSync(0, …)` issued before the human has typed
|
|
75
|
+
* fails with EAGAIN instead of waiting. The first shipped line reader treated
|
|
76
|
+
* every error as EOF, so `approval setup identity` on a macOS terminal printed
|
|
77
|
+
* "no identity was entered" the instant the prompt appeared. Both readers now
|
|
78
|
+
* go through {@link readByteBlocking}, which sleeps a few milliseconds on
|
|
79
|
+
* EAGAIN and tries again, and treats nothing else as "wait".
|
|
80
|
+
*/
|
|
81
|
+
import type { Streams } from "./main.js";
|
|
82
|
+
/** The result of a no-echo read. `aborted` is Ctrl-C or Ctrl-D, distinctly. */
|
|
83
|
+
export type SecretRead = {
|
|
84
|
+
ok: true;
|
|
85
|
+
value: string;
|
|
86
|
+
} | {
|
|
87
|
+
ok: false;
|
|
88
|
+
reason: "aborted";
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
91
|
+
* One line from stdin, synchronously. `null` at EOF.
|
|
92
|
+
*
|
|
93
|
+
* Hoisted verbatim from `cli/amend.ts` (APRV-74), which is why it reads bytes
|
|
94
|
+
* rather than using a stream: it is called from inside a synchronous command
|
|
95
|
+
* function, after output has already been written, and it must not consume more
|
|
96
|
+
* of stdin than the line it was asked for.
|
|
97
|
+
*/
|
|
98
|
+
export declare function readLineFromStdin(read?: ByteReader): string | null;
|
|
99
|
+
/** One byte of stdin into `buffer[0]`; returns the count read (0 at EOF). */
|
|
100
|
+
export type ByteReader = (buffer: Buffer) => number;
|
|
101
|
+
/**
|
|
102
|
+
* One byte from fd 0, blocking, whatever mode libuv left the descriptor in.
|
|
103
|
+
*
|
|
104
|
+
* EAGAIN means the terminal has nothing yet: nap and retry (`Atomics.wait` is
|
|
105
|
+
* the one synchronous sleep JavaScript has, and it keeps the wait from being a
|
|
106
|
+
* hot loop). Every other error propagates, and 0 bytes is EOF, exactly as with
|
|
107
|
+
* `readSync` itself.
|
|
108
|
+
*/
|
|
109
|
+
export declare function readByteBlocking(buffer: Buffer): number;
|
|
110
|
+
/**
|
|
111
|
+
* Read a secret with no echo. See the module doc for what this is worth.
|
|
112
|
+
*
|
|
113
|
+
* The byte loop, in full: `\r` or `\n` ends the read; DEL (0x7f) and BS (0x08)
|
|
114
|
+
* remove the last character and echo nothing (no cursor movement — there is
|
|
115
|
+
* nothing on screen to erase); Ctrl-C aborts always; Ctrl-D aborts only on an
|
|
116
|
+
* empty buffer, which is EOF's meaning everywhere else in a shell and which
|
|
117
|
+
* leaves the "Ctrl-D at the end of a typed line" case as an ordinary end. Every
|
|
118
|
+
* other byte accumulates.
|
|
119
|
+
*
|
|
120
|
+
* Raw mode is restored in a `finally`, and a newline is printed after, so the
|
|
121
|
+
* caller's next output starts on its own line whichever way the read ended.
|
|
122
|
+
*/
|
|
123
|
+
export declare function readSecret(streams: Streams, prompt: string, read?: ByteReader): SecretRead;
|
|
124
|
+
/**
|
|
125
|
+
* The three ways `setup` talks to a human, as an interface.
|
|
126
|
+
*
|
|
127
|
+
* A seam, for the reason every other seam in this codebase exists: the
|
|
128
|
+
* alternative is a test suite that needs a terminal. Tests pass a scripted
|
|
129
|
+
* prompter and assert on what was asked as well as on what was done, which is
|
|
130
|
+
* how "Ctrl-C mid-secret stores nothing" becomes a test rather than a claim.
|
|
131
|
+
*/
|
|
132
|
+
export interface Prompter {
|
|
133
|
+
/** A question with a typed answer. `null` at EOF. */
|
|
134
|
+
readLine(prompt: string): string | null;
|
|
135
|
+
/** A question whose answer is not echoed. */
|
|
136
|
+
readSecret(prompt: string): SecretRead;
|
|
137
|
+
/** `[y/N]` (or `[Y/n]`). Anything unrecognised takes the default. */
|
|
138
|
+
confirm(prompt: string, defaultNo?: boolean): boolean;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* The real prompter, or `null` when stdin is not a terminal.
|
|
142
|
+
*
|
|
143
|
+
* Refusing to construct is deliberate: a prompter that fell back to reading a
|
|
144
|
+
* pipe would let a CI job answer `setup`'s confirmations by feeding it a
|
|
145
|
+
* heredoc, and "a human was asked" is the entire content of these questions.
|
|
146
|
+
* Callers check `process.stdin.isTTY` themselves and print the non-interactive
|
|
147
|
+
* alternative; this is the second lock on the same door.
|
|
148
|
+
*/
|
|
149
|
+
export declare function createPrompter(streams: Streams): Prompter | null;
|
|
150
|
+
/**
|
|
151
|
+
* How many failed answers to one question before the loop gives up. See the
|
|
152
|
+
* module doc: bounded so that a prompt nobody is answering terminates.
|
|
153
|
+
*/
|
|
154
|
+
export declare const DEFAULT_MAX_ATTEMPTS = 5;
|
|
155
|
+
/**
|
|
156
|
+
* A validator's verdict on one typed answer.
|
|
157
|
+
*
|
|
158
|
+
* `value` is the PARSED form, not the bytes: a validator is where `carter`
|
|
159
|
+
* becomes `human:carter` and `"2"` becomes the second choice, so the caller
|
|
160
|
+
* never re-derives from the string what the validator already decided.
|
|
161
|
+
* `reason` is the single line the operator is shown before the question comes
|
|
162
|
+
* back, so it names what was wrong and nothing else.
|
|
163
|
+
*/
|
|
164
|
+
export type AnswerVerdict<T> = {
|
|
165
|
+
ok: true;
|
|
166
|
+
value: T;
|
|
167
|
+
} | {
|
|
168
|
+
ok: false;
|
|
169
|
+
reason: string;
|
|
170
|
+
};
|
|
171
|
+
/** How {@link askUntil} ended. `attempts` is how many answers were read. */
|
|
172
|
+
export type AskOutcome<T> = {
|
|
173
|
+
ok: true;
|
|
174
|
+
value: T;
|
|
175
|
+
} | {
|
|
176
|
+
ok: false;
|
|
177
|
+
reason: "aborted" | "exhausted";
|
|
178
|
+
attempts: number;
|
|
179
|
+
};
|
|
180
|
+
/**
|
|
181
|
+
* Ask until the answer validates, the human gives up, or the attempts run out.
|
|
182
|
+
*
|
|
183
|
+
* The reason line goes to STDOUT, indented, because it is part of the
|
|
184
|
+
* conversation and not a diagnostic about a command: it is read by the person
|
|
185
|
+
* at the terminal, in between their answer and the next prompt. A caller turns
|
|
186
|
+
* an `aborted` or `exhausted` outcome into its own one-line refusal; nothing
|
|
187
|
+
* here prints a help page (see the module doc).
|
|
188
|
+
*/
|
|
189
|
+
export declare function askUntil<T>(streams: Streams, prompter: Prompter, question: string, validate: (answer: string) => AnswerVerdict<T>, options?: {
|
|
190
|
+
maxAttempts?: number;
|
|
191
|
+
}): AskOutcome<T>;
|
|
192
|
+
/**
|
|
193
|
+
* A yes/no question that asks AGAIN when the answer is neither (APRV-99).
|
|
194
|
+
*
|
|
195
|
+
* {@link Prompter.confirm} takes the default for anything it does not
|
|
196
|
+
* recognise, which is the right reading for a question whose default is the
|
|
197
|
+
* safe answer: a stray keystroke declines. It is the wrong reading for a
|
|
198
|
+
* question whose default is YES and whose subject is a check the operator
|
|
199
|
+
* asked for, where a typo would silently skip the proof and report it as a
|
|
200
|
+
* decision. APRV-90's answer to that class of question is that every question
|
|
201
|
+
* loops, so this is {@link askUntil} over a yes/no validator: the same bounded
|
|
202
|
+
* loop, the same indented reason line, the same abort handling.
|
|
203
|
+
*
|
|
204
|
+
* Existing `confirm` calls are deliberately untouched — their output is pinned
|
|
205
|
+
* byte for byte — so this is an addition to the vocabulary, not a replacement.
|
|
206
|
+
* A withdrawal (Ctrl-D) or an exhausted loop reads as NO, which is the
|
|
207
|
+
* fail-closed direction for a question that offers to do something.
|
|
208
|
+
*/
|
|
209
|
+
export declare function confirmUntil(streams: Streams, prompter: Prompter, question: string, defaultYes: boolean): boolean;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `approval quickstart` (APRV-309): one human ceremony for a small, operative
|
|
3
|
+
* policy. The command is terminal-only, displays the exact bytes it will
|
|
4
|
+
* attest, and leaves any interrupted setup unattested.
|
|
5
|
+
*/
|
|
6
|
+
import { spawn } from "node:child_process";
|
|
7
|
+
import { type SourceRunner } from "../core/env-file.js";
|
|
8
|
+
import type { Streams } from "./main.js";
|
|
9
|
+
import { type Prompter } from "./prompt.js";
|
|
10
|
+
import type { SetupDeps } from "./setup-common.js";
|
|
11
|
+
export declare const QUICKSTART_CLASSES: readonly [{
|
|
12
|
+
readonly pattern: "communicate.*";
|
|
13
|
+
readonly label: "send a message or email";
|
|
14
|
+
}, {
|
|
15
|
+
readonly pattern: "financial.*";
|
|
16
|
+
readonly label: "spend money";
|
|
17
|
+
}, {
|
|
18
|
+
readonly pattern: "files.delete.*";
|
|
19
|
+
readonly label: "delete files";
|
|
20
|
+
}, {
|
|
21
|
+
readonly pattern: "public.*";
|
|
22
|
+
readonly label: "post publicly";
|
|
23
|
+
}, {
|
|
24
|
+
readonly pattern: "vcs.push.main";
|
|
25
|
+
readonly label: "push to main";
|
|
26
|
+
}];
|
|
27
|
+
type Channel = "cli" | "telegram";
|
|
28
|
+
export interface QuickstartDeps {
|
|
29
|
+
prompter?: Prompter | null;
|
|
30
|
+
setup?: SetupDeps;
|
|
31
|
+
sourceRunner?: SourceRunner;
|
|
32
|
+
doctor?: (dir: string, actor: string, resolvedEnv: Readonly<Record<string, string>>, apiBase: string | null) => Promise<DoctorRun>;
|
|
33
|
+
}
|
|
34
|
+
interface DoctorRun {
|
|
35
|
+
code: number;
|
|
36
|
+
stdout: string;
|
|
37
|
+
stderr: string;
|
|
38
|
+
}
|
|
39
|
+
export declare function renderSoloPolicy(humanId: string, channel: Channel, patterns: readonly string[]): string;
|
|
40
|
+
export declare function runQuickstartDoctor(dir: string, actor: string, resolvedEnv: Readonly<Record<string, string>>, apiBase: string | null, options?: {
|
|
41
|
+
timeoutMs?: number;
|
|
42
|
+
outputLimitBytes?: number;
|
|
43
|
+
spawnChild?: typeof spawn;
|
|
44
|
+
}): Promise<DoctorRun>;
|
|
45
|
+
export declare function commandQuickstart(argv: string[], streams: Streams, cwd: string, deps?: QuickstartDeps): Promise<number>;
|
|
46
|
+
export {};
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `approval quickstart` (APRV-309): one human ceremony for a small, operative
|
|
3
|
+
* policy. The command is terminal-only, displays the exact bytes it will
|
|
4
|
+
* attest, and leaves any interrupted setup unattested.
|
|
5
|
+
*/
|
|
6
|
+
import { spawn } from "node:child_process";
|
|
7
|
+
import { existsSync } from "node:fs";
|
|
8
|
+
import { isAbsolute, join, resolve as resolvePathSegments } from "node:path";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
10
|
+
import { appendAttestation, HUMAN_ACTOR_ENV, policyBytesHash } from "../core/attest.js";
|
|
11
|
+
import { SECRET_ENV_PREFIXES } from "../core/child-env.js";
|
|
12
|
+
import { defaultSourceRunner, envFilePathFor, resolveEnvironment, upsertEnvFileEntries, } from "../core/env-file.js";
|
|
13
|
+
import { loadPolicy, loadPolicyText } from "../core/policy-load.js";
|
|
14
|
+
import { parseFlags, boolFlag, stringFlag } from "./args.js";
|
|
15
|
+
import { EXIT_INTEGRITY, EXIT_IO, EXIT_OK, EXIT_TORN_TAIL, EXIT_USAGE } from "./exit-codes.js";
|
|
16
|
+
import { QUICKSTART_HELP } from "./help.js";
|
|
17
|
+
import { commandInit } from "./init.js";
|
|
18
|
+
import { DEFAULT_LOG_PATH } from "./paths.js";
|
|
19
|
+
import { askUntil, createPrompter } from "./prompt.js";
|
|
20
|
+
import { commandSetupChannel } from "./setup-channel.js";
|
|
21
|
+
import { identityFromAnswer } from "./setup.js";
|
|
22
|
+
import { usageErrorText } from "./usage.js";
|
|
23
|
+
const FLAGS = {
|
|
24
|
+
"--dir": "string",
|
|
25
|
+
"--api-base": "string",
|
|
26
|
+
"--json": "boolean",
|
|
27
|
+
"--help": "boolean",
|
|
28
|
+
"-h": "boolean",
|
|
29
|
+
};
|
|
30
|
+
export const QUICKSTART_CLASSES = [
|
|
31
|
+
{ pattern: "communicate.*", label: "send a message or email" },
|
|
32
|
+
{ pattern: "financial.*", label: "spend money" },
|
|
33
|
+
{ pattern: "files.delete.*", label: "delete files" },
|
|
34
|
+
{ pattern: "public.*", label: "post publicly" },
|
|
35
|
+
{ pattern: "vcs.push.main", label: "push to main" },
|
|
36
|
+
];
|
|
37
|
+
const DOCTOR_TIMEOUT_MS = 20_000;
|
|
38
|
+
const DOCTOR_OUTPUT_LIMIT_BYTES = 256 * 1024;
|
|
39
|
+
function absolute(value, cwd) {
|
|
40
|
+
return isAbsolute(value) ? value : resolvePathSegments(cwd, value);
|
|
41
|
+
}
|
|
42
|
+
function refusal(streams, message) {
|
|
43
|
+
streams.err(usageErrorText(message, QUICKSTART_HELP));
|
|
44
|
+
return EXIT_USAGE;
|
|
45
|
+
}
|
|
46
|
+
function shellQuote(value) {
|
|
47
|
+
return `'${value.replaceAll("'", `'"'"'`)}'`;
|
|
48
|
+
}
|
|
49
|
+
function nonInteractive(streams, json) {
|
|
50
|
+
const message = "quickstart needs a human at a terminal and has no JSON mode. Non-interactive equivalent: run `approval init`, write and review APPROVAL.md, run `approval setup identity`, optionally run `approval setup channel telegram`, then run `approval policy attest --as human:<id>`.";
|
|
51
|
+
if (json)
|
|
52
|
+
streams.err(`${JSON.stringify({ error: { code: "usage", message } })}\n`);
|
|
53
|
+
else
|
|
54
|
+
streams.err(usageErrorText(message, QUICKSTART_HELP));
|
|
55
|
+
return EXIT_USAGE;
|
|
56
|
+
}
|
|
57
|
+
function channelAnswer(answer) {
|
|
58
|
+
switch (answer.trim().toLowerCase()) {
|
|
59
|
+
case "1":
|
|
60
|
+
case "terminal":
|
|
61
|
+
case "cli":
|
|
62
|
+
return { ok: true, value: "cli" };
|
|
63
|
+
case "2":
|
|
64
|
+
case "telegram":
|
|
65
|
+
return { ok: true, value: "telegram" };
|
|
66
|
+
default:
|
|
67
|
+
return { ok: false, reason: "choose 1 for this terminal or 2 for Telegram" };
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function classesAnswer(answer) {
|
|
71
|
+
const typed = answer.trim().toLowerCase();
|
|
72
|
+
if (typed.length === 0 || typed === "all") {
|
|
73
|
+
return { ok: true, value: QUICKSTART_CLASSES.map((entry) => entry.pattern) };
|
|
74
|
+
}
|
|
75
|
+
if (typed === "none")
|
|
76
|
+
return { ok: true, value: [] };
|
|
77
|
+
const indexes = typed.split(",").map((part) => Number(part.trim()));
|
|
78
|
+
if (indexes.some((index) => !Number.isInteger(index) || index < 1 || index > QUICKSTART_CLASSES.length)) {
|
|
79
|
+
return { ok: false, reason: "enter comma-separated numbers 1-5, all, none, or press Enter for all" };
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
ok: true,
|
|
83
|
+
value: [...new Set(indexes)].map((index) => QUICKSTART_CLASSES[index - 1]?.pattern),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function quickstartIdentity(answer) {
|
|
87
|
+
const parsed = identityFromAnswer(answer);
|
|
88
|
+
if (!parsed.ok)
|
|
89
|
+
return parsed;
|
|
90
|
+
const humanId = parsed.value.slice("human:".length);
|
|
91
|
+
return /^[a-z0-9][a-z0-9_-]*$/u.test(humanId)
|
|
92
|
+
? parsed
|
|
93
|
+
: { ok: false, reason: "the id must start with a lowercase letter or digit and contain only lowercase letters, digits, _ or -" };
|
|
94
|
+
}
|
|
95
|
+
export function renderSoloPolicy(humanId, channel, patterns) {
|
|
96
|
+
const rules = patterns
|
|
97
|
+
.map((pattern) => ` ${pattern}:\n autonomy: manual\n approvers: [${humanId}]`)
|
|
98
|
+
.join("\n");
|
|
99
|
+
const channelBlock = channel === "telegram"
|
|
100
|
+
? "\nchannels:\n telegram:\n chat_id_env: APPROVAL_TG_CHAT\n token_env: APPROVAL_TG_TOKEN\n"
|
|
101
|
+
: "";
|
|
102
|
+
return `# Approval Policy\n\nThis is a solo policy. The selected class families ask the person below.\nOther classified reversible actions use the autonomous default. Runtime safety\nfloors and unclassified-command refusals still apply.\n\n\`\`\`yaml approval-policy\nversion: "0.1"\n\ndefaults:\n autonomy: autonomous # Tighten to manual to make every unnamed class ask.\n channel: ${channel}\n approval_ttl: 24h\n on_expiry: reject\n\napprovers:\n ${humanId}:\n channels: [${channel}]\n\nclasses:\n${rules.length === 0 ? " {}" : rules}\n${channelBlock}\`\`\`\n\nNo audit block means retrospective sampling is off. Add one only through a\nhuman-reviewed policy amendment; see \`approval policy amend --help\`.\n`;
|
|
103
|
+
}
|
|
104
|
+
function appendBounded(current, chunk, limit) {
|
|
105
|
+
const remaining = limit - Buffer.byteLength(current);
|
|
106
|
+
if (remaining <= 0)
|
|
107
|
+
return { text: current, overflow: true };
|
|
108
|
+
const bytes = Buffer.from(chunk);
|
|
109
|
+
if (bytes.byteLength <= remaining)
|
|
110
|
+
return { text: current + chunk, overflow: false };
|
|
111
|
+
return { text: current + bytes.subarray(0, remaining).toString("utf8"), overflow: true };
|
|
112
|
+
}
|
|
113
|
+
export function runQuickstartDoctor(dir, actor, resolvedEnv, apiBase, options = {}) {
|
|
114
|
+
return new Promise((resolve) => {
|
|
115
|
+
const env = { ...process.env };
|
|
116
|
+
// The check must not borrow identity or credentials from another instance.
|
|
117
|
+
// Only values explicitly resolved from this quickstart's source map return.
|
|
118
|
+
for (const name of Object.keys(env)) {
|
|
119
|
+
if (SECRET_ENV_PREFIXES.some((prefix) => name.startsWith(prefix)))
|
|
120
|
+
delete env[name];
|
|
121
|
+
}
|
|
122
|
+
Object.assign(env, resolvedEnv, { [HUMAN_ACTOR_ENV]: actor });
|
|
123
|
+
const cliEntry = fileURLToPath(new URL("./main.js", import.meta.url));
|
|
124
|
+
const childArgs = [cliEntry, "doctor", "--json", "--dir", dir];
|
|
125
|
+
if (apiBase !== null)
|
|
126
|
+
childArgs.push("--api-base", apiBase);
|
|
127
|
+
const child = (options.spawnChild ?? spawn)(process.execPath, childArgs, {
|
|
128
|
+
cwd: dir,
|
|
129
|
+
env,
|
|
130
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
131
|
+
});
|
|
132
|
+
let stdout = "";
|
|
133
|
+
let stderr = "";
|
|
134
|
+
let settled = false;
|
|
135
|
+
const outputLimit = options.outputLimitBytes ?? DOCTOR_OUTPUT_LIMIT_BYTES;
|
|
136
|
+
const finish = (result) => {
|
|
137
|
+
if (settled)
|
|
138
|
+
return;
|
|
139
|
+
settled = true;
|
|
140
|
+
clearTimeout(timer);
|
|
141
|
+
resolve(result);
|
|
142
|
+
};
|
|
143
|
+
const abortForOutput = () => {
|
|
144
|
+
child.kill("SIGTERM");
|
|
145
|
+
finish({ code: EXIT_IO, stdout, stderr: `${stderr}approval doctor output exceeded ${String(outputLimit)} bytes and was stopped\n` });
|
|
146
|
+
};
|
|
147
|
+
child.stdout.setEncoding("utf8");
|
|
148
|
+
child.stderr.setEncoding("utf8");
|
|
149
|
+
child.stdout.on("data", (text) => {
|
|
150
|
+
const next = appendBounded(stdout, text, outputLimit);
|
|
151
|
+
stdout = next.text;
|
|
152
|
+
if (next.overflow)
|
|
153
|
+
abortForOutput();
|
|
154
|
+
});
|
|
155
|
+
child.stderr.on("data", (text) => {
|
|
156
|
+
const next = appendBounded(stderr, text, outputLimit);
|
|
157
|
+
stderr = next.text;
|
|
158
|
+
if (next.overflow)
|
|
159
|
+
abortForOutput();
|
|
160
|
+
});
|
|
161
|
+
child.once("error", (cause) => finish({ code: EXIT_IO, stdout, stderr: `${stderr}${cause.message}\n` }));
|
|
162
|
+
child.once("close", (code) => finish({ code: code ?? EXIT_IO, stdout, stderr }));
|
|
163
|
+
const timer = setTimeout(() => {
|
|
164
|
+
child.kill("SIGTERM");
|
|
165
|
+
finish({ code: EXIT_IO, stdout, stderr: `${stderr}approval doctor exceeded ${String(options.timeoutMs ?? DOCTOR_TIMEOUT_MS)}ms and was stopped\n` });
|
|
166
|
+
}, options.timeoutMs ?? DOCTOR_TIMEOUT_MS);
|
|
167
|
+
timer.unref();
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
function doctorPreflight(run) {
|
|
171
|
+
if (run.code === EXIT_OK)
|
|
172
|
+
return { ok: true };
|
|
173
|
+
if (run.code === EXIT_INTEGRITY) {
|
|
174
|
+
try {
|
|
175
|
+
const report = JSON.parse(run.stdout);
|
|
176
|
+
const failed = report.checks?.filter((check) => check.status === "fail") ?? [];
|
|
177
|
+
if (failed.length === 1 &&
|
|
178
|
+
failed[0]?.check === "attestation" &&
|
|
179
|
+
typeof failed[0].detail === "string" &&
|
|
180
|
+
failed[0].detail.includes("has never been attested"))
|
|
181
|
+
return { ok: true };
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
// The diagnostic's malformed output is reported below with its exit.
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return {
|
|
188
|
+
ok: false,
|
|
189
|
+
code: run.code,
|
|
190
|
+
message: `approval doctor preflight reported a setup problem; the policy remains unattested:\n${run.stderr}${run.stdout}`,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
export async function commandQuickstart(argv, streams, cwd, deps = {}) {
|
|
194
|
+
const json = argv.includes("--json");
|
|
195
|
+
const parsed = parseFlags(argv, FLAGS);
|
|
196
|
+
if (!parsed.ok)
|
|
197
|
+
return refusal(streams, parsed.message);
|
|
198
|
+
if (boolFlag(parsed.flags, "--help") || boolFlag(parsed.flags, "-h")) {
|
|
199
|
+
streams.out(`${QUICKSTART_HELP}\n`);
|
|
200
|
+
return EXIT_OK;
|
|
201
|
+
}
|
|
202
|
+
if (parsed.positionals.length > 0)
|
|
203
|
+
return refusal(streams, `unexpected argument ${JSON.stringify(parsed.positionals[0])}`);
|
|
204
|
+
const prompter = deps.prompter === undefined ? createPrompter(streams) : deps.prompter;
|
|
205
|
+
if (json || prompter === null)
|
|
206
|
+
return nonInteractive(streams, json);
|
|
207
|
+
const dirFlag = stringFlag(parsed.flags, "--dir");
|
|
208
|
+
const dir = dirFlag === null ? cwd : absolute(dirFlag, cwd);
|
|
209
|
+
const apiBase = stringFlag(parsed.flags, "--api-base");
|
|
210
|
+
if (existsSync(join(dir, "APPROVAL.md")) ||
|
|
211
|
+
existsSync(join(dir, "APPROVALS.md")) ||
|
|
212
|
+
existsSync(join(dir, ".approval"))) {
|
|
213
|
+
streams.err(`approval: setup artifacts already exist in ${dir}; quickstart only initializes a fresh instance and changed nothing\n`);
|
|
214
|
+
return EXIT_IO;
|
|
215
|
+
}
|
|
216
|
+
streams.out("approval quickstart — three decisions to make a solo gate operative.\n\n");
|
|
217
|
+
const identity = askUntil(streams, prompter, "1/3 your name (for human:<id>): ", quickstartIdentity);
|
|
218
|
+
if (!identity.ok)
|
|
219
|
+
return refusal(streams, "no valid human identity was entered; nothing was written");
|
|
220
|
+
const actor = identity.value;
|
|
221
|
+
const humanId = actor.slice("human:".length);
|
|
222
|
+
const channel = askUntil(streams, prompter, "2/3 button location (1 terminal, 2 Telegram): ", channelAnswer);
|
|
223
|
+
if (!channel.ok)
|
|
224
|
+
return refusal(streams, "no channel was chosen; nothing was written");
|
|
225
|
+
streams.out(`${QUICKSTART_CLASSES.map((entry, index) => ` ${String(index + 1)}. ${entry.label}`).join("\n")}\n`);
|
|
226
|
+
const selected = askUntil(streams, prompter, "3/3 always ask (numbers, Enter for all): ", classesAnswer);
|
|
227
|
+
if (!selected.ok)
|
|
228
|
+
return refusal(streams, "no valid checklist was entered; nothing was written");
|
|
229
|
+
const policy = renderSoloPolicy(humanId, channel.value, selected.value);
|
|
230
|
+
const loaded = loadPolicyText(join(dir, "APPROVAL.md"), policy);
|
|
231
|
+
if (!loaded.ok) {
|
|
232
|
+
streams.err(`approval: generated policy failed validation: ${loaded.message}\n`);
|
|
233
|
+
return EXIT_IO;
|
|
234
|
+
}
|
|
235
|
+
let initOut = "";
|
|
236
|
+
let initErr = "";
|
|
237
|
+
const initCode = commandInit([], { out: (text) => { initOut += text; }, err: (text) => { initErr += text; } }, dir, { policyText: policy });
|
|
238
|
+
if (initCode !== EXIT_OK) {
|
|
239
|
+
streams.err(`${initErr}${initOut}`);
|
|
240
|
+
return initCode;
|
|
241
|
+
}
|
|
242
|
+
const envPath = envFilePathFor(join(dir, DEFAULT_LOG_PATH));
|
|
243
|
+
const envWrite = upsertEnvFileEntries(envPath, [{ key: HUMAN_ACTOR_ENV, value: actor }]);
|
|
244
|
+
if (!envWrite.ok) {
|
|
245
|
+
streams.err(`approval: ${envWrite.message}\n`);
|
|
246
|
+
return EXIT_IO;
|
|
247
|
+
}
|
|
248
|
+
if (channel.value === "telegram") {
|
|
249
|
+
const setupArgs = ["telegram", "--as", actor, "--dir", dir];
|
|
250
|
+
if (apiBase !== null)
|
|
251
|
+
setupArgs.push("--api-base", apiBase);
|
|
252
|
+
const setupCode = await commandSetupChannel(setupArgs, streams, dir, { ...deps.setup, prompter });
|
|
253
|
+
if (setupCode !== EXIT_OK)
|
|
254
|
+
return setupCode;
|
|
255
|
+
}
|
|
256
|
+
const onDisk = loadPolicy({ dir });
|
|
257
|
+
if (!onDisk.ok) {
|
|
258
|
+
streams.err(`approval: generated policy could not be reloaded for preflight: ${onDisk.message}\n`);
|
|
259
|
+
return EXIT_IO;
|
|
260
|
+
}
|
|
261
|
+
const resolution = resolveEnvironment(onDisk, envPath, deps.sourceRunner ?? defaultSourceRunner, {});
|
|
262
|
+
if (!resolution.ok) {
|
|
263
|
+
streams.err(`approval: quickstart environment could not be resolved: ${resolution.message}\n`);
|
|
264
|
+
return EXIT_IO;
|
|
265
|
+
}
|
|
266
|
+
const resolvedEnv = {};
|
|
267
|
+
for (const variable of resolution.variables) {
|
|
268
|
+
if (variable.value !== undefined)
|
|
269
|
+
resolvedEnv[variable.name] = variable.value;
|
|
270
|
+
else if (variable.declared) {
|
|
271
|
+
streams.err(`approval: ${variable.name} could not be resolved from this instance (${variable.source}); the policy remains unattested\n`);
|
|
272
|
+
return EXIT_IO;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
const doctor = await (deps.doctor ?? runQuickstartDoctor)(dir, actor, resolvedEnv, apiBase);
|
|
276
|
+
const preflight = doctorPreflight(doctor);
|
|
277
|
+
if (!preflight.ok) {
|
|
278
|
+
streams.err(preflight.message);
|
|
279
|
+
return preflight.code;
|
|
280
|
+
}
|
|
281
|
+
streams.out(`\nReview the exact policy that will become operative:\n\n${policy}\n`);
|
|
282
|
+
const understood = askUntil(streams, prompter, "type understood to attest these exact bytes: ", (answer) => answer.trim() === "understood"
|
|
283
|
+
? { ok: true, value: true }
|
|
284
|
+
: { ok: false, reason: "type understood exactly, or Ctrl-D to leave the policy unattested" });
|
|
285
|
+
if (!understood.ok)
|
|
286
|
+
return refusal(streams, "policy left unattested");
|
|
287
|
+
const attestation = appendAttestation(join(dir, DEFAULT_LOG_PATH), join(dir, "APPROVAL.md"), actor, { expectedSha256: policyBytesHash(Buffer.from(policy, "utf8")) });
|
|
288
|
+
if (!attestation.ok) {
|
|
289
|
+
streams.err(`approval: ${attestation.error.message}\n`);
|
|
290
|
+
return attestation.error.code === "corrupt-tail" ? EXIT_TORN_TAIL : EXIT_IO;
|
|
291
|
+
}
|
|
292
|
+
streams.out(`ready: ${String(selected.value.length)} selected class families ask ${actor} on ${channel.value}; other classified reversible actions use the autonomous default\n`);
|
|
293
|
+
streams.out(`activate: eval "$(approval env --dir ${shellQuote(dir)})"\n`);
|
|
294
|
+
streams.out("try: approval hook classify -- curl -X POST https://api.example.com/apply\n");
|
|
295
|
+
return EXIT_OK;
|
|
296
|
+
}
|
|
297
|
+
//# sourceMappingURL=quickstart.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quickstart.js","sourceRoot":"","sources":["../../../src/cli/quickstart.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading stored lines back out of the log, for `tail` and `export`.
|
|
3
|
+
*
|
|
4
|
+
* Read-only in the strictest sense: this module opens the file once for
|
|
5
|
+
* reading and returns the *stored bytes* of each complete line. It never
|
|
6
|
+
* re-serializes, never normalizes, never repairs. `export` is required to be
|
|
7
|
+
* byte-faithful, and the cheapest way to guarantee that is never to have a
|
|
8
|
+
* parsed object on the write path at all.
|
|
9
|
+
*
|
|
10
|
+
* Called only after {@link verify} has passed over the same file, so `count` is
|
|
11
|
+
* the number of complete lines verification vouched for. If the file no longer
|
|
12
|
+
* has that many lines, something is writing underneath us; that is reported as
|
|
13
|
+
* an I/O condition rather than papered over.
|
|
14
|
+
*/
|
|
15
|
+
export type ReadResult = {
|
|
16
|
+
ok: true;
|
|
17
|
+
lines: string[];
|
|
18
|
+
} | {
|
|
19
|
+
ok: false;
|
|
20
|
+
message: string;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* The first `count` complete (newline-terminated) lines of the log, exactly as
|
|
24
|
+
* stored, without their terminators.
|
|
25
|
+
*/
|
|
26
|
+
export declare function readCompleteLines(logPath: string, count: number): ReadResult;
|
|
27
|
+
/** Parse stored lines back into records for `--json` output. */
|
|
28
|
+
export declare function parseLines(logPath: string, lines: string[]): {
|
|
29
|
+
ok: true;
|
|
30
|
+
records: unknown[];
|
|
31
|
+
} | {
|
|
32
|
+
ok: false;
|
|
33
|
+
message: string;
|
|
34
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `approval render` — regenerate `.approval/QUEUE.md` from the log (SPEC.md
|
|
3
|
+
* §9.1, §10.1).
|
|
4
|
+
*
|
|
5
|
+
* As everywhere else in this CLI, no logic lives here. The projection is
|
|
6
|
+
* `channels/render-queue.ts`; this file splits argv, resolves paths, reads the
|
|
7
|
+
* clock **once at the edge** and hands the instant to core, and maps a result
|
|
8
|
+
* onto the frozen exit table.
|
|
9
|
+
*
|
|
10
|
+
* The clock is the point worth stating. The renderer is a pure function of
|
|
11
|
+
* (verified log, policy, `now`), which is what makes an identical log render to
|
|
12
|
+
* identical bytes. That property survives only if exactly one place reads a real
|
|
13
|
+
* clock, and this is it.
|
|
14
|
+
*
|
|
15
|
+
* `render` writes one file and never the log. A corrupt log refuses (exit 1)
|
|
16
|
+
* rather than producing a queue: a projection of a log that does not verify
|
|
17
|
+
* would be a screenshot of something nobody should be reading.
|
|
18
|
+
*/
|
|
19
|
+
import type { Streams } from "./main.js";
|
|
20
|
+
/** SPEC.md §9.1: where the queue projection lives. */
|
|
21
|
+
export declare const DEFAULT_QUEUE_PATH = ".approval/QUEUE.md";
|
|
22
|
+
export declare function commandRender(argv: string[], streams: Streams, cwd: string): number;
|