@mikeargento/bitgraph-player 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 +31 -1
- package/SPEC.md +5 -0
- package/dist/__tests__/init-play.test.d.ts +2 -0
- package/dist/__tests__/init-play.test.d.ts.map +1 -0
- package/dist/__tests__/init-play.test.js +302 -0
- package/dist/__tests__/init-play.test.js.map +1 -0
- package/dist/cli.js +127 -60
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/init.d.ts +26 -0
- package/dist/init.d.ts.map +1 -0
- package/dist/init.js +104 -0
- package/dist/init.js.map +1 -0
- package/dist/play.d.ts +24 -0
- package/dist/play.d.ts.map +1 -0
- package/dist/play.js +61 -0
- package/dist/play.js.map +1 -0
- package/dist/verdict.d.ts +27 -1
- package/dist/verdict.d.ts.map +1 -1
- package/dist/verdict.js +11 -23
- package/dist/verdict.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/init-play.test.ts +358 -0
- package/src/cli.ts +137 -59
- package/src/index.ts +5 -1
- package/src/init.ts +118 -0
- package/src/play.ts +78 -0
- package/src/verdict.ts +12 -8
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@ Deterministic evaluation of causal rules over BitGraph proof bundles.
|
|
|
5
5
|
BitGraph records. Player executes.
|
|
6
6
|
|
|
7
7
|
A BitGraph proof bundle establishes facts: these bits were recorded at
|
|
8
|
-
these causal positions,
|
|
8
|
+
these causal positions, anchored to a public timeline. Player
|
|
9
9
|
evaluates a rule over those facts and produces a verdict anyone can
|
|
10
10
|
reproduce from the bundle alone — no network, no clock, no account, no
|
|
11
11
|
trust in the machine that ran it first.
|
|
@@ -25,6 +25,24 @@ folders the BitGraph Folder writes). Exit codes: `0` TRUE, `1` FALSE,
|
|
|
25
25
|
`2` UNDETERMINED, `3` error. `--out file` writes the verdict to a file;
|
|
26
26
|
`--summary` prints a bundle reconnaissance to stderr.
|
|
27
27
|
|
|
28
|
+
## Start a rule
|
|
29
|
+
|
|
30
|
+
bitgraph-play init po.pdf delivery.jpg approval.pdf --out rule.json
|
|
31
|
+
|
|
32
|
+
`init` hashes the files you name and writes a rule skeleton with the
|
|
33
|
+
cast filled in: one role per file, digests computed, role names from
|
|
34
|
+
filenames. The skeleton does not run as written — `requires.ordering`
|
|
35
|
+
is a placeholder you must replace, because the trust floor is the
|
|
36
|
+
rule's own security policy and has no default, from the scaffolder or
|
|
37
|
+
anywhere else. Choose the floor, say what each digest means, refine the
|
|
38
|
+
claim, then run it.
|
|
39
|
+
|
|
40
|
+
`init` is a reserved first word as of 0.2.0. A rule file literally
|
|
41
|
+
named `init` is still reachable: write it as `./init`, or after `--`,
|
|
42
|
+
which ends option parsing (`bitgraph-play -- init bundle/`). When a
|
|
43
|
+
file named `init` exists in the working directory, the bare spelling is
|
|
44
|
+
refused as ambiguous rather than silently picking a mode.
|
|
45
|
+
|
|
28
46
|
## A rule
|
|
29
47
|
|
|
30
48
|
```json
|
|
@@ -79,6 +97,18 @@ agree with it.
|
|
|
79
97
|
|
|
80
98
|
## API
|
|
81
99
|
|
|
100
|
+
One call runs the whole pipeline — the CLI is built on the same
|
|
101
|
+
function, so embedding Player cannot drift from it:
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
import { play } from "@mikeargento/bitgraph-player";
|
|
105
|
+
|
|
106
|
+
const { verdict, bytes, exitCode } = await play("rule.json", "bundle/");
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
The pieces are exported individually for callers that already hold an
|
|
110
|
+
`AuditResult` or want to intercept a stage:
|
|
111
|
+
|
|
82
112
|
```ts
|
|
83
113
|
import { runAudit } from "@mikeargento/bitgraph-audit";
|
|
84
114
|
import {
|
package/SPEC.md
CHANGED
|
@@ -232,3 +232,8 @@ bundle contents MUST be byte-identical.
|
|
|
232
232
|
0 TRUE 1 FALSE 2 UNDETERMINED 3 error
|
|
233
233
|
|
|
234
234
|
Diagnostics go to stderr. Stdout carries verdict bytes only.
|
|
235
|
+
|
|
236
|
+
These requirements govern evaluation invocations. A command-line Player
|
|
237
|
+
MAY offer authoring conveniences (such as rule scaffolding) as distinct
|
|
238
|
+
subcommands; those are outside this specification and MUST NOT change
|
|
239
|
+
the behavior of evaluation invocations.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init-play.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/init-play.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
// Copyright (c) 2024-2026 Mike Argento. Licensed under the MIT License. See LICENSE.
|
|
2
|
+
/**
|
|
3
|
+
* Tests for the init scaffolder and the play() one-call pipeline.
|
|
4
|
+
*
|
|
5
|
+
* Same discipline as the rest of the suite: synthetic AuditResult
|
|
6
|
+
* fixtures, no ledger writes, no network. The CLI wiring tests spawn the
|
|
7
|
+
* built cli.js against temp files created under os.tmpdir().
|
|
8
|
+
*/
|
|
9
|
+
import { strict as assert } from "node:assert";
|
|
10
|
+
import { spawnSync } from "node:child_process";
|
|
11
|
+
import { createHash } from "node:crypto";
|
|
12
|
+
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
13
|
+
import { tmpdir } from "node:os";
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
import { fileURLToPath } from "node:url";
|
|
16
|
+
import { test } from "node:test";
|
|
17
|
+
import { ORDERING_PLACEHOLDER, roleNameForFile, scaffoldRule } from "../init.js";
|
|
18
|
+
import { play, playAudit, PlayError } from "../play.js";
|
|
19
|
+
import { parseRule, RuleError } from "../rule.js";
|
|
20
|
+
import { serializeVerdict } from "../verdict.js";
|
|
21
|
+
import { digestFor, makeAudit } from "./fixtures.js";
|
|
22
|
+
const CLI_PATH = fileURLToPath(new URL("../cli.js", import.meta.url));
|
|
23
|
+
function hexOf(text) {
|
|
24
|
+
return createHash("sha256").update(text).digest("hex");
|
|
25
|
+
}
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
// roleNameForFile
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
test("role names: extension dropped, path stripped, disallowed chars folded", () => {
|
|
30
|
+
assert.equal(roleNameForFile("purchase_order.pdf"), "purchase_order");
|
|
31
|
+
assert.equal(roleNameForFile("/some/dir/delivery note.jpg"), "delivery-note");
|
|
32
|
+
assert.equal(roleNameForFile("My Photo (1).jpg"), "My-Photo-1");
|
|
33
|
+
assert.equal(roleNameForFile("archive.tar.gz"), "archive.tar");
|
|
34
|
+
});
|
|
35
|
+
test("role names: backslash is an ordinary character, never a separator", () => {
|
|
36
|
+
// On POSIX `a\b.jpg` is one legal filename. The CLI hands scaffoldRule a
|
|
37
|
+
// platform-correct basename; this function must not re-split it.
|
|
38
|
+
assert.equal(roleNameForFile(String.raw `a\b.jpg`), "a-b");
|
|
39
|
+
assert.equal(roleNameForFile(String.raw `C:\photos\approval.png`), "C-photos-approval");
|
|
40
|
+
});
|
|
41
|
+
test("role names: grammar guards — never empty, never pure-integer, dotfiles keep their stem", () => {
|
|
42
|
+
assert.equal(roleNameForFile("2024.jpg"), "file-2024");
|
|
43
|
+
assert.equal(roleNameForFile("™️.jpg"), "file");
|
|
44
|
+
assert.equal(roleNameForFile(".gitignore"), "gitignore");
|
|
45
|
+
assert.equal(roleNameForFile("..."), "file");
|
|
46
|
+
});
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
// scaffoldRule
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
const ENTRIES = [
|
|
51
|
+
{ name: "po.pdf", sha256Hex: hexOf("po") },
|
|
52
|
+
{ name: "delivery.jpg", sha256Hex: hexOf("delivery") },
|
|
53
|
+
];
|
|
54
|
+
test("scaffold does not parse as written, and the only issue is the ordering placeholder", () => {
|
|
55
|
+
const text = scaffoldRule(ENTRIES);
|
|
56
|
+
assert.ok(text.includes(ORDERING_PLACEHOLDER));
|
|
57
|
+
assert.throws(() => parseRule(text), (err) => {
|
|
58
|
+
assert.ok(err instanceof RuleError);
|
|
59
|
+
const ruleErr = err;
|
|
60
|
+
assert.equal(ruleErr.issues.length, 1);
|
|
61
|
+
assert.match(ruleErr.issues[0], /requires\.ordering/);
|
|
62
|
+
return true;
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
test("scaffold parses once the floor is chosen; cast in input order; digests normalize", () => {
|
|
66
|
+
const text = scaffoldRule(ENTRIES).replace(ORDERING_PLACEHOLDER, "hash-linked");
|
|
67
|
+
const rule = parseRule(text);
|
|
68
|
+
assert.deepEqual(Object.keys(rule.cast), ["po", "delivery"]);
|
|
69
|
+
assert.equal(rule.cast["po"]?.digest, `sha256:${hexOf("po")}`);
|
|
70
|
+
assert.equal(rule.cast["po"]?.means, "po.pdf");
|
|
71
|
+
assert.equal(rule.requires.ordering, "hash-linked");
|
|
72
|
+
assert.deepEqual(rule.claim, {
|
|
73
|
+
all: [{ exists: "po" }, { exists: "delivery" }],
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
test("scaffold is deterministic; id depends on digests, not entry order", () => {
|
|
77
|
+
assert.equal(scaffoldRule(ENTRIES), scaffoldRule(ENTRIES));
|
|
78
|
+
const reversed = scaffoldRule([...ENTRIES].reverse());
|
|
79
|
+
const idOf = (text) => JSON.parse(text).id;
|
|
80
|
+
assert.equal(idOf(scaffoldRule(ENTRIES)), idOf(reversed));
|
|
81
|
+
assert.match(idOf(scaffoldRule(ENTRIES)), /^rule-[0-9a-f]{12}$/);
|
|
82
|
+
});
|
|
83
|
+
test("scaffold: colliding role names get ordinal suffixes in input order", () => {
|
|
84
|
+
const text = scaffoldRule([
|
|
85
|
+
{ name: "a/photo.jpg", sha256Hex: hexOf("one") },
|
|
86
|
+
{ name: "b/photo.png", sha256Hex: hexOf("two") },
|
|
87
|
+
{ name: "c/photo.webp", sha256Hex: hexOf("three") },
|
|
88
|
+
]).replace(ORDERING_PLACEHOLDER, "hash-linked");
|
|
89
|
+
assert.deepEqual(Object.keys(parseRule(text).cast), ["photo", "photo-2", "photo-3"]);
|
|
90
|
+
});
|
|
91
|
+
test("scaffold: a collision suffix never takes another file's natural name", () => {
|
|
92
|
+
// photo.png must NOT become "photo-2" while the real photo-2.jpg is
|
|
93
|
+
// pushed to "photo-2-2": an author editing the claim by role name would
|
|
94
|
+
// silently bind the wrong digest.
|
|
95
|
+
const text = scaffoldRule([
|
|
96
|
+
{ name: "photo.jpg", sha256Hex: hexOf("one") },
|
|
97
|
+
{ name: "photo.png", sha256Hex: hexOf("two") },
|
|
98
|
+
{ name: "photo-2.jpg", sha256Hex: hexOf("three") },
|
|
99
|
+
]).replace(ORDERING_PLACEHOLDER, "hash-linked");
|
|
100
|
+
const rule = parseRule(text);
|
|
101
|
+
assert.deepEqual(Object.keys(rule.cast), ["photo", "photo-3", "photo-2"]);
|
|
102
|
+
assert.equal(rule.cast["photo-2"]?.digest, `sha256:${hexOf("three")}`);
|
|
103
|
+
assert.equal(rule.cast["photo-3"]?.digest, `sha256:${hexOf("two")}`);
|
|
104
|
+
});
|
|
105
|
+
test("scaffold: backslash names keep their identity; degenerate names omit means", () => {
|
|
106
|
+
const text = scaffoldRule([
|
|
107
|
+
{ name: String.raw `a\b.jpg`, sha256Hex: hexOf("bs") },
|
|
108
|
+
{ name: "b.jpg", sha256Hex: hexOf("plain") },
|
|
109
|
+
]).replace(ORDERING_PLACEHOLDER, "hash-linked");
|
|
110
|
+
const rule = parseRule(text);
|
|
111
|
+
assert.deepEqual(Object.keys(rule.cast), ["a-b", "b"]);
|
|
112
|
+
assert.equal(rule.cast["a-b"]?.means, String.raw `a\b.jpg`);
|
|
113
|
+
assert.equal(rule.cast["b"]?.means, "b.jpg");
|
|
114
|
+
// A trailing-slash or empty name has no basename to assert: no means
|
|
115
|
+
// key at all, never means: "".
|
|
116
|
+
const degenerate = scaffoldRule([
|
|
117
|
+
{ name: "photos/", sha256Hex: hexOf("deg") },
|
|
118
|
+
]).replace(ORDERING_PLACEHOLDER, "hash-linked");
|
|
119
|
+
const degRule = parseRule(degenerate);
|
|
120
|
+
assert.deepEqual(Object.keys(degRule.cast), ["file"]);
|
|
121
|
+
assert.equal(degRule.cast["file"]?.means, undefined);
|
|
122
|
+
});
|
|
123
|
+
test("scaffold: __proto__ as a filename stem becomes an ordinary cast key", () => {
|
|
124
|
+
const text = scaffoldRule([{ name: "__proto__.jpg", sha256Hex: hexOf("p") }]).replace(ORDERING_PLACEHOLDER, "assumption-dependent");
|
|
125
|
+
const rule = parseRule(text);
|
|
126
|
+
assert.deepEqual(Object.keys(rule.cast), ["__proto__"]);
|
|
127
|
+
assert.equal(rule.cast["__proto__"]?.digest, `sha256:${hexOf("p")}`);
|
|
128
|
+
});
|
|
129
|
+
test("scaffold rejects empty input and malformed hex", () => {
|
|
130
|
+
assert.throws(() => scaffoldRule([]));
|
|
131
|
+
assert.throws(() => scaffoldRule([{ name: "a", sha256Hex: "ABC" }]));
|
|
132
|
+
assert.throws(() => scaffoldRule([{ name: "a", sha256Hex: hexOf("x").toUpperCase() }]));
|
|
133
|
+
});
|
|
134
|
+
// ---------------------------------------------------------------------------
|
|
135
|
+
// playAudit / play
|
|
136
|
+
// ---------------------------------------------------------------------------
|
|
137
|
+
const RULE_TEXT = JSON.stringify({
|
|
138
|
+
rule: "bitgraph-player/1",
|
|
139
|
+
id: "play-test",
|
|
140
|
+
cast: {
|
|
141
|
+
first: { digest: digestFor("first") },
|
|
142
|
+
second: { digest: digestFor("second") },
|
|
143
|
+
},
|
|
144
|
+
world: "closed",
|
|
145
|
+
requires: { ordering: "hash-linked" },
|
|
146
|
+
claim: { before: ["first", "second"] },
|
|
147
|
+
then: { label: "ok" },
|
|
148
|
+
});
|
|
149
|
+
function chainAudit() {
|
|
150
|
+
return makeAudit({
|
|
151
|
+
proofs: [
|
|
152
|
+
{ name: "p1", digestB64: digestFor("first"), epochId: "e1", counter: "1" },
|
|
153
|
+
{ name: "p2", digestB64: digestFor("second"), epochId: "e1", counter: "2", prev: "p1" },
|
|
154
|
+
],
|
|
155
|
+
partitions: [{ epochId: "e1", members: ["p1", "p2"], components: [["p1", "p2"]] }],
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
test("playAudit: verdict, bytes, and exit code agree with the pipeline pieces", () => {
|
|
159
|
+
const sha = hexOf(RULE_TEXT);
|
|
160
|
+
const result = playAudit(parseRule(RULE_TEXT), sha, chainAudit());
|
|
161
|
+
assert.equal(result.verdict.result, "TRUE");
|
|
162
|
+
assert.equal(result.exitCode, 0);
|
|
163
|
+
assert.equal(result.verdict.rule.sha256, sha);
|
|
164
|
+
assert.equal(result.bytes, serializeVerdict(result.verdict));
|
|
165
|
+
assert.equal(result.verdict.then?.label, "ok");
|
|
166
|
+
});
|
|
167
|
+
test("playAudit exit codes: FALSE is 1, UNDETERMINED is 2", () => {
|
|
168
|
+
const falseRule = parseRule(RULE_TEXT.replace('"before"', '"after"'));
|
|
169
|
+
assert.equal(playAudit(falseRule, hexOf("x"), chainAudit()).exitCode, 1);
|
|
170
|
+
const emptyAudit = makeAudit({ proofs: [] });
|
|
171
|
+
const rule = parseRule(RULE_TEXT);
|
|
172
|
+
assert.equal(playAudit(rule, hexOf("x"), emptyAudit).exitCode, 2);
|
|
173
|
+
});
|
|
174
|
+
test("play: missing rule file throws PlayError with stage rule-read", async () => {
|
|
175
|
+
await assert.rejects(() => play("/nonexistent/rule.json", "/nonexistent/bundle"), (err) => {
|
|
176
|
+
assert.ok(err instanceof PlayError);
|
|
177
|
+
assert.equal(err.stage, "rule-read");
|
|
178
|
+
return true;
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
test("play: invalid rule file throws RuleError before any bundle work", async () => {
|
|
182
|
+
const dir = mkdtempSync(join(tmpdir(), "player-play-"));
|
|
183
|
+
try {
|
|
184
|
+
const rulePath = join(dir, "rule.json");
|
|
185
|
+
writeFileSync(rulePath, '{"rule": "bitgraph-player/1"}');
|
|
186
|
+
await assert.rejects(() => play(rulePath, "/nonexistent/bundle"), (err) => err instanceof RuleError);
|
|
187
|
+
}
|
|
188
|
+
finally {
|
|
189
|
+
rmSync(dir, { recursive: true, force: true });
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
// ---------------------------------------------------------------------------
|
|
193
|
+
// CLI init wiring
|
|
194
|
+
// ---------------------------------------------------------------------------
|
|
195
|
+
test("cli init: hashes files, emits skeleton on stdout, exit 0", () => {
|
|
196
|
+
const dir = mkdtempSync(join(tmpdir(), "player-init-"));
|
|
197
|
+
try {
|
|
198
|
+
const po = join(dir, "po.pdf");
|
|
199
|
+
const delivery = join(dir, "delivery.jpg");
|
|
200
|
+
writeFileSync(po, "po-bytes");
|
|
201
|
+
writeFileSync(delivery, "delivery-bytes");
|
|
202
|
+
const run = spawnSync(process.execPath, [CLI_PATH, "init", po, delivery], {
|
|
203
|
+
encoding: "utf8",
|
|
204
|
+
});
|
|
205
|
+
assert.equal(run.status, 0, run.stderr);
|
|
206
|
+
const skeleton = JSON.parse(run.stdout);
|
|
207
|
+
assert.deepEqual(Object.keys(skeleton.cast), ["po", "delivery"]);
|
|
208
|
+
assert.equal(skeleton.cast["po"]?.digest, `sha256:${hexOf("po-bytes")}`);
|
|
209
|
+
assert.equal(skeleton.requires.ordering, ORDERING_PLACEHOLDER);
|
|
210
|
+
assert.match(run.stderr, /next steps:/);
|
|
211
|
+
}
|
|
212
|
+
finally {
|
|
213
|
+
rmSync(dir, { recursive: true, force: true });
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
test("cli init --out: writes the file, refuses to overwrite it, exit 3", () => {
|
|
217
|
+
const dir = mkdtempSync(join(tmpdir(), "player-init-out-"));
|
|
218
|
+
try {
|
|
219
|
+
const input = join(dir, "photo.jpg");
|
|
220
|
+
writeFileSync(input, "photo-bytes");
|
|
221
|
+
const out = join(dir, "rule.json");
|
|
222
|
+
const first = spawnSync(process.execPath, [CLI_PATH, "init", input, "--out", out], {
|
|
223
|
+
encoding: "utf8",
|
|
224
|
+
});
|
|
225
|
+
assert.equal(first.status, 0, first.stderr);
|
|
226
|
+
assert.equal(first.stdout, "");
|
|
227
|
+
const written = JSON.parse(readFileSync(out, "utf8"));
|
|
228
|
+
assert.equal(written.cast["photo"]?.digest, `sha256:${hexOf("photo-bytes")}`);
|
|
229
|
+
const second = spawnSync(process.execPath, [CLI_PATH, "init", input, "--out", out], {
|
|
230
|
+
encoding: "utf8",
|
|
231
|
+
});
|
|
232
|
+
assert.equal(second.status, 3);
|
|
233
|
+
assert.match(second.stderr, /refusing to overwrite/);
|
|
234
|
+
}
|
|
235
|
+
finally {
|
|
236
|
+
rmSync(dir, { recursive: true, force: true });
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
test("cli init: unreadable input is an error, exit 3; no files is usage, exit 3", () => {
|
|
240
|
+
const missing = spawnSync(process.execPath, [CLI_PATH, "init", "/nonexistent/file.jpg"], { encoding: "utf8" });
|
|
241
|
+
assert.equal(missing.status, 3);
|
|
242
|
+
assert.match(missing.stderr, /cannot hash/);
|
|
243
|
+
const empty = spawnSync(process.execPath, [CLI_PATH, "init"], { encoding: "utf8" });
|
|
244
|
+
assert.equal(empty.status, 3);
|
|
245
|
+
assert.match(empty.stderr, /usage:/);
|
|
246
|
+
});
|
|
247
|
+
test("cli: a file named init in cwd makes `bitgraph-play init …` a loud error, not a silent mode pick", () => {
|
|
248
|
+
const dir = mkdtempSync(join(tmpdir(), "player-init-shadow-"));
|
|
249
|
+
try {
|
|
250
|
+
writeFileSync(join(dir, "init"), "{}");
|
|
251
|
+
writeFileSync(join(dir, "photo.jpg"), "bytes");
|
|
252
|
+
const run = spawnSync(process.execPath, [CLI_PATH, "init", "photo.jpg"], {
|
|
253
|
+
encoding: "utf8",
|
|
254
|
+
cwd: dir,
|
|
255
|
+
});
|
|
256
|
+
assert.equal(run.status, 3);
|
|
257
|
+
assert.match(run.stderr, /ambiguous/);
|
|
258
|
+
assert.equal(run.stdout, "");
|
|
259
|
+
}
|
|
260
|
+
finally {
|
|
261
|
+
rmSync(dir, { recursive: true, force: true });
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
test("cli: -- forces evaluate mode, so a rule file named init is still reachable", () => {
|
|
265
|
+
const dir = mkdtempSync(join(tmpdir(), "player-init-dashdash-"));
|
|
266
|
+
try {
|
|
267
|
+
// An invalid rule proves the point: init-mode would hash it and exit 0;
|
|
268
|
+
// evaluate mode must report an invalid rule with exit 3.
|
|
269
|
+
writeFileSync(join(dir, "init"), "{}");
|
|
270
|
+
const run = spawnSync(process.execPath, [CLI_PATH, "--", "init", dir], {
|
|
271
|
+
encoding: "utf8",
|
|
272
|
+
cwd: dir,
|
|
273
|
+
});
|
|
274
|
+
assert.equal(run.status, 3);
|
|
275
|
+
assert.match(run.stderr, /invalid rule/);
|
|
276
|
+
}
|
|
277
|
+
finally {
|
|
278
|
+
rmSync(dir, { recursive: true, force: true });
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
test("PLAYER_VERSION matches package.json (the constant must not drift across releases)", async () => {
|
|
282
|
+
const { PLAYER_VERSION } = await import("../verdict.js");
|
|
283
|
+
const pkg = JSON.parse(readFileSync(new URL("../../package.json", import.meta.url), "utf8"));
|
|
284
|
+
assert.equal(PLAYER_VERSION, pkg.version);
|
|
285
|
+
});
|
|
286
|
+
test("cli evaluate: unchanged contract, invalid rule reports issues on stderr, exit 3", () => {
|
|
287
|
+
const dir = mkdtempSync(join(tmpdir(), "player-eval-"));
|
|
288
|
+
try {
|
|
289
|
+
const rulePath = join(dir, "rule.json");
|
|
290
|
+
writeFileSync(rulePath, "{}");
|
|
291
|
+
const run = spawnSync(process.execPath, [CLI_PATH, rulePath, dir], {
|
|
292
|
+
encoding: "utf8",
|
|
293
|
+
});
|
|
294
|
+
assert.equal(run.status, 3);
|
|
295
|
+
assert.match(run.stderr, /invalid rule/);
|
|
296
|
+
assert.equal(run.stdout, "");
|
|
297
|
+
}
|
|
298
|
+
finally {
|
|
299
|
+
rmSync(dir, { recursive: true, force: true });
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
//# sourceMappingURL=init-play.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init-play.test.js","sourceRoot":"","sources":["../../src/__tests__/init-play.test.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.js
CHANGED
|
@@ -2,28 +2,38 @@
|
|
|
2
2
|
// Copyright (c) 2024-2026 Mike Argento. Licensed under the MIT License. See LICENSE.
|
|
3
3
|
/**
|
|
4
4
|
* bitgraph-play <rule.json> <bundle> [--out <file>] [--summary]
|
|
5
|
+
* bitgraph-play init <file>... [--out <rule.json>]
|
|
5
6
|
*
|
|
6
|
-
*
|
|
7
|
-
* .tar, .tar.gz, or .tgz) and writes the verdict JSON to
|
|
8
|
-
* --out). Offline: the audit pipeline and the evaluator make
|
|
9
|
-
* requests of any kind.
|
|
7
|
+
* Evaluate: runs a bitgraph-player/1 rule against a proof bundle
|
|
8
|
+
* (directory, .tar, .tar.gz, or .tgz) and writes the verdict JSON to
|
|
9
|
+
* stdout (or --out). Offline: the audit pipeline and the evaluator make
|
|
10
|
+
* no network requests of any kind.
|
|
10
11
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
12
|
+
* Init: hashes the given files and emits a rule skeleton with the cast
|
|
13
|
+
* filled in. The skeleton does not run as written: requires.ordering is
|
|
14
|
+
* a placeholder the author must replace — the trust floor has no
|
|
15
|
+
* default, and the scaffolder must not choose it either.
|
|
16
|
+
*
|
|
17
|
+
* Exit codes: 0 TRUE, 1 FALSE, 2 UNDETERMINED, 3 error (init: 0 or 3).
|
|
18
|
+
* Diagnostics go to stderr; stdout carries the verdict (or skeleton)
|
|
19
|
+
* bytes only.
|
|
13
20
|
*
|
|
14
21
|
* The process exits by setting process.exitCode and returning, never by
|
|
15
|
-
* process.exit(): exiting early would truncate
|
|
16
|
-
*
|
|
22
|
+
* process.exit(): exiting early would truncate output still draining to
|
|
23
|
+
* a pipe.
|
|
17
24
|
*/
|
|
18
25
|
import { createHash } from "node:crypto";
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
26
|
+
import { createReadStream, existsSync, writeFileSync } from "node:fs";
|
|
27
|
+
import { basename } from "node:path";
|
|
28
|
+
import { pipeline } from "node:stream/promises";
|
|
29
|
+
import { scaffoldRule } from "./init.js";
|
|
30
|
+
import { play, PlayError } from "./play.js";
|
|
31
|
+
import { RuleError } from "./rule.js";
|
|
25
32
|
function usage() {
|
|
26
33
|
process.stderr.write("usage: bitgraph-play <rule.json> <bundle> [--out <file>] [--summary]\n" +
|
|
34
|
+
" bitgraph-play init <file>... [--out <rule.json>]\n" +
|
|
35
|
+
' "--" ends option parsing; a rule file literally named "init" is\n' +
|
|
36
|
+
" evaluated with: bitgraph-play -- init <bundle>\n" +
|
|
27
37
|
" exit codes: 0 TRUE, 1 FALSE, 2 UNDETERMINED, 3 error\n");
|
|
28
38
|
return 3;
|
|
29
39
|
}
|
|
@@ -42,44 +52,97 @@ function printSummary(audit) {
|
|
|
42
52
|
lines.push(`unchained: ${audit.reconstruction.unchainedProofHashes.length}, unpartitioned: ${audit.reconstruction.unpartitionedProofHashes.length}`);
|
|
43
53
|
process.stderr.write(lines.join("\n") + "\n");
|
|
44
54
|
}
|
|
45
|
-
async function
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
55
|
+
async function sha256HexOfFile(path) {
|
|
56
|
+
const hash = createHash("sha256");
|
|
57
|
+
await pipeline(createReadStream(path), hash);
|
|
58
|
+
return hash.digest("hex");
|
|
59
|
+
}
|
|
60
|
+
function parseArgs(args) {
|
|
61
|
+
const parsed = { positional: [], summary: false };
|
|
50
62
|
for (let i = 0; i < args.length; i++) {
|
|
51
63
|
const arg = args[i];
|
|
52
|
-
if (arg === "--
|
|
64
|
+
if (arg === "--") {
|
|
65
|
+
// End of options: everything after is positional, dashes and all.
|
|
66
|
+
parsed.positional.push(...args.slice(i + 1));
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
else if (arg === "--out") {
|
|
53
70
|
const next = args[++i];
|
|
54
71
|
if (next === undefined || next.startsWith("-"))
|
|
55
|
-
return
|
|
56
|
-
outFile = next;
|
|
72
|
+
return undefined;
|
|
73
|
+
parsed.outFile = next;
|
|
57
74
|
}
|
|
58
75
|
else if (arg === "--summary") {
|
|
59
|
-
summary = true;
|
|
76
|
+
parsed.summary = true;
|
|
60
77
|
}
|
|
61
78
|
else if (arg.startsWith("-")) {
|
|
62
|
-
return
|
|
79
|
+
return undefined;
|
|
63
80
|
}
|
|
64
81
|
else {
|
|
65
|
-
positional.push(arg);
|
|
82
|
+
parsed.positional.push(arg);
|
|
66
83
|
}
|
|
67
84
|
}
|
|
68
|
-
|
|
85
|
+
return parsed;
|
|
86
|
+
}
|
|
87
|
+
async function runInit(args) {
|
|
88
|
+
if (args.summary)
|
|
69
89
|
return usage();
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
catch (err) {
|
|
76
|
-
process.stderr.write(`error: cannot read rule file: ${err.message}\n`);
|
|
90
|
+
const files = args.positional;
|
|
91
|
+
if (files.length === 0)
|
|
92
|
+
return usage();
|
|
93
|
+
if (args.outFile !== undefined && existsSync(args.outFile)) {
|
|
94
|
+
process.stderr.write(`error: refusing to overwrite existing file: ${args.outFile}\n`);
|
|
77
95
|
return 3;
|
|
78
96
|
}
|
|
79
|
-
const
|
|
80
|
-
|
|
97
|
+
const entries = [];
|
|
98
|
+
for (const file of files) {
|
|
99
|
+
let sha256Hex;
|
|
100
|
+
try {
|
|
101
|
+
sha256Hex = await sha256HexOfFile(file);
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
process.stderr.write(`error: cannot hash ${file}: ${err.message}\n`);
|
|
105
|
+
return 3;
|
|
106
|
+
}
|
|
107
|
+
// Platform-correct basename here, at the boundary: scaffoldRule
|
|
108
|
+
// itself splits only on "/" so its output is machine-independent.
|
|
109
|
+
entries.push({ name: basename(file), sha256Hex });
|
|
110
|
+
process.stderr.write(` sha256:${sha256Hex} ${file}\n`);
|
|
111
|
+
}
|
|
112
|
+
const skeleton = scaffoldRule(entries);
|
|
113
|
+
if (args.outFile !== undefined) {
|
|
114
|
+
// Exclusive write: the pre-hash existsSync is only a fast-fail, and
|
|
115
|
+
// hashing is unbounded in duration, so the refusal must hold at the
|
|
116
|
+
// write itself or a file created mid-hash is silently clobbered.
|
|
117
|
+
try {
|
|
118
|
+
writeFileSync(args.outFile, skeleton, { flag: "wx" });
|
|
119
|
+
}
|
|
120
|
+
catch (err) {
|
|
121
|
+
if (err.code === "EEXIST") {
|
|
122
|
+
process.stderr.write(`error: refusing to overwrite existing file: ${args.outFile}\n`);
|
|
123
|
+
return 3;
|
|
124
|
+
}
|
|
125
|
+
throw err;
|
|
126
|
+
}
|
|
127
|
+
process.stderr.write(`wrote ${args.outFile}\n`);
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
process.stdout.write(skeleton);
|
|
131
|
+
}
|
|
132
|
+
process.stderr.write("\nnext steps:\n" +
|
|
133
|
+
' 1. set requires.ordering — "hash-linked" accepts only hash-link evidence;\n' +
|
|
134
|
+
' "assumption-dependent" also accepts counter and anchor evidence\n' +
|
|
135
|
+
" 2. say what each digest means; refine the claim (before / after / between / not)\n" +
|
|
136
|
+
" 3. bitgraph-play <rule.json> <bundle>\n");
|
|
137
|
+
return 0;
|
|
138
|
+
}
|
|
139
|
+
async function runEvaluate(args) {
|
|
140
|
+
if (args.positional.length !== 2)
|
|
141
|
+
return usage();
|
|
142
|
+
const [rulePath, bundlePath] = args.positional;
|
|
143
|
+
let result;
|
|
81
144
|
try {
|
|
82
|
-
|
|
145
|
+
result = await play(rulePath, bundlePath);
|
|
83
146
|
}
|
|
84
147
|
catch (err) {
|
|
85
148
|
if (err instanceof RuleError) {
|
|
@@ -87,39 +150,43 @@ async function main() {
|
|
|
87
150
|
for (const issue of err.issues)
|
|
88
151
|
process.stderr.write(` - ${issue}\n`);
|
|
89
152
|
}
|
|
153
|
+
else if (err instanceof PlayError) {
|
|
154
|
+
process.stderr.write(`error: ${err.message}\n`);
|
|
155
|
+
}
|
|
90
156
|
else {
|
|
91
157
|
process.stderr.write(`error: ${err.message}\n`);
|
|
92
158
|
}
|
|
93
159
|
return 3;
|
|
94
160
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
audit = await runAudit(bundlePath);
|
|
98
|
-
}
|
|
99
|
-
catch (err) {
|
|
100
|
-
process.stderr.write(`error: bundle audit failed: ${err.message}\n`);
|
|
101
|
-
return 3;
|
|
102
|
-
}
|
|
103
|
-
const resolutions = resolveCast(rule.cast, audit);
|
|
104
|
-
const evaluation = evaluate(rule, resolutions, audit);
|
|
105
|
-
const verdict = buildVerdict(rule, ruleSha256Hex, resolutions, evaluation, audit);
|
|
106
|
-
const bytes = serializeVerdict(verdict);
|
|
107
|
-
if (outFile !== undefined) {
|
|
108
|
-
writeFileSync(outFile, bytes);
|
|
161
|
+
if (args.outFile !== undefined) {
|
|
162
|
+
writeFileSync(args.outFile, result.bytes);
|
|
109
163
|
}
|
|
110
164
|
else {
|
|
111
|
-
process.stdout.write(bytes);
|
|
165
|
+
process.stdout.write(result.bytes);
|
|
112
166
|
}
|
|
113
|
-
if (summary)
|
|
114
|
-
printSummary(audit);
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
167
|
+
if (args.summary)
|
|
168
|
+
printSummary(result.audit);
|
|
169
|
+
return result.exitCode;
|
|
170
|
+
}
|
|
171
|
+
async function main() {
|
|
172
|
+
const argv = process.argv.slice(2);
|
|
173
|
+
// "--" as the first token forces evaluate mode: parseArgs treats
|
|
174
|
+
// everything after it as positional, so a rule file literally named
|
|
175
|
+
// "init" is reachable as `bitgraph-play -- init <bundle>`.
|
|
176
|
+
const isInit = argv[0] === "init";
|
|
177
|
+
if (isInit && existsSync("init")) {
|
|
178
|
+
// Both readings are plausible here; a silent pick would hand a
|
|
179
|
+
// 0.1.1 caller a skeleton with exit 0 where the published contract
|
|
180
|
+
// returned a verdict. Refuse loudly instead.
|
|
181
|
+
process.stderr.write('error: a file named "init" exists here, so this command is ambiguous.\n' +
|
|
182
|
+
' to evaluate it as a rule: bitgraph-play -- init <bundle> (or ./init)\n' +
|
|
183
|
+
" to scaffold a rule: rename that file or run from another directory\n");
|
|
184
|
+
return 3;
|
|
122
185
|
}
|
|
186
|
+
const parsed = parseArgs(isInit ? argv.slice(1) : argv);
|
|
187
|
+
if (parsed === undefined)
|
|
188
|
+
return usage();
|
|
189
|
+
return isInit ? runInit(parsed) : runEvaluate(parsed);
|
|
123
190
|
}
|
|
124
191
|
main().then((code) => {
|
|
125
192
|
process.exitCode = code;
|