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,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `approval channel web` — the runtime half of the local web queue channel
|
|
3
|
+
* (SPEC.md §5.1 `channels.web.port`, §9, §10.3, §10.4, §11 — APRV-25).
|
|
4
|
+
*
|
|
5
|
+
* As everywhere else in this CLI, **no logic lives here**. Rendering and the
|
|
6
|
+
* HTTP server are `channels/web.ts`; turning a submitted form into an event is
|
|
7
|
+
* `channels/contract.ts`'s `recordChannelDecision` and `channels/batch.ts`'s
|
|
8
|
+
* `recordBatchDecisions`, both of which call the human-only `decide()` in
|
|
9
|
+
* `core/gate.ts`. This file resolves configuration, supplies the live pending
|
|
10
|
+
* queue, wires the two together, and chooses an exit code.
|
|
11
|
+
*
|
|
12
|
+
* Three things it does that the channel deliberately cannot:
|
|
13
|
+
*
|
|
14
|
+
* 1. **It reads the log.** {@link buildPendingQueue} runs here, once per page
|
|
15
|
+
* view, and the channel is handed the resulting {@link ChannelRequest}s. A
|
|
16
|
+
* channel that read the log would be deriving the facts it is meant to be
|
|
17
|
+
* transporting. Because that read happens per *request* rather than per
|
|
18
|
+
* process, this channel needs no dispatch of its own (APRV-55): a request
|
|
19
|
+
* appended while the server is running appears on the next page load, and a
|
|
20
|
+
* decided or TTL-lapsed one disappears the same way. Pull channels get for
|
|
21
|
+
* free what the Telegram listener has to arrange with a per-cycle send.
|
|
22
|
+
* 2. **It declares who is approving.** `--as` / `APPROVAL_HUMAN`, never
|
|
23
|
+
* anything the browser sent — there is nothing in an unauthenticated form
|
|
24
|
+
* post that could name a person. SPEC.md §11: identity is config-declared,
|
|
25
|
+
* the trust boundary is the local machine, and the page says so in a banner
|
|
26
|
+
* because the page is where the human is looking.
|
|
27
|
+
* 3. **It holds the token.** `recordChannelDecision` returns the raw execution
|
|
28
|
+
* token to *this* handler, which hands it back to the channel as one-shot
|
|
29
|
+
* *notice text* at render time and keeps no copy. See `channels/web.ts`'s
|
|
30
|
+
* header for why this channel shows the token on the page while the Telegram
|
|
31
|
+
* channel refuses to put it in a chat — that asymmetry is flagged there.
|
|
32
|
+
*
|
|
33
|
+
* ## Port precedence
|
|
34
|
+
*
|
|
35
|
+
* `--port` > `channels.web.port` in the attested policy > 4680. Nothing else:
|
|
36
|
+
* no environment variable, because a port that moves when an unrelated variable
|
|
37
|
+
* is exported is a port an operator will eventually fail to find, and no
|
|
38
|
+
* `--host` at any precedence at all (`channels/web.ts` explains).
|
|
39
|
+
*
|
|
40
|
+
* ## Identity is required at startup
|
|
41
|
+
*
|
|
42
|
+
* Unlike `approval channel cli`, which may merely *list* a queue, this verb
|
|
43
|
+
* exists to collect decisions: its only output is a page with Grant and Reject
|
|
44
|
+
* buttons on it. Starting a server whose buttons cannot record anything would
|
|
45
|
+
* spend a human's attention and then refuse their answer, so a missing or
|
|
46
|
+
* non-human identity is a usage error (2) before the socket is bound.
|
|
47
|
+
*/
|
|
48
|
+
import type { Server } from "node:http";
|
|
49
|
+
import { type PayloadSource } from "../channels/tagging.js";
|
|
50
|
+
import { WebChannel } from "../channels/web.js";
|
|
51
|
+
import type { DecideOptions } from "../core/gate.js";
|
|
52
|
+
import { type PolicyLoadResult } from "../core/policy-load.js";
|
|
53
|
+
import type { Streams } from "./main.js";
|
|
54
|
+
/**
|
|
55
|
+
* `channels.web.port` from a loaded policy, or `null`.
|
|
56
|
+
*
|
|
57
|
+
* A policy that does not load, does not declare `channels`, or declares a port
|
|
58
|
+
* that is not a usable TCP port yields `null` — which means the default, not a
|
|
59
|
+
* crash: an operator whose policy has a typo in an optional cosmetic field
|
|
60
|
+
* should still get a queue page.
|
|
61
|
+
*/
|
|
62
|
+
export declare function policyWebPort(load: PolicyLoadResult): number | null;
|
|
63
|
+
export type PortResolution = {
|
|
64
|
+
ok: true;
|
|
65
|
+
port: number;
|
|
66
|
+
} | {
|
|
67
|
+
ok: false;
|
|
68
|
+
message: string;
|
|
69
|
+
};
|
|
70
|
+
/** `--port` > policy `channels.web.port` > {@link WEB_DEFAULT_PORT}. */
|
|
71
|
+
export declare function resolveWebPort(portFlag: string | null, fromPolicy: number | null): PortResolution;
|
|
72
|
+
/**
|
|
73
|
+
* Payload material for one action key, from `--payload-dir` — the same shape
|
|
74
|
+
* `approval channel cli` uses, deliberately, so an operator's payload directory
|
|
75
|
+
* works with either channel.
|
|
76
|
+
*
|
|
77
|
+
* An override since APRV-28: with no flag the bytes come from the payload store
|
|
78
|
+
* beside the log, so the ordinary path needs no directory at all. This answers
|
|
79
|
+
* first for the keys it covers, and the store answers for the rest.
|
|
80
|
+
*
|
|
81
|
+
* The tagger re-hashes whatever this returns and refuses anything that does not
|
|
82
|
+
* match the recorded binding, so a wrong file produces a visible skip rather
|
|
83
|
+
* than a rendering of bytes the token would refuse to execute.
|
|
84
|
+
*/
|
|
85
|
+
export declare function payloadSource(dir: string, complain: (message: string) => void): PayloadSource;
|
|
86
|
+
export interface StartWebChannelOptions {
|
|
87
|
+
/** The log to read the queue from, and to append decisions to. */
|
|
88
|
+
logPath: string;
|
|
89
|
+
/** The approver every decision is recorded against. Must be `human:<id>`. */
|
|
90
|
+
actor: string;
|
|
91
|
+
/** Where `APPROVAL.md` lives; passed to the tagger and to the gate. */
|
|
92
|
+
policy?: {
|
|
93
|
+
dir?: string;
|
|
94
|
+
file?: string;
|
|
95
|
+
};
|
|
96
|
+
/** Port to bind. `0` asks the OS for an ephemeral one (tests). */
|
|
97
|
+
port?: number;
|
|
98
|
+
/** Payload material for manual requests (SPEC.md §10.4). */
|
|
99
|
+
payload?: PayloadSource;
|
|
100
|
+
/** Where operational complaints go. Defaults to stderr. */
|
|
101
|
+
log?: (message: string) => void;
|
|
102
|
+
/** The display instant for each queue build. Injectable for tests. */
|
|
103
|
+
now?: () => string;
|
|
104
|
+
/** Extra gate options (an injected clock, a schema dir). */
|
|
105
|
+
gateOptions?: DecideOptions;
|
|
106
|
+
}
|
|
107
|
+
/** A running web channel. `close()` releases the socket. */
|
|
108
|
+
export interface RunningWebChannel {
|
|
109
|
+
server: Server;
|
|
110
|
+
channel: WebChannel;
|
|
111
|
+
port: number;
|
|
112
|
+
close(): Promise<void>;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Start the server and wire it to the gate.
|
|
116
|
+
*
|
|
117
|
+
* Exported for the tests and for the M5 daemon: the verb below is a thin shell
|
|
118
|
+
* around this, and a caller that wants a queue page inside its own process
|
|
119
|
+
* should not have to spawn a CLI to get one.
|
|
120
|
+
*/
|
|
121
|
+
export declare function startWebChannel(options: StartWebChannelOptions): Promise<RunningWebChannel>;
|
|
122
|
+
/**
|
|
123
|
+
* `approval channel web` — serve the queue until interrupted.
|
|
124
|
+
*
|
|
125
|
+
* Long-lived, like `approval channel telegram listen`: it returns a promise
|
|
126
|
+
* that settles on SIGINT/SIGTERM, which is why `main` treats `channel`
|
|
127
|
+
* specially. There is no `--once`: a page is fetched by a human whenever they
|
|
128
|
+
* choose to look, so "handle exactly one request and exit" would be a shape
|
|
129
|
+
* nobody wants. Tests drive {@link startWebChannel} directly instead.
|
|
130
|
+
*/
|
|
131
|
+
export declare function commandWeb(argv: string[], streams: Streams, cwd: string): number | Promise<number>;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `approval channel cli` (APRV-23) — the zero-config channel, driven over the
|
|
3
|
+
* plugin contract rather than around it.
|
|
4
|
+
*
|
|
5
|
+
* There is no second decision path in this codebase and this verb is not one.
|
|
6
|
+
* It builds the pending queue with `channels/tagging.ts`, hands each request to
|
|
7
|
+
* `channels/cli.ts` for rendering, and registers a decision handler whose entire
|
|
8
|
+
* body is a call to `recordChannelDecision` — which calls the human-only
|
|
9
|
+
* `decide()` in `core/gate.ts`. Every gate rule (human actor, TTL, budgets,
|
|
10
|
+
* attestation, idempotency, compare-and-append) applies here unchanged, because
|
|
11
|
+
* nothing here reimplements any of them.
|
|
12
|
+
*
|
|
13
|
+
* ## Interactive, and deliberately not by default
|
|
14
|
+
*
|
|
15
|
+
* A prompt that blocks is a prompt that hangs a pipeline. So the verb is
|
|
16
|
+
* interactive only when stdin is a TTY, or when `--interactive` says so
|
|
17
|
+
* explicitly; `--json` is never interactive. Anything else lists the queue and
|
|
18
|
+
* exits 0. That rule is in `--help` because an agent that shells out to this
|
|
19
|
+
* verb must be able to predict, before it spawns anything, whether the child
|
|
20
|
+
* will return.
|
|
21
|
+
*
|
|
22
|
+
* ## The reading aids (APRV-197)
|
|
23
|
+
*
|
|
24
|
+
* Two of them, and they are different in kind. The deterministic one is the
|
|
25
|
+
* `command_breakdown` line: derived by the classifier from the bound bytes,
|
|
26
|
+
* marked `[computed] (classifier)`, free, and always present for a command this
|
|
27
|
+
* runtime's own tokenizer can read. The other is the model gloss, which costs a
|
|
28
|
+
* subprocess and 10-15 seconds and is marked `(model, unverified)` on the line
|
|
29
|
+
* itself. Until APRV-197 only the Telegram listener attached the second, so an
|
|
30
|
+
* operator deciding here read the agent's raw summary and nothing else; the two
|
|
31
|
+
* surfaces now share `cli/gloss-attach.ts`. See {@link glossRunner} for when a
|
|
32
|
+
* model is asked at all — only under `--gloss`, and never on the `--json` path,
|
|
33
|
+
* which is not interactive and has nobody waiting at it.
|
|
34
|
+
*
|
|
35
|
+
* ## Identity is declared, not proved
|
|
36
|
+
*
|
|
37
|
+
* `--as human:<id>`, else `APPROVAL_HUMAN`. The trust boundary is the local
|
|
38
|
+
* machine: anyone who can set that variable and write to the log is inside it,
|
|
39
|
+
* so a decision recorded here proves that *someone with local control* answered,
|
|
40
|
+
* not *who*. Stated in `--help` rather than implied, because a reader who
|
|
41
|
+
* believes this authenticates anybody would be wrong in a way that matters. The
|
|
42
|
+
* identity is required only when a decision could be recorded — listing the
|
|
43
|
+
* queue asks nothing of anyone.
|
|
44
|
+
*
|
|
45
|
+
* ## Where the payload comes from
|
|
46
|
+
*
|
|
47
|
+
* v0.1's log records `payload_hash`, never the payload bytes, so the material to
|
|
48
|
+
* render comes from the payload store beside the log — `.approval/payloads/`,
|
|
49
|
+
* written by `approval request --payload` (APRV-28) — which is why this verb
|
|
50
|
+
* needs no flag at all in the ordinary case. `--payload-dir` remains as an
|
|
51
|
+
* override for an operator whose bytes live elsewhere: one JSON file per action
|
|
52
|
+
* key, consulted first. `channels/tagging.ts` hashes whatever it is given and refuses
|
|
53
|
+
* anything that does not match the recorded binding, so a wrong file is a
|
|
54
|
+
* refusal and never a rendering. A manual request with no material is *skipped*
|
|
55
|
+
* and reported — visibly, never silently, because a request missing from a queue
|
|
56
|
+
* is a request nobody will approve.
|
|
57
|
+
*
|
|
58
|
+
* ## The asynchronous exit code
|
|
59
|
+
*
|
|
60
|
+
* `main()` is synchronous by contract: `cli.js` assigns its return value to
|
|
61
|
+
* `process.exitCode`. The prompt loop is asynchronous (readline), so the
|
|
62
|
+
* interactive path returns {@link EXIT_OK} to `main` and assigns the real code
|
|
63
|
+
* to `process.exitCode` when the loop settles. Node exits with the last value
|
|
64
|
+
* assigned, so a refusal still exits 1. Every synchronous path — `--json`, a
|
|
65
|
+
* non-TTY listing, a usage error, an I/O fact — returns its code the ordinary
|
|
66
|
+
* way.
|
|
67
|
+
*/
|
|
68
|
+
import type { Streams } from "./main.js";
|
|
69
|
+
export declare function commandChannelCli(argv: string[], streams: Streams, cwd: string): number;
|
|
70
|
+
/** `approval channel <subcommand>` — `cli`, `web` (APRV-25), `telegram`. */
|
|
71
|
+
export declare function commandChannel(argv: string[], streams: Streams, cwd: string): number | Promise<number>;
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The checkpoint tap: custody, the offer, the prompt text, the signature
|
|
3
|
+
* (APRV-257, the delivery half of APRV-220).
|
|
4
|
+
*
|
|
5
|
+
* APRV-220 built the record and gave a human exactly one way to sign one:
|
|
6
|
+
* `approval log checkpoint` at a terminal, remembered. This file is what makes
|
|
7
|
+
* it happen without the remembering, and it is deliberately the ONLY file
|
|
8
|
+
* between a channel and a signature.
|
|
9
|
+
*
|
|
10
|
+
* ## Why custody lives here now
|
|
11
|
+
*
|
|
12
|
+
* `core/checkpoint.ts` takes the private key as a value and reads it from
|
|
13
|
+
* nowhere, so that one file decides where a checkpoint key may come from. Until
|
|
14
|
+
* this task that file was `cli/log-checkpoint.ts`, because the terminal verb was
|
|
15
|
+
* the only caller. It is not any more: the Telegram listener and the CLI channel
|
|
16
|
+
* both sign now. So the decision moved here rather than being copied, and
|
|
17
|
+
* `cli/log-checkpoint.ts` calls {@link resolveCheckpointKey} like everyone else.
|
|
18
|
+
* There is still exactly one place to read to learn every way a key can reach a
|
|
19
|
+
* signature, which was the whole property.
|
|
20
|
+
*
|
|
21
|
+
* Two sources, in this order, unchanged from APRV-220:
|
|
22
|
+
*
|
|
23
|
+
* 1. `--key-file <path>`, for a key an operator keeps outside the vault.
|
|
24
|
+
* 2. The credential vault, under `approval.checkpoint.key`. Encrypted at rest
|
|
25
|
+
* under the passphrase `vault.passphrase_env` names, which
|
|
26
|
+
* `core/child-env.ts` strips from every child this runtime spawns
|
|
27
|
+
* (APRV-205), behind a file whose reading classifies `account.credential`.
|
|
28
|
+
*
|
|
29
|
+
* There is no `--key` flag and no environment variable holding the key.
|
|
30
|
+
*
|
|
31
|
+
* ## Why an agent-launched process cannot reach any of this
|
|
32
|
+
*
|
|
33
|
+
* Three independent locks, and the tap adds none of its own — it inherits all
|
|
34
|
+
* three, which is why the tap can be built at all:
|
|
35
|
+
*
|
|
36
|
+
* 1. **Classification.** `approval log checkpoint` and `approval setup
|
|
37
|
+
* checkpoint` classify `policy.core` in `core/command-class.ts`, which the
|
|
38
|
+
* reference policy holds `human-only`, so the Claude Code hook denies both
|
|
39
|
+
* with `hook-class-human-only` before a process starts.
|
|
40
|
+
* 2. **The passphrase.** `core/child-env.ts` strips `vault.passphrase_env` from
|
|
41
|
+
* every child this runtime spawns, so a process an agent launched cannot
|
|
42
|
+
* open the vault even if it ran this code.
|
|
43
|
+
* 3. **The launch.** The listener holds the passphrase because a HUMAN
|
|
44
|
+
* exported it into the shell they started `approval up` in. Nothing an agent
|
|
45
|
+
* can do puts it into a process the agent controls.
|
|
46
|
+
*
|
|
47
|
+
* `tests/checkpoint-tap.test.ts` proves the first two and proves the third
|
|
48
|
+
* structurally: the hook's module graph never reaches this file.
|
|
49
|
+
*
|
|
50
|
+
* ## What the human is shown is what gets signed
|
|
51
|
+
*
|
|
52
|
+
* The offer carries a `(seq, hash)`; the prompt prints it; the signature covers
|
|
53
|
+
* it. The head may have moved several times over between the prompt and the tap
|
|
54
|
+
* — a phone is in a pocket and a daemon is not — and
|
|
55
|
+
* {@link ../core/checkpoint.js appendCheckpointAt} signs the head that was on
|
|
56
|
+
* the screen, checking first that this chain still carries those bytes at that
|
|
57
|
+
* seq. APRV-220's verify rule (a checkpoint signs any seq below its own) exists
|
|
58
|
+
* precisely so this is a legal record rather than a clever one.
|
|
59
|
+
*/
|
|
60
|
+
import { type CheckpointOffer } from "../core/checkpoint.js";
|
|
61
|
+
/** Where a policy is, spelled the way every CLI verb spells it. */
|
|
62
|
+
export interface PolicyWhere {
|
|
63
|
+
file?: string;
|
|
64
|
+
dir?: string;
|
|
65
|
+
}
|
|
66
|
+
/** Everything a surface needs to offer and take a checkpoint. */
|
|
67
|
+
export interface CheckpointTap {
|
|
68
|
+
logPath: string;
|
|
69
|
+
policy: PolicyWhere;
|
|
70
|
+
/** `--key-file`, absolute, or `null` for the vault. */
|
|
71
|
+
keyFile: string | null;
|
|
72
|
+
/** An explicit vault path, or `null` for the one beside the log. */
|
|
73
|
+
vault: string | null;
|
|
74
|
+
schemaDir?: string;
|
|
75
|
+
}
|
|
76
|
+
/** Why no key could be had. One code: the repair is the message, not a branch. */
|
|
77
|
+
export declare const CHECKPOINT_KEY_REFUSAL = "checkpoint-key-unreadable";
|
|
78
|
+
export type KeyResolution = {
|
|
79
|
+
ok: true;
|
|
80
|
+
privateKey: string;
|
|
81
|
+
} | {
|
|
82
|
+
ok: false;
|
|
83
|
+
code: string;
|
|
84
|
+
message: string;
|
|
85
|
+
};
|
|
86
|
+
/**
|
|
87
|
+
* The private key, or a refusal naming which source failed and how to fix it.
|
|
88
|
+
*
|
|
89
|
+
* Moved verbatim from `cli/log-checkpoint.ts` (APRV-257) so that the terminal
|
|
90
|
+
* verb, the Telegram listener and the CLI channel share one answer to "where
|
|
91
|
+
* may a checkpoint key come from". The sentences are unchanged: an operator who
|
|
92
|
+
* has seen this refusal once should not meet it in new words on another
|
|
93
|
+
* surface.
|
|
94
|
+
*/
|
|
95
|
+
export declare function resolveCheckpointKey(keyFile: string | null, logPath: string, vaultFlag: string | null, policyWhere: PolicyWhere, cwd: string,
|
|
96
|
+
/**
|
|
97
|
+
* Where the passphrase is read from. `process.env` in production.
|
|
98
|
+
*
|
|
99
|
+
* A seam and not a back door, and the same one `setup adapter` carries: it
|
|
100
|
+
* goes through {@link passphraseFrom}, which is the function `approval vault
|
|
101
|
+
* set` uses, and it never resolves `.approval/env` (SPEC.md §11.1 invariant
|
|
102
|
+
* 7). Injectable so a suite can prove the vault path without mutating an
|
|
103
|
+
* environment every other test in the process shares.
|
|
104
|
+
*/
|
|
105
|
+
env?: NodeJS.ProcessEnv): KeyResolution;
|
|
106
|
+
/**
|
|
107
|
+
* The checkpoint this log is owed, or `null`.
|
|
108
|
+
*
|
|
109
|
+
* Reads the policy first and gives up the moment it names no cadence, so a
|
|
110
|
+
* dispatch cycle on a gate that has never turned checkpoints on pays one policy
|
|
111
|
+
* load and no log walk. Everything after that is
|
|
112
|
+
* {@link ../core/checkpoint.js checkpointDue} over verified records, which is
|
|
113
|
+
* the same call the daemon's warning and doctor's row make: three surfaces,
|
|
114
|
+
* one rule, no arrangement in which they disagree.
|
|
115
|
+
*
|
|
116
|
+
* A log that does not verify produces no offer and no complaint. A chain that
|
|
117
|
+
* is not fit to be read is not fit to be signed either, and the surfaces that
|
|
118
|
+
* exist to shout about a bad chain — `approval log verify`, the daemon's
|
|
119
|
+
* re-proof, doctor's `log` row — are already shouting.
|
|
120
|
+
*/
|
|
121
|
+
export declare function checkpointOfferFor(tap: CheckpointTap, now?: number): CheckpointOffer | null;
|
|
122
|
+
/**
|
|
123
|
+
* What a human reads before they tap, on every channel.
|
|
124
|
+
*
|
|
125
|
+
* One text for every surface, because the thing being consented to is identical
|
|
126
|
+
* and a phone that phrased it differently from a terminal would be two claims
|
|
127
|
+
* about one gesture. The `(seq, hash)` is first and whole: it is the entire
|
|
128
|
+
* content of the signature, and an approver who cannot see what they are
|
|
129
|
+
* signing is not approving anything.
|
|
130
|
+
*
|
|
131
|
+
* The last line is the one that keeps this honest. Declining costs nothing —
|
|
132
|
+
* there is no path in this runtime from a checkpoint that is due to a refusal
|
|
133
|
+
* of anything — and a prompt that implied otherwise would be manufacturing
|
|
134
|
+
* pressure for a signature.
|
|
135
|
+
*/
|
|
136
|
+
export declare function checkpointPromptLines(offer: CheckpointOffer): string[];
|
|
137
|
+
export type CheckpointTapResult = {
|
|
138
|
+
ok: true;
|
|
139
|
+
seq: number;
|
|
140
|
+
signed: {
|
|
141
|
+
seq: number;
|
|
142
|
+
hash: string;
|
|
143
|
+
};
|
|
144
|
+
fingerprint: string;
|
|
145
|
+
} | {
|
|
146
|
+
ok: false;
|
|
147
|
+
code: string;
|
|
148
|
+
message: string;
|
|
149
|
+
};
|
|
150
|
+
/**
|
|
151
|
+
* Sign one head and append the record, on the machine the channel runs on.
|
|
152
|
+
*
|
|
153
|
+
* `head` is the `(seq, hash)` that was on the screen, handed back by the
|
|
154
|
+
* channel unchanged. It is a head rather than the whole offer on purpose: by
|
|
155
|
+
* tap time the offer's cadence arithmetic is hours stale and nothing should be
|
|
156
|
+
* tempted to read it, while the head is the one part that must survive
|
|
157
|
+
* verbatim.
|
|
158
|
+
*
|
|
159
|
+
* The key is resolved at TAP time and not at offer time, and that ordering is
|
|
160
|
+
* the point: a prompt sitting on a phone for an hour holds no key material
|
|
161
|
+
* anywhere, and a listener whose vault the operator has since re-keyed refuses
|
|
162
|
+
* the tap with a sentence rather than signing with something stale.
|
|
163
|
+
*/
|
|
164
|
+
export declare function signCheckpointOffer(tap: CheckpointTap, head: {
|
|
165
|
+
seq: number;
|
|
166
|
+
hash: string;
|
|
167
|
+
}, actor: string, channel: string, cwd: string): CheckpointTapResult;
|
|
168
|
+
/** What a channel says on the message it just edited, once a tap has landed. */
|
|
169
|
+
export declare function checkpointSignedLines(result: CheckpointTapResult): string[];
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import { boolFlag, parseFlags, stringFlag } from "./args.js";
|
|
3
|
+
import { EXIT_INTEGRITY, EXIT_IO, EXIT_OK, EXIT_USAGE } from "./exit-codes.js";
|
|
4
|
+
import { CODEX_HELP } from "./help.js";
|
|
5
|
+
import { usageErrorText } from "./usage.js";
|
|
6
|
+
import { strictDoctor } from "../codex/doctor.js";
|
|
7
|
+
import { checkBundle, prepareBundle } from "../codex/templates.js";
|
|
8
|
+
function emitError(streams, json, code, message) {
|
|
9
|
+
if (json)
|
|
10
|
+
streams.err(`${JSON.stringify({ error: { code, message } })}\n`);
|
|
11
|
+
else
|
|
12
|
+
streams.err(`approval: ${message}\n`);
|
|
13
|
+
}
|
|
14
|
+
function usage(streams, json, message) {
|
|
15
|
+
if (json)
|
|
16
|
+
emitError(streams, true, "usage", message);
|
|
17
|
+
else
|
|
18
|
+
streams.err(usageErrorText(message, CODEX_HELP));
|
|
19
|
+
return EXIT_USAGE;
|
|
20
|
+
}
|
|
21
|
+
function required(flags, name) {
|
|
22
|
+
const value = stringFlag(flags, name);
|
|
23
|
+
return value === null || value.length === 0 ? null : value;
|
|
24
|
+
}
|
|
25
|
+
export function commandCodex(argv, streams, cwd) {
|
|
26
|
+
const json = argv.includes("--json");
|
|
27
|
+
const subcommand = argv[0];
|
|
28
|
+
const rest = argv.slice(1);
|
|
29
|
+
if (subcommand === undefined || subcommand === "--help" || subcommand === "-h") {
|
|
30
|
+
streams.out(`${CODEX_HELP}\n`);
|
|
31
|
+
return subcommand === undefined ? EXIT_USAGE : EXIT_OK;
|
|
32
|
+
}
|
|
33
|
+
if (subcommand === "prepare") {
|
|
34
|
+
const parsed = parseFlags(rest, {
|
|
35
|
+
"--instance": "string",
|
|
36
|
+
"--workspace": "string",
|
|
37
|
+
"--primary": "string",
|
|
38
|
+
"--install-root": "string",
|
|
39
|
+
"--output": "string",
|
|
40
|
+
"--codex": "string",
|
|
41
|
+
"--node": "string",
|
|
42
|
+
"--json": "boolean",
|
|
43
|
+
"--help": "boolean",
|
|
44
|
+
"-h": "boolean",
|
|
45
|
+
});
|
|
46
|
+
if (!parsed.ok)
|
|
47
|
+
return usage(streams, json, parsed.message);
|
|
48
|
+
if (boolFlag(parsed.flags, "--help") || boolFlag(parsed.flags, "-h")) {
|
|
49
|
+
streams.out(`${CODEX_HELP}\n`);
|
|
50
|
+
return EXIT_OK;
|
|
51
|
+
}
|
|
52
|
+
if (parsed.positionals.length > 0)
|
|
53
|
+
return usage(streams, json, "prepare takes flags only");
|
|
54
|
+
const names = ["--instance", "--workspace", "--primary", "--install-root", "--output", "--codex", "--node"];
|
|
55
|
+
const values = Object.fromEntries(names.map((name) => [name, required(parsed.flags, name)]));
|
|
56
|
+
const missing = names.find((name) => values[name] === null);
|
|
57
|
+
if (missing !== undefined)
|
|
58
|
+
return usage(streams, json, `prepare requires ${missing}`);
|
|
59
|
+
const result = prepareBundle({
|
|
60
|
+
instanceId: values["--instance"],
|
|
61
|
+
workspace: resolve(cwd, values["--workspace"]),
|
|
62
|
+
primary: resolve(cwd, values["--primary"]),
|
|
63
|
+
installRoot: resolve(cwd, values["--install-root"]),
|
|
64
|
+
output: resolve(cwd, values["--output"]),
|
|
65
|
+
codexExecutable: resolve(cwd, values["--codex"]),
|
|
66
|
+
nodeExecutable: resolve(cwd, values["--node"]),
|
|
67
|
+
});
|
|
68
|
+
if (!result.ok) {
|
|
69
|
+
emitError(streams, json, result.code, result.message);
|
|
70
|
+
return result.code === "manifest-invalid" || result.code === "output-overlap" ? EXIT_INTEGRITY : EXIT_IO;
|
|
71
|
+
}
|
|
72
|
+
if (json)
|
|
73
|
+
streams.out(`${JSON.stringify({ ok: true, inert: true, output: result.output, files: result.files, manifest: result.manifest })}\n`);
|
|
74
|
+
else
|
|
75
|
+
streams.out(`Prepared inert Codex host bundle at ${result.output}. Review it; no host configuration changed.\n`);
|
|
76
|
+
return EXIT_OK;
|
|
77
|
+
}
|
|
78
|
+
if (subcommand === "setup") {
|
|
79
|
+
const parsed = parseFlags(rest, {
|
|
80
|
+
"--check": "string",
|
|
81
|
+
"--json": "boolean",
|
|
82
|
+
"--help": "boolean",
|
|
83
|
+
"-h": "boolean",
|
|
84
|
+
});
|
|
85
|
+
if (!parsed.ok)
|
|
86
|
+
return usage(streams, json, parsed.message);
|
|
87
|
+
if (boolFlag(parsed.flags, "--help") || boolFlag(parsed.flags, "-h")) {
|
|
88
|
+
streams.out(`${CODEX_HELP}\n`);
|
|
89
|
+
return EXIT_OK;
|
|
90
|
+
}
|
|
91
|
+
if (parsed.positionals.length > 0)
|
|
92
|
+
return usage(streams, json, "setup takes flags only");
|
|
93
|
+
const directory = required(parsed.flags, "--check");
|
|
94
|
+
if (directory === null)
|
|
95
|
+
return usage(streams, json, "setup requires --check <bundle>");
|
|
96
|
+
const result = checkBundle(resolve(cwd, directory));
|
|
97
|
+
if (!result.ok) {
|
|
98
|
+
emitError(streams, json, result.code, result.message);
|
|
99
|
+
return EXIT_INTEGRITY;
|
|
100
|
+
}
|
|
101
|
+
const response = {
|
|
102
|
+
ok: true,
|
|
103
|
+
inert: true,
|
|
104
|
+
ready: false,
|
|
105
|
+
bundle: resolve(cwd, directory),
|
|
106
|
+
files: result.files,
|
|
107
|
+
reason: "broker-and-runner-not-shipped",
|
|
108
|
+
};
|
|
109
|
+
if (json)
|
|
110
|
+
streams.out(`${JSON.stringify(response)}\n`);
|
|
111
|
+
else
|
|
112
|
+
streams.out("Bundle is internally consistent and inert. Broker and runner are not shipped; do not activate it.\n");
|
|
113
|
+
return EXIT_OK;
|
|
114
|
+
}
|
|
115
|
+
if (subcommand === "doctor") {
|
|
116
|
+
const parsed = parseFlags(rest, {
|
|
117
|
+
"--manifest": "string",
|
|
118
|
+
"--strict": "boolean",
|
|
119
|
+
"--json": "boolean",
|
|
120
|
+
"--help": "boolean",
|
|
121
|
+
"-h": "boolean",
|
|
122
|
+
});
|
|
123
|
+
if (!parsed.ok)
|
|
124
|
+
return usage(streams, json, parsed.message);
|
|
125
|
+
if (boolFlag(parsed.flags, "--help") || boolFlag(parsed.flags, "-h")) {
|
|
126
|
+
streams.out(`${CODEX_HELP}\n`);
|
|
127
|
+
return EXIT_OK;
|
|
128
|
+
}
|
|
129
|
+
if (parsed.positionals.length > 0)
|
|
130
|
+
return usage(streams, json, "doctor takes flags only");
|
|
131
|
+
if (!boolFlag(parsed.flags, "--strict"))
|
|
132
|
+
return usage(streams, json, "doctor requires --strict");
|
|
133
|
+
const manifest = required(parsed.flags, "--manifest");
|
|
134
|
+
if (manifest === null)
|
|
135
|
+
return usage(streams, json, "doctor requires --manifest <path>");
|
|
136
|
+
const result = strictDoctor(resolve(cwd, manifest));
|
|
137
|
+
if (!result.ok) {
|
|
138
|
+
if (json)
|
|
139
|
+
streams.err(`${JSON.stringify({ ok: false, ready: false, error: { code: result.code, message: result.message }, findings: result.report?.findings ?? [] })}\n`);
|
|
140
|
+
else {
|
|
141
|
+
streams.err(`approval: ${result.message}\n`);
|
|
142
|
+
for (const finding of result.report?.findings ?? []) {
|
|
143
|
+
streams.err(` ${finding.code}${finding.path === undefined ? "" : ` ${finding.path}`}: ${finding.message}\n`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return EXIT_INTEGRITY;
|
|
147
|
+
}
|
|
148
|
+
return EXIT_OK;
|
|
149
|
+
}
|
|
150
|
+
if (subcommand === "start" || subcommand === "serve") {
|
|
151
|
+
const parsed = parseFlags(rest, {
|
|
152
|
+
"--manifest": "string",
|
|
153
|
+
"--json": "boolean",
|
|
154
|
+
"--help": "boolean",
|
|
155
|
+
"-h": "boolean",
|
|
156
|
+
});
|
|
157
|
+
if (!parsed.ok)
|
|
158
|
+
return usage(streams, json, parsed.message);
|
|
159
|
+
if (boolFlag(parsed.flags, "--help") || boolFlag(parsed.flags, "-h")) {
|
|
160
|
+
streams.out(`${CODEX_HELP}\n`);
|
|
161
|
+
return EXIT_OK;
|
|
162
|
+
}
|
|
163
|
+
if (parsed.positionals.length > 0)
|
|
164
|
+
return usage(streams, json, `${subcommand} takes flags only`);
|
|
165
|
+
if (required(parsed.flags, "--manifest") === null)
|
|
166
|
+
return usage(streams, json, `${subcommand} requires --manifest <path>`);
|
|
167
|
+
emitError(streams, json, "codex-not-ready", `codex ${subcommand} is not implemented until APRV-325.2 and APRV-325.3 provide the broker and runner`);
|
|
168
|
+
return EXIT_INTEGRITY;
|
|
169
|
+
}
|
|
170
|
+
return usage(streams, json, `unknown codex subcommand ${JSON.stringify(subcommand)}`);
|
|
171
|
+
}
|
|
172
|
+
//# sourceMappingURL=codex.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex.js","sourceRoot":"","sources":["../../../src/cli/codex.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `approval coverage` — observed side effects, joined to the verified log
|
|
3
|
+
* (APRV-245, SPEC.md §10.1).
|
|
4
|
+
*
|
|
5
|
+
* ## What it answers
|
|
6
|
+
*
|
|
7
|
+
* "Here is everything the witnesses outside this runtime say happened, and here
|
|
8
|
+
* is what the log says about each one." Nothing more: the join is
|
|
9
|
+
* `core/coverage.ts`, the witnesses are `core/coverage-sources/`, and this file
|
|
10
|
+
* is argument parsing, source selection, and the rendering of a table.
|
|
11
|
+
*
|
|
12
|
+
* ## Why it exits 0 with gaps
|
|
13
|
+
*
|
|
14
|
+
* Because it is INFORMATIONAL, on exactly SPEC.md §10.1's rule for the APRV-145
|
|
15
|
+
* harness-start coverage in `approval status`: a coverage measurement is not an
|
|
16
|
+
* integrity verdict, and a control an operator learns to silence is worse than
|
|
17
|
+
* one that reports beside the verdict. A gap here is a question ("was this
|
|
18
|
+
* effect ever declared?"), and questions with legitimate answers must not fail
|
|
19
|
+
* a build. The two codes it can still emit are the filesystem's: 2 for a usage
|
|
20
|
+
* error and 4 for a log this process could not read, plus 3 for a torn tail,
|
|
21
|
+
* because a log it could not read is a report it did not make.
|
|
22
|
+
*
|
|
23
|
+
* ## Why a source that cannot be reached is not a gap
|
|
24
|
+
*
|
|
25
|
+
* A source reports `available: false` with a reason, and its effects are absent
|
|
26
|
+
* rather than uncovered. "`gh` is not on PATH" and "`gh` saw nothing" are
|
|
27
|
+
* different facts, and a report that flattened them would let a broken tool read
|
|
28
|
+
* as a clean bill of health. Every unavailable source prints its reason on its
|
|
29
|
+
* own line.
|
|
30
|
+
*
|
|
31
|
+
* ## Writes nothing, reads only verified records
|
|
32
|
+
*
|
|
33
|
+
* The log is read through `readVerifiedRecords` (SPEC.md §11.1 invariant 1) and
|
|
34
|
+
* nothing here appends, renders, or caches. Running it changes no state any
|
|
35
|
+
* later verdict depends on, which is what lets it be safe to run on a timer.
|
|
36
|
+
*/
|
|
37
|
+
import { type CoverageEntry } from "../core/coverage.js";
|
|
38
|
+
import type { Streams } from "./main.js";
|
|
39
|
+
/** One source's contribution to the report, ready to print or serialize. */
|
|
40
|
+
interface RenderedSource {
|
|
41
|
+
name: string;
|
|
42
|
+
available: boolean;
|
|
43
|
+
reason: string | null;
|
|
44
|
+
entries: CoverageEntry[];
|
|
45
|
+
observed: number;
|
|
46
|
+
covered: number;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The evidence column, as one short string a reader can act on.
|
|
50
|
+
*
|
|
51
|
+
* The qualifier says how the record was found, so that a weaker match is never
|
|
52
|
+
* read as a stronger one and the strongest is not read as the ordinary one.
|
|
53
|
+
* `(id)` is APRV-251's: the record names this exact effect by the provider's own
|
|
54
|
+
* identifier, which is a different claim from "a record of this class sits in
|
|
55
|
+
* this effect's window" and prints as one.
|
|
56
|
+
*/
|
|
57
|
+
export declare function evidenceText(entry: CoverageEntry): string;
|
|
58
|
+
/** The coverage line one source prints, in the shape the help promises. */
|
|
59
|
+
export declare function coverageLine(source: RenderedSource): string;
|
|
60
|
+
export declare function commandCoverage(argv: string[], streams: Streams, cwd: string): Promise<number>;
|
|
61
|
+
export {};
|