actradeck 0.5.1 → 0.6.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 CHANGED
@@ -1,8 +1,9 @@
1
1
  # actradeck
2
2
 
3
- > Bootstrap CLI for **ActraDeck** — a local-first control plane for coding agents (Claude
4
- > Code, Codex, …): observe them, redact secrets before they're stored, gate high-risk actions
5
- > behind approvals, and keep a tamper-evident audit trail.
3
+ > Bootstrap CLI for **ActraDeck** — a local-first audit cockpit for coding agents (Claude
4
+ > Code, Codex, …): observe them, redact secrets before they're stored, keep a tamper-evident
5
+ > audit trail, and relay approvals where supported (Claude Code in Attach, Codex in Managed
6
+ > Mode).
6
7
 
7
8
  This package is a **thin, dependency-free bootstrapper**. It does not contain the product —
8
9
  the full four-tier stack ships as a **signed GitHub Release** and a **signed GHCR image**.
@@ -21,11 +22,15 @@ npx actradeck@latest doctor # diagnose: platform / Node / pnpm / git / Do
21
22
  npx actradeck@latest install # verify + fetch the latest signed release, then quickstart
22
23
  npx actradeck@latest up # print the Docker cockpit command (prints only; runs nothing)
23
24
  npx actradeck@latest version # your CLI version + whether a newer stable release exists
25
+ npx actradeck@latest conformance < events.jsonl # (next release) check an adapter's stream vs the contract
24
26
  ```
25
27
 
26
- > **Not published yet.** The first npm publish is planned for **v0.5** (see the project's ADR
27
- > 0013). Until then the commands above describe the intended flow; the canonical, already-signed
28
- > way to get ActraDeck is the GitHub Release / GHCR image or `scripts/install.sh` from the repo.
28
+ > **`conformance` is not in the published CLI yet.** The four commands above ship in the current
29
+ > published `actradeck`; `conformance` was added after the latest release and lands in the next
30
+ > tagged one (npm publish is USER-GATED see ADR 0013). To run the checker today, use the from-clone
31
+ > path in the [ingestion contract §8](../../docs/ingestion-contract.md#8-verify-your-adapter-conformance-checker).
32
+ > The canonical, already-signed way to get the full product is the GitHub Release / GHCR image or
33
+ > `scripts/install.sh`.
29
34
 
30
35
  ### `install`
31
36
 
@@ -45,6 +50,36 @@ and provenance is verified unless you pass `--skip-provenance` (which requires y
45
50
  the reduced guarantee). `--skip-provenance` exists only for machines that cannot install the
46
51
  GitHub CLI.
47
52
 
53
+ ### `conformance`
54
+
55
+ Validate that a third-party ingestion adapter's event stream satisfies the ActraDeck ingestion
56
+ contract — **without cloning the monorepo**. (Ships in the next release; the currently published
57
+ CLI predates it — run the checker from a clone today, see the
58
+ [ingestion contract §8](../../docs/ingestion-contract.md#8-verify-your-adapter-conformance-checker).)
59
+ Capture your adapter's emitted NormalizedEvents as **JSONL** (one JSON object per line, in emission
60
+ order) and pipe them in:
61
+
62
+ ```sh
63
+ npx actradeck@latest conformance < events.jsonl # read JSONL from stdin
64
+ npx actradeck@latest conformance events.jsonl # or from a file
65
+ npx actradeck@latest conformance events.jsonl --json # machine-readable JSON report
66
+ ```
67
+
68
+ It checks the stream-level and cross-field invariants a single-event schema parse cannot see:
69
+ every event parses as a NormalizedEvent; `payload.kind === event_type`; per-session `timestamp` is
70
+ non-decreasing; and per-session `seq`, when present, is a dense 0-based counter (so the backend can
71
+ detect silent mid-stream drops — a session that emits no `seq` is a **warning**, not an error). A
72
+ repeated `event_id` or `seq` is a **warning**, not an error — an at-least-once retry is legitimate
73
+ and the backend dedupes it (§3.3 / §4.4). Redaction is **not** checked: the backend ingress
74
+ redaction floor is the sole redaction point, so an adapter cannot and need not prove it.
75
+
76
+ **Exit codes:** `0` = conformant (warnings allowed) · `1` = one or more errors · `2` = usage /
77
+ input error. The checker core is ActraDeck's canonical `checkConformance`, bundled into this CLI at
78
+ build time — the published package still has **zero runtime dependencies**. It is the same check as
79
+ the in-repo `scripts/check-conformance.mjs` (see `docs/ingestion-contract.md` §8); for piped output
80
+ it is byte-identical (an interactive TTY differs only in ANSI color). The input is read fully into
81
+ memory, which suits adapter sample streams rather than an unbounded live feed.
82
+
48
83
  ## Environment
49
84
 
50
85
  | Variable | Default | Meaning |
@@ -0,0 +1,41 @@
1
+ ActraDeck CLI — third-party notices for code bundled into dist/lib/conformance-core.js.
2
+ The `conformance` command inlines the checker's dependency closure at build time; the
3
+ licenses of those bundled packages follow.
4
+
5
+ ==============================================================================
6
+ uuid@11.1.1 (MIT)
7
+ ==============================================================================
8
+ The MIT License (MIT)
9
+
10
+ Copyright (c) 2010-2020 Robert Kieffer and other contributors
11
+
12
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17
+
18
+ ==============================================================================
19
+ zod@4.4.3 (MIT)
20
+ ==============================================================================
21
+ MIT License
22
+
23
+ Copyright (c) 2025 Colin McDonnell
24
+
25
+ Permission is hereby granted, free of charge, to any person obtaining a copy
26
+ of this software and associated documentation files (the "Software"), to deal
27
+ in the Software without restriction, including without limitation the rights
28
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
29
+ copies of the Software, and to permit persons to whom the Software is
30
+ furnished to do so, subject to the following conditions:
31
+
32
+ The above copyright notice and this permission notice shall be included in all
33
+ copies or substantial portions of the Software.
34
+
35
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
36
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
37
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
38
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
39
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
40
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
41
+ SOFTWARE.
package/dist/cli.d.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  import type { Deps } from "./lib/types.js";
2
2
  import { type InstallOpts } from "./commands/install.js";
3
+ import { type ConformanceOpts } from "./commands/conformance.js";
3
4
  export declare function parseInstallOpts(args: string[]): InstallOpts;
5
+ export declare function parseConformanceOpts(args: string[]): ConformanceOpts;
4
6
  export declare function run(deps: Deps, argv: string[]): Promise<number>;
package/dist/cli.js CHANGED
@@ -2,7 +2,8 @@ import { cmdDoctor } from "./commands/doctor.js";
2
2
  import { cmdUp } from "./commands/up.js";
3
3
  import { cmdVersion } from "./commands/version.js";
4
4
  import { cmdInstall } from "./commands/install.js";
5
- const USAGE = `actradeck bootstrap CLI for ActraDeck (local-first control plane for coding agents)
5
+ import { cmdConformance } from "./commands/conformance.js";
6
+ const USAGE = `actradeck — bootstrap CLI for ActraDeck (local-first audit cockpit for coding agents)
6
7
 
7
8
  Usage:
8
9
  actradeck <command> [options]
@@ -15,6 +16,10 @@ Commands:
15
16
  --skip-provenance checksum-only (explicit opt-out; not recommended)
16
17
  up Print the Docker cockpit bring-up command (prints only — does not run it).
17
18
  version Print this CLI's version and whether a newer stable release is available.
19
+ conformance Validate an adapter's JSONL event stream against the ingestion contract.
20
+ [file] read JSONL from a file (default: stdin)
21
+ --json emit a machine-readable JSON report instead of the summary
22
+ Exit: 0 = conformant · 1 = one or more errors · 2 = usage/setup error.
18
23
 
19
24
  Global:
20
25
  -h, --help show this help
@@ -53,6 +58,11 @@ export function parseInstallOpts(args) {
53
58
  }
54
59
  return { version, dryRun, skipProvenance };
55
60
  }
61
+ export function parseConformanceOpts(args) {
62
+ const json = args.includes("--json");
63
+ const file = args.find((a) => !a.startsWith("--"));
64
+ return file !== undefined ? { file, json } : { json };
65
+ }
56
66
  export async function run(deps, argv) {
57
67
  const cmd = argv[0];
58
68
  const rest = argv.slice(1);
@@ -76,6 +86,8 @@ export async function run(deps, argv) {
76
86
  return await cmdVersion(deps);
77
87
  case "install":
78
88
  return await cmdInstall(deps, parseInstallOpts(rest));
89
+ case "conformance":
90
+ return await cmdConformance(deps, parseConformanceOpts(rest));
79
91
  default:
80
92
  deps.io.err(`Unknown command: ${cmd}`);
81
93
  deps.io.out(USAGE);
@@ -0,0 +1,8 @@
1
+ import type { Deps } from "../lib/types.js";
2
+ import type { ConformanceReport } from "../lib/conformance-types.js";
3
+ export interface ConformanceOpts {
4
+ file?: string;
5
+ json: boolean;
6
+ }
7
+ export declare function cmdConformance(deps: Deps, opts: ConformanceOpts): Promise<number>;
8
+ export declare function renderHuman(report: ConformanceReport, malformed: Map<number, string>): string[];
@@ -0,0 +1,57 @@
1
+ export async function cmdConformance(deps, opts) {
2
+ let raw;
3
+ try {
4
+ raw = await deps.readInput(opts.file);
5
+ }
6
+ catch (err) {
7
+ const code = err.code ?? (err instanceof Error ? err.message : String(err));
8
+ deps.io.err(`conformance: could not read input (${code})`);
9
+ return 2;
10
+ }
11
+ const events = [];
12
+ const malformed = new Map();
13
+ for (const line of raw.split("\n")) {
14
+ const trimmed = line.trim();
15
+ if (trimmed.length === 0)
16
+ continue;
17
+ const index = events.length;
18
+ try {
19
+ events.push(JSON.parse(trimmed));
20
+ }
21
+ catch (err) {
22
+ malformed.set(index, err instanceof Error ? err.message : String(err));
23
+ events.push({});
24
+ }
25
+ }
26
+ const report = await deps.checkConformance(events);
27
+ if (opts.json) {
28
+ deps.io.out(JSON.stringify(report, null, 2));
29
+ return report.ok ? 0 : 1;
30
+ }
31
+ for (const line of renderHuman(report, malformed))
32
+ deps.io.out(line);
33
+ return report.ok ? 0 : 1;
34
+ }
35
+ export function renderHuman(report, malformed) {
36
+ const lines = [];
37
+ lines.push(`conformance: ${report.total} events · ${report.schemaValid} schema-valid · ${report.sessions} session(s)`);
38
+ for (const f of report.findings)
39
+ lines.push(findingLine(f, malformed));
40
+ if (report.ok) {
41
+ const suffix = report.warnings > 0 ? ` (${report.warnings} warning(s))` : "";
42
+ lines.push(`PASS — the stream conforms to the ingestion contract${suffix}`);
43
+ }
44
+ else {
45
+ lines.push(`FAIL — ${report.errors} error(s), ${report.warnings} warning(s)`);
46
+ }
47
+ return lines;
48
+ }
49
+ function findingLine(f, malformed) {
50
+ const where = f.index >= 0 ? `event ${f.index}` : "stream";
51
+ const sess = f.sessionId ? ` [session ${f.sessionId}]` : "";
52
+ const message = f.rule === "schema" && malformed.has(f.index)
53
+ ? `not valid JSON: ${malformed.get(f.index)}`
54
+ : f.message;
55
+ const tag = f.severity === "error" ? "ERROR" : "WARN";
56
+ return ` ${tag} ${where}${sess} (${f.rule}): ${message}`;
57
+ }