protect-mcp 0.7.6 → 0.9.2
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/CHANGELOG.md +64 -0
- package/README.md +2 -2
- package/dist/{chunk-6E2DHBAR.mjs → chunk-744JMCY4.mjs} +9 -1
- package/dist/chunk-AYNQIEN7.mjs +10 -0
- package/dist/{chunk-JCMDLN5I.mjs → chunk-CXW2EIRM.mjs} +2 -2
- package/dist/{chunk-LJQOALYR.mjs → chunk-FFVJL3KQ.mjs} +5 -564
- package/dist/{chunk-VTPZ4G5I.mjs → chunk-GHR65WVD.mjs} +1 -1
- package/dist/{chunk-WIPWNWMJ.mjs → chunk-IDUH2O4Q.mjs} +1 -0
- package/dist/chunk-JIDDQUSQ.mjs +568 -0
- package/dist/chunk-KMNXHGGT.mjs +94 -0
- package/dist/{chunk-WV4DKYE4.mjs → chunk-UWB5ALVO.mjs} +10 -12
- package/dist/claim-TUDH2WPB.mjs +201 -0
- package/dist/cli.js +610 -31
- package/dist/cli.mjs +308 -24
- package/dist/{ed25519-BSHMMVNX.mjs → ed25519-SQA3S2RV.mjs} +2 -1
- package/dist/hook-server.js +428 -0
- package/dist/hook-server.mjs +6 -2
- package/dist/{http-transport-JBORN27G.mjs → http-transport-D7C64PIA.mjs} +2 -2
- package/dist/index.d.mts +16 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +107 -18
- package/dist/index.mjs +14 -9
- package/dist/{signing-committed-QXCW24RF.mjs → signing-committed-TGWXSLAO.mjs} +4 -2
- package/package.json +1 -1
- package/policies/agent.cedar +0 -50
package/dist/cli.js
CHANGED
|
@@ -469,6 +469,7 @@ function signDecision(entry) {
|
|
|
469
469
|
if (entry.timing) payload.timing = entry.timing;
|
|
470
470
|
if (entry.swarm) payload.swarm = entry.swarm;
|
|
471
471
|
if (entry.payload_digest) payload.payload_digest = entry.payload_digest;
|
|
472
|
+
if (entry.enrichment) payload.enrichment = entry.enrichment;
|
|
472
473
|
if (entry.action_readback) payload.action_readback = entry.action_readback;
|
|
473
474
|
if (entry.deny_iteration) payload.deny_iteration = entry.deny_iteration;
|
|
474
475
|
const result = artifactsModule.createSignedArtifact(
|
|
@@ -5081,6 +5082,294 @@ var init_sha256 = __esm({
|
|
|
5081
5082
|
}
|
|
5082
5083
|
});
|
|
5083
5084
|
|
|
5085
|
+
// src/receipt-enrichment.ts
|
|
5086
|
+
function canonicalJson(value) {
|
|
5087
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
5088
|
+
const enc = (v) => {
|
|
5089
|
+
if (v === null || v === void 0) return "null";
|
|
5090
|
+
const t = typeof v;
|
|
5091
|
+
if (t === "number") return Number.isFinite(v) ? JSON.stringify(v) : "null";
|
|
5092
|
+
if (t === "boolean" || t === "string") return JSON.stringify(v);
|
|
5093
|
+
if (t === "bigint") return JSON.stringify(v.toString());
|
|
5094
|
+
if (t === "function" || t === "symbol") return "null";
|
|
5095
|
+
if (Array.isArray(v)) return "[" + v.map(enc).join(",") + "]";
|
|
5096
|
+
if (t === "object") {
|
|
5097
|
+
const o = v;
|
|
5098
|
+
if (seen.has(o)) return '"[circular]"';
|
|
5099
|
+
seen.add(o);
|
|
5100
|
+
const body = Object.keys(o).sort().map((k) => JSON.stringify(k) + ":" + enc(o[k])).join(",");
|
|
5101
|
+
seen.delete(o);
|
|
5102
|
+
return "{" + body + "}";
|
|
5103
|
+
}
|
|
5104
|
+
return "null";
|
|
5105
|
+
};
|
|
5106
|
+
return enc(value);
|
|
5107
|
+
}
|
|
5108
|
+
function sha256Hex2(s) {
|
|
5109
|
+
return bytesToHex(sha2562(new TextEncoder().encode(s)));
|
|
5110
|
+
}
|
|
5111
|
+
function deriveCapabilities(tool, input) {
|
|
5112
|
+
const t = String(tool || "").toLowerCase();
|
|
5113
|
+
let text = "";
|
|
5114
|
+
try {
|
|
5115
|
+
text = canonicalJson(input).toLowerCase();
|
|
5116
|
+
} catch {
|
|
5117
|
+
}
|
|
5118
|
+
const caps = /* @__PURE__ */ new Set();
|
|
5119
|
+
for (const r of RULES) {
|
|
5120
|
+
if (r.tool && r.tool.test(t)) caps.add(r.cap);
|
|
5121
|
+
if (r.text && r.text.test(text)) caps.add(r.cap);
|
|
5122
|
+
}
|
|
5123
|
+
return Array.from(caps).sort();
|
|
5124
|
+
}
|
|
5125
|
+
function deriveResource(input) {
|
|
5126
|
+
const o = input && typeof input === "object" ? input : {};
|
|
5127
|
+
const path = o.file_path ?? o.path ?? o.filePath ?? o.notebook_path ?? o.filename;
|
|
5128
|
+
if (typeof path === "string" && path.trim()) return { kind: "path", digest: sha256Hex2(path.replace(/\\/g, "/")) };
|
|
5129
|
+
const url = o.url ?? o.uri ?? o.endpoint ?? o.href;
|
|
5130
|
+
if (typeof url === "string" && url.trim()) {
|
|
5131
|
+
try {
|
|
5132
|
+
return { kind: "host", digest: sha256Hex2(new URL(url).host.toLowerCase()) };
|
|
5133
|
+
} catch {
|
|
5134
|
+
}
|
|
5135
|
+
}
|
|
5136
|
+
const cmd = o.command ?? o.cmd ?? o.script;
|
|
5137
|
+
if (typeof cmd === "string" && cmd.trim()) {
|
|
5138
|
+
const first = cmd.trim().split(/\s+/)[0];
|
|
5139
|
+
if (first) return { kind: "command", digest: sha256Hex2(first) };
|
|
5140
|
+
}
|
|
5141
|
+
return void 0;
|
|
5142
|
+
}
|
|
5143
|
+
function buildEnrichment(tool, input) {
|
|
5144
|
+
const e = {
|
|
5145
|
+
v: ENRICHMENT_VERSION,
|
|
5146
|
+
input_digest: sha256Hex2(canonicalJson(input ?? {})),
|
|
5147
|
+
capabilities: deriveCapabilities(tool, input)
|
|
5148
|
+
};
|
|
5149
|
+
const resource = deriveResource(input);
|
|
5150
|
+
if (resource) e.resource = resource;
|
|
5151
|
+
return e;
|
|
5152
|
+
}
|
|
5153
|
+
var ENRICHMENT_VERSION, RULES;
|
|
5154
|
+
var init_receipt_enrichment = __esm({
|
|
5155
|
+
"src/receipt-enrichment.ts"() {
|
|
5156
|
+
"use strict";
|
|
5157
|
+
init_sha256();
|
|
5158
|
+
init_utils();
|
|
5159
|
+
ENRICHMENT_VERSION = 1;
|
|
5160
|
+
RULES = [
|
|
5161
|
+
{ cap: "exec.shell", tool: /bash|shell|exec|terminal|run_command|command/ },
|
|
5162
|
+
{ cap: "fs.read", tool: /(^|[_.])(read|cat|glob|grep|search|ls|view|list_files|open)/ },
|
|
5163
|
+
{ cap: "fs.write", tool: /write|create_file|save|append|edit|patch|replace|update_file|multiedit|notebook/ },
|
|
5164
|
+
{ cap: "fs.delete", tool: /delete|remove|unlink|trash|(^|[_.])rm/ },
|
|
5165
|
+
{ cap: "net.egress", tool: /fetch|http|curl|wget|request|download|browse|navigate|webfetch|web_search|scrape/ },
|
|
5166
|
+
{ cap: "vcs", tool: /(^|[_.])git/, text: /\bgit\s+(commit|push|pull|clone|reset|checkout|branch|rebase|merge|tag)\b/ },
|
|
5167
|
+
{ cap: "package.install", text: /\b(npm|pnpm|yarn)\s+(i|install|add)\b|\bpip3?\s+install\b|\bgo\s+get\b|\bcargo\s+add\b|\bbrew\s+install\b|\bapt(-get)?\s+install\b|\bgem\s+install\b/ },
|
|
5168
|
+
{ cap: "secret.adjacent", text: /\.env\b|secret|credential|passwd|password|api[_-]?key|private[_-]?key|\.pem\b|\.key\b|id_rsa|bearer\s|aws_(access|secret)|authorization/ },
|
|
5169
|
+
{ cap: "destructive", text: /rm\s+-[a-z]*[rf]|\brmdir\b|drop\s+table|truncate\s+table|delete\s+from|reset\s+--hard|--force\b|\bmkfs\b|\bdd\s+if=|shutdown|reboot|kill\s+-9|>\s*\/dev\/sd/ },
|
|
5170
|
+
{ cap: "financial", text: /\b(order|trade|buy|sell|transfer|wire|payment|withdraw|deposit|swap|invoice|charge|refund|settle)\b/ },
|
|
5171
|
+
{ cap: "data.query", text: /\bselect\s+[\s\S]+\bfrom\b|\binsert\s+into\b|\bupdate\s+[\s\S]+\bset\b|\bdelete\s+from\b/ }
|
|
5172
|
+
];
|
|
5173
|
+
}
|
|
5174
|
+
});
|
|
5175
|
+
|
|
5176
|
+
// src/claim.ts
|
|
5177
|
+
var claim_exports = {};
|
|
5178
|
+
__export(claim_exports, {
|
|
5179
|
+
ANCHOR_SCHEMA: () => ANCHOR_SCHEMA,
|
|
5180
|
+
CLAIM_TYPE: () => CLAIM_TYPE,
|
|
5181
|
+
DEFAULT_LOG: () => DEFAULT_LOG,
|
|
5182
|
+
anchorClaim: () => anchorClaim,
|
|
5183
|
+
buildAnchorEnvelope: () => buildAnchorEnvelope,
|
|
5184
|
+
buildClaim: () => buildClaim,
|
|
5185
|
+
claimDigest: () => claimDigest,
|
|
5186
|
+
evaluate: () => evaluate,
|
|
5187
|
+
leafHash: () => leafHash,
|
|
5188
|
+
merkleRoot: () => merkleRoot,
|
|
5189
|
+
receiptToLeaf: () => receiptToLeaf,
|
|
5190
|
+
verifyClaim: () => verifyClaim
|
|
5191
|
+
});
|
|
5192
|
+
function sha256Hex3(input) {
|
|
5193
|
+
const bytes = typeof input === "string" ? new TextEncoder().encode(input) : input;
|
|
5194
|
+
return bytesToHex(sha2562(bytes));
|
|
5195
|
+
}
|
|
5196
|
+
function receiptToLeaf(e) {
|
|
5197
|
+
const p = e && typeof e.payload === "object" && e.payload || e;
|
|
5198
|
+
const dec = String(p.decision || e.decision || "").toLowerCase();
|
|
5199
|
+
const v = /den|block|reject|refus/.test(dec) ? "blocked" : /ask|approv|hold|escal|review|pending/.test(dec) ? "held" : "allowed";
|
|
5200
|
+
const enr = p.enrichment;
|
|
5201
|
+
const c = enr && Array.isArray(enr.capabilities) ? enr.capabilities.map(String).sort() : [];
|
|
5202
|
+
const tsRaw = e.issued_at || p.timestamp || p.issued_at;
|
|
5203
|
+
const ms = typeof tsRaw === "number" ? tsRaw : typeof tsRaw === "string" ? Date.parse(tsRaw) : NaN;
|
|
5204
|
+
const t = isFinite(ms) ? new Date(ms).toISOString() : "";
|
|
5205
|
+
const d = sha256Hex3(canonicalJson(e));
|
|
5206
|
+
return { d, v, c, t };
|
|
5207
|
+
}
|
|
5208
|
+
function leafHash(leaf) {
|
|
5209
|
+
return sha256Hex3(canonicalJson(leaf));
|
|
5210
|
+
}
|
|
5211
|
+
function merkleRoot(leafHashes) {
|
|
5212
|
+
if (leafHashes.length === 0) return sha256Hex3("scopeblind.claim.empty");
|
|
5213
|
+
let level = [...leafHashes].sort();
|
|
5214
|
+
while (level.length > 1) {
|
|
5215
|
+
const next = [];
|
|
5216
|
+
for (let i = 0; i < level.length; i += 2) {
|
|
5217
|
+
const a = level[i];
|
|
5218
|
+
const b = i + 1 < level.length ? level[i + 1] : level[i];
|
|
5219
|
+
next.push(sha256Hex3(a + b));
|
|
5220
|
+
}
|
|
5221
|
+
level = next;
|
|
5222
|
+
}
|
|
5223
|
+
return level[0];
|
|
5224
|
+
}
|
|
5225
|
+
function evaluate(pred, leaves) {
|
|
5226
|
+
if (pred.kind === "no_capability") {
|
|
5227
|
+
const matched2 = leaves.filter((l) => l.c.indexOf(pred.capability) >= 0).length;
|
|
5228
|
+
return { statement: `No action used capability "${pred.capability}"`, holds: matched2 === 0, matched: matched2 };
|
|
5229
|
+
}
|
|
5230
|
+
if (pred.kind === "only_capabilities") {
|
|
5231
|
+
const allow = new Set(pred.capabilities);
|
|
5232
|
+
const matched2 = leaves.filter((l) => !l.c.every((c) => allow.has(c))).length;
|
|
5233
|
+
return { statement: `All actions were confined to capabilities {${pred.capabilities.join(", ")}}`, holds: matched2 === 0, matched: matched2 };
|
|
5234
|
+
}
|
|
5235
|
+
if (pred.kind === "no_verdict") {
|
|
5236
|
+
const matched2 = leaves.filter((l) => l.v === pred.verdict).length;
|
|
5237
|
+
return { statement: `No action was ${pred.verdict}`, holds: matched2 === 0, matched: matched2 };
|
|
5238
|
+
}
|
|
5239
|
+
const matched = leaves.filter((l) => l.v === pred.verdict).length;
|
|
5240
|
+
return { statement: `${matched} action${matched === 1 ? " was" : "s were"} ${pred.verdict}`, holds: true, matched };
|
|
5241
|
+
}
|
|
5242
|
+
function messageHash(unsigned) {
|
|
5243
|
+
return sha2562(new TextEncoder().encode(canonicalJson(unsigned)));
|
|
5244
|
+
}
|
|
5245
|
+
function buildClaim(receipts, predicate, key, issuedAt) {
|
|
5246
|
+
const leaves = receipts.map(receiptToLeaf);
|
|
5247
|
+
const root = merkleRoot(leaves.map(leafHash));
|
|
5248
|
+
const claim = evaluate(predicate, leaves);
|
|
5249
|
+
const times = leaves.map((l) => l.t).filter(Boolean).sort();
|
|
5250
|
+
const unsigned = {
|
|
5251
|
+
type: CLAIM_TYPE,
|
|
5252
|
+
predicate,
|
|
5253
|
+
claim,
|
|
5254
|
+
scope: { total: leaves.length, from: times[0] || "", to: times[times.length - 1] || "" },
|
|
5255
|
+
record: { root },
|
|
5256
|
+
leaves,
|
|
5257
|
+
issuer: { kid: key.kid, publicKey: key.publicKey, issuer: key.issuer || "protect-mcp" },
|
|
5258
|
+
issued_at: issuedAt
|
|
5259
|
+
};
|
|
5260
|
+
const signature = bytesToHex(ed25519.sign(messageHash(unsigned), hexToBytes(key.privateKey)));
|
|
5261
|
+
return { ...unsigned, signature };
|
|
5262
|
+
}
|
|
5263
|
+
function verifyClaim(pack, overridePublicKey) {
|
|
5264
|
+
const reasons = [];
|
|
5265
|
+
const leaves = Array.isArray(pack.leaves) ? pack.leaves : [];
|
|
5266
|
+
const recomputedRoot = merkleRoot(leaves.map(leafHash));
|
|
5267
|
+
const root_ok = !!pack.record && recomputedRoot === pack.record.root;
|
|
5268
|
+
if (!root_ok) reasons.push("record commitment (Merkle root) does not match the disclosed decisions");
|
|
5269
|
+
const recomputed = evaluate(pack.predicate, leaves);
|
|
5270
|
+
const predicate_ok = !!pack.claim && recomputed.holds === pack.claim.holds && recomputed.matched === pack.claim.matched;
|
|
5271
|
+
if (!predicate_ok) reasons.push("claim result does not match the predicate recomputed over the disclosed decisions");
|
|
5272
|
+
let authentic = false;
|
|
5273
|
+
try {
|
|
5274
|
+
const { signature, ...unsigned } = pack;
|
|
5275
|
+
const pub = overridePublicKey || pack.issuer && pack.issuer.publicKey;
|
|
5276
|
+
if (pub && signature) {
|
|
5277
|
+
authentic = ed25519.verify(hexToBytes(signature), messageHash(unsigned), hexToBytes(pub));
|
|
5278
|
+
}
|
|
5279
|
+
} catch {
|
|
5280
|
+
}
|
|
5281
|
+
if (!authentic) reasons.push("signature does not verify against the issuer public key");
|
|
5282
|
+
return {
|
|
5283
|
+
valid: authentic && root_ok && predicate_ok,
|
|
5284
|
+
authentic,
|
|
5285
|
+
root_ok,
|
|
5286
|
+
predicate_ok,
|
|
5287
|
+
holds: !!(pack.claim && pack.claim.holds),
|
|
5288
|
+
matched: pack.claim ? pack.claim.matched : recomputed.matched,
|
|
5289
|
+
total: leaves.length,
|
|
5290
|
+
statement: pack.claim ? pack.claim.statement : recomputed.statement,
|
|
5291
|
+
reasons
|
|
5292
|
+
};
|
|
5293
|
+
}
|
|
5294
|
+
function anchorDeepSort(o) {
|
|
5295
|
+
if (o === null || typeof o !== "object") return o;
|
|
5296
|
+
if (Array.isArray(o)) return o.map(anchorDeepSort);
|
|
5297
|
+
const src = o;
|
|
5298
|
+
const out = {};
|
|
5299
|
+
for (const k of Object.keys(src).sort()) out[k] = anchorDeepSort(src[k]);
|
|
5300
|
+
return out;
|
|
5301
|
+
}
|
|
5302
|
+
function toBase64(bytes) {
|
|
5303
|
+
let bin = "";
|
|
5304
|
+
for (let i = 0; i < bytes.length; i++) bin += String.fromCharCode(bytes[i]);
|
|
5305
|
+
return btoa(bin);
|
|
5306
|
+
}
|
|
5307
|
+
function claimDigest(pack) {
|
|
5308
|
+
return sha256Hex3(canonicalJson(pack));
|
|
5309
|
+
}
|
|
5310
|
+
function buildAnchorEnvelope(pack, key, issuedAt) {
|
|
5311
|
+
const signed = {
|
|
5312
|
+
type: "evidence_pack",
|
|
5313
|
+
schema: ANCHOR_SCHEMA,
|
|
5314
|
+
anchors: "protect-mcp-claim",
|
|
5315
|
+
claim_digest: claimDigest(pack),
|
|
5316
|
+
record_root: pack.record.root,
|
|
5317
|
+
statement: pack.claim.statement,
|
|
5318
|
+
holds: pack.claim.holds,
|
|
5319
|
+
matched: pack.claim.matched,
|
|
5320
|
+
total: pack.scope.total,
|
|
5321
|
+
issued_at: issuedAt,
|
|
5322
|
+
verification_key: key.publicKey,
|
|
5323
|
+
disclosure: "internal"
|
|
5324
|
+
};
|
|
5325
|
+
const hash = sha2562(new TextEncoder().encode(JSON.stringify(anchorDeepSort(signed))));
|
|
5326
|
+
const digest = bytesToHex(hash);
|
|
5327
|
+
const signature = bytesToHex(ed25519.sign(hash, hexToBytes(key.privateKey)));
|
|
5328
|
+
return { ...signed, signature, digest };
|
|
5329
|
+
}
|
|
5330
|
+
async function anchorClaim(pack, key, opts) {
|
|
5331
|
+
const envelope = buildAnchorEnvelope(pack, key, opts.issuedAt);
|
|
5332
|
+
const base = (opts.log || DEFAULT_LOG).replace(/\/+$/, "");
|
|
5333
|
+
const doFetch = opts.fetchImpl || globalThis.fetch;
|
|
5334
|
+
if (!doFetch) return { ok: false, claim_digest: envelope.claim_digest, error: "fetch_unavailable", envelope };
|
|
5335
|
+
const encoded = toBase64(new TextEncoder().encode(JSON.stringify(envelope)));
|
|
5336
|
+
try {
|
|
5337
|
+
const resp = await doFetch(`${base}/fn/log/anchor-pack`, {
|
|
5338
|
+
method: "POST",
|
|
5339
|
+
headers: { "content-type": "application/json" },
|
|
5340
|
+
body: JSON.stringify({ encoded })
|
|
5341
|
+
});
|
|
5342
|
+
const data = await resp.json().catch(() => null);
|
|
5343
|
+
if (!resp.ok || !data || !data.ok || typeof data.seq !== "number") {
|
|
5344
|
+
return { ok: false, claim_digest: envelope.claim_digest, error: data && data.error || `http_${resp.status}`, envelope };
|
|
5345
|
+
}
|
|
5346
|
+
return {
|
|
5347
|
+
ok: true,
|
|
5348
|
+
claim_digest: envelope.claim_digest,
|
|
5349
|
+
seq: data.seq,
|
|
5350
|
+
entry_url: `${base}/fn/log/${data.seq}`,
|
|
5351
|
+
anchored_at: data.anchored_at,
|
|
5352
|
+
already_anchored: !!data.already_anchored,
|
|
5353
|
+
envelope
|
|
5354
|
+
};
|
|
5355
|
+
} catch {
|
|
5356
|
+
return { ok: false, claim_digest: envelope.claim_digest, error: "network_error", envelope };
|
|
5357
|
+
}
|
|
5358
|
+
}
|
|
5359
|
+
var CLAIM_TYPE, ANCHOR_SCHEMA, DEFAULT_LOG;
|
|
5360
|
+
var init_claim = __esm({
|
|
5361
|
+
"src/claim.ts"() {
|
|
5362
|
+
"use strict";
|
|
5363
|
+
init_sha256();
|
|
5364
|
+
init_utils();
|
|
5365
|
+
init_ed25519();
|
|
5366
|
+
init_receipt_enrichment();
|
|
5367
|
+
CLAIM_TYPE = "scopeblind.claim.v1";
|
|
5368
|
+
ANCHOR_SCHEMA = "scopeblind.protect-mcp.anchor.v1";
|
|
5369
|
+
DEFAULT_LOG = "https://scopeblind.com";
|
|
5370
|
+
}
|
|
5371
|
+
});
|
|
5372
|
+
|
|
5084
5373
|
// src/commitments/merkle.ts
|
|
5085
5374
|
function hashLeaf(leafBytes) {
|
|
5086
5375
|
const buf = new Uint8Array(leafBytes.length + 1);
|
|
@@ -5095,7 +5384,7 @@ function hashInternal(left, right) {
|
|
|
5095
5384
|
buf.set(right, 1 + left.length);
|
|
5096
5385
|
return sha2562(buf);
|
|
5097
5386
|
}
|
|
5098
|
-
function
|
|
5387
|
+
function merkleRoot2(leafHashes) {
|
|
5099
5388
|
if (leafHashes.length === 0) {
|
|
5100
5389
|
throw new Error("merkleRoot: cannot compute root of empty leaf set");
|
|
5101
5390
|
}
|
|
@@ -5104,8 +5393,8 @@ function merkleRoot(leafHashes) {
|
|
|
5104
5393
|
}
|
|
5105
5394
|
const n = leafHashes.length;
|
|
5106
5395
|
const k = largestPowerOfTwoLessThan(n);
|
|
5107
|
-
const left =
|
|
5108
|
-
const right =
|
|
5396
|
+
const left = merkleRoot2(leafHashes.slice(0, k));
|
|
5397
|
+
const right = merkleRoot2(leafHashes.slice(k));
|
|
5109
5398
|
return hashInternal(left, right);
|
|
5110
5399
|
}
|
|
5111
5400
|
function generateProof(leafHashes, index) {
|
|
@@ -5131,21 +5420,21 @@ function collectPath(leaves, index, out) {
|
|
|
5131
5420
|
const k = largestPowerOfTwoLessThan(n);
|
|
5132
5421
|
if (index < k) {
|
|
5133
5422
|
collectPath(leaves.slice(0, k), index, out);
|
|
5134
|
-
out.push(
|
|
5423
|
+
out.push(merkleRoot2(leaves.slice(k)));
|
|
5135
5424
|
} else {
|
|
5136
5425
|
collectPath(leaves.slice(k), index - k, out);
|
|
5137
|
-
out.push(
|
|
5426
|
+
out.push(merkleRoot2(leaves.slice(0, k)));
|
|
5138
5427
|
}
|
|
5139
5428
|
}
|
|
5140
|
-
function verifyProof(expectedRootHex,
|
|
5429
|
+
function verifyProof(expectedRootHex, leafHash2, proof) {
|
|
5141
5430
|
if (proof.index < 0 || proof.index >= proof.treeSize) return false;
|
|
5142
5431
|
if (proof.treeSize === 1) {
|
|
5143
|
-
return proof.siblings.length === 0 && bytesToHex(
|
|
5432
|
+
return proof.siblings.length === 0 && bytesToHex(leafHash2).toLowerCase() === expectedRootHex.toLowerCase();
|
|
5144
5433
|
}
|
|
5145
5434
|
let result;
|
|
5146
5435
|
try {
|
|
5147
5436
|
result = reconstructRoot(
|
|
5148
|
-
|
|
5437
|
+
leafHash2,
|
|
5149
5438
|
proof.index,
|
|
5150
5439
|
proof.treeSize,
|
|
5151
5440
|
proof.siblings
|
|
@@ -5155,12 +5444,12 @@ function verifyProof(expectedRootHex, leafHash, proof) {
|
|
|
5155
5444
|
}
|
|
5156
5445
|
return bytesToHex(result).toLowerCase() === expectedRootHex.toLowerCase();
|
|
5157
5446
|
}
|
|
5158
|
-
function reconstructRoot(
|
|
5447
|
+
function reconstructRoot(leafHash2, index, treeSize, siblings) {
|
|
5159
5448
|
if (treeSize === 1) {
|
|
5160
5449
|
if (siblings.length !== 0) {
|
|
5161
5450
|
throw new Error("reconstructRoot: extra siblings at single-leaf level");
|
|
5162
5451
|
}
|
|
5163
|
-
return
|
|
5452
|
+
return leafHash2;
|
|
5164
5453
|
}
|
|
5165
5454
|
if (siblings.length === 0) {
|
|
5166
5455
|
throw new Error("reconstructRoot: ran out of siblings before single-leaf");
|
|
@@ -5169,11 +5458,11 @@ function reconstructRoot(leafHash, index, treeSize, siblings) {
|
|
|
5169
5458
|
const outermostSibling = hexToBytes(siblings[siblings.length - 1]);
|
|
5170
5459
|
const innerSiblings = siblings.slice(0, -1);
|
|
5171
5460
|
if (index < k) {
|
|
5172
|
-
const leftHash = reconstructRoot(
|
|
5461
|
+
const leftHash = reconstructRoot(leafHash2, index, k, innerSiblings);
|
|
5173
5462
|
return hashInternal(leftHash, outermostSibling);
|
|
5174
5463
|
} else {
|
|
5175
5464
|
const rightHash = reconstructRoot(
|
|
5176
|
-
|
|
5465
|
+
leafHash2,
|
|
5177
5466
|
index - k,
|
|
5178
5467
|
treeSize - k,
|
|
5179
5468
|
innerSiblings
|
|
@@ -5319,7 +5608,7 @@ function signCommittedDecision(entry, committedFieldNames, signingKey, publicKey
|
|
|
5319
5608
|
if (committedFields.length > 0) {
|
|
5320
5609
|
const { sorted, leafBytes } = leavesFromFields(committedFields);
|
|
5321
5610
|
const leafHashes = leafBytes.map(hashLeaf);
|
|
5322
|
-
const root =
|
|
5611
|
+
const root = merkleRoot2(leafHashes);
|
|
5323
5612
|
committedFieldsRoot = bytesToHex(root);
|
|
5324
5613
|
sorted.forEach((f, i) => {
|
|
5325
5614
|
openings[f.name] = { name: f.name, value: f.value, salt: f.salt, index: i };
|
|
@@ -5337,8 +5626,8 @@ function signCommittedDecision(entry, committedFieldNames, signingKey, publicKey
|
|
|
5337
5626
|
payload.committed_field_names = committedFields.map((f) => f.name);
|
|
5338
5627
|
}
|
|
5339
5628
|
const canonical = jcs(payload);
|
|
5340
|
-
const
|
|
5341
|
-
const signatureBytes = ed25519.sign(
|
|
5629
|
+
const messageHash2 = sha2562(new TextEncoder().encode(canonical));
|
|
5630
|
+
const signatureBytes = ed25519.sign(messageHash2, hexToBytes(signingKey));
|
|
5342
5631
|
const signedReceipt = {
|
|
5343
5632
|
...payload,
|
|
5344
5633
|
signature: {
|
|
@@ -5490,9 +5779,9 @@ function verifyCommittedReceiptSignature(receipt) {
|
|
|
5490
5779
|
return null;
|
|
5491
5780
|
}
|
|
5492
5781
|
const { signature: _signature, ...payloadWithoutSig } = receipt;
|
|
5493
|
-
const
|
|
5782
|
+
const messageHash2 = sha2562(new TextEncoder().encode(jcs(payloadWithoutSig)));
|
|
5494
5783
|
try {
|
|
5495
|
-
return ed25519.verify(base64urlDecode(sig.sig),
|
|
5784
|
+
return ed25519.verify(base64urlDecode(sig.sig), messageHash2, hexToBytes(sig.public_key));
|
|
5496
5785
|
} catch {
|
|
5497
5786
|
return false;
|
|
5498
5787
|
}
|
|
@@ -6065,6 +6354,9 @@ async function handlePreToolUse(input, state) {
|
|
|
6065
6354
|
});
|
|
6066
6355
|
const payloadDigest = computePayloadDigest(input.toolInput);
|
|
6067
6356
|
const actionReadback = buildActionReadback(toolName, input.toolInput || {});
|
|
6357
|
+
const enrichment = buildEnrichment(toolName, input.toolInput || {});
|
|
6358
|
+
const inflightRec = state.inflightTools.get(requestId);
|
|
6359
|
+
if (inflightRec) inflightRec.enrichment = enrichment;
|
|
6068
6360
|
const swarm = {
|
|
6069
6361
|
...state.swarmContext,
|
|
6070
6362
|
...input.agentId && { agent_id: input.agentId },
|
|
@@ -6484,6 +6776,8 @@ function emitDecisionLog(state, entry) {
|
|
|
6484
6776
|
...entry.sandbox_state && { sandbox_state: entry.sandbox_state },
|
|
6485
6777
|
...entry.plan_receipt_id && { plan_receipt_id: entry.plan_receipt_id }
|
|
6486
6778
|
};
|
|
6779
|
+
const enr = state.inflightTools.get(log.request_id)?.enrichment;
|
|
6780
|
+
if (enr) log.enrichment = enr;
|
|
6487
6781
|
process.stderr.write(`[PROTECT_MCP] ${JSON.stringify(log)}
|
|
6488
6782
|
`);
|
|
6489
6783
|
try {
|
|
@@ -6846,6 +7140,7 @@ var init_hook_server = __esm({
|
|
|
6846
7140
|
init_http_server();
|
|
6847
7141
|
init_scopeblind_bridge();
|
|
6848
7142
|
init_action_readback();
|
|
7143
|
+
init_receipt_enrichment();
|
|
6849
7144
|
DEFAULT_PORT = 9377;
|
|
6850
7145
|
LOG_FILE3 = ".protect-mcp-log.jsonl";
|
|
6851
7146
|
RECEIPTS_FILE2 = ".protect-mcp-receipts.jsonl";
|
|
@@ -8295,6 +8590,8 @@ Usage:
|
|
|
8295
8590
|
protect-mcp digest [--today] [--dir <path>]
|
|
8296
8591
|
protect-mcp receipts [--last <n>] [--dir <path>]
|
|
8297
8592
|
protect-mcp record [--dir <path>] [--live] [--no-open]
|
|
8593
|
+
protect-mcp claim [--no <cap>] [--only <c,c>] [--count <verdict>] [--dir <path>] [--output <path>]
|
|
8594
|
+
protect-mcp verify-claim <claim.json> [--key <public-hex>]
|
|
8298
8595
|
protect-mcp bundle [--output <path>] [--dir <path>]
|
|
8299
8596
|
protect-mcp simulate --policy <path> [--log <path>] [--tier <tier>] [--json]
|
|
8300
8597
|
protect-mcp report [--period <days>d] [--format md|json] [--output <path>] [--dir <path>]
|
|
@@ -8333,6 +8630,8 @@ Commands:
|
|
|
8333
8630
|
digest Generate a human-readable summary of agent activity
|
|
8334
8631
|
receipts Show recent persisted signed receipts
|
|
8335
8632
|
record Open a local, searchable view of your record in the browser
|
|
8633
|
+
claim Attest a signed, position-blind claim over your record (e.g. no egress)
|
|
8634
|
+
verify-claim Verify a claim attestation offline (signature + predicate + commitment)
|
|
8336
8635
|
bundle Export an offline-verifiable audit bundle
|
|
8337
8636
|
|
|
8338
8637
|
Examples:
|
|
@@ -10224,7 +10523,13 @@ function mapRecordEntry(e) {
|
|
|
10224
10523
|
if (e.receipt_hash) digest = String(e.receipt_hash);
|
|
10225
10524
|
else if (e.digest) digest = String(e.digest);
|
|
10226
10525
|
else if (p.payload_digest && p.payload_digest.output_hash) digest = String(p.payload_digest.output_hash);
|
|
10227
|
-
|
|
10526
|
+
const enr = p && typeof p.enrichment === "object" && p.enrichment || typeof e.enrichment === "object" && e.enrichment || null;
|
|
10527
|
+
const caps = enr && Array.isArray(enr.capabilities) ? enr.capabilities.map(String) : [];
|
|
10528
|
+
const sw = p && typeof p.swarm === "object" && p.swarm || null;
|
|
10529
|
+
const agent = sw && (sw.agent_name || sw.agent_id || sw.agent_type) ? String(sw.agent_name || sw.agent_id || sw.agent_type) : "main agent";
|
|
10530
|
+
const tm = p && typeof p.timing === "object" && p.timing || null;
|
|
10531
|
+
const dur = tm && typeof tm.tool_duration_ms === "number" ? tm.tool_duration_ms : 0;
|
|
10532
|
+
return { ts, tool, verdict, reason, hook, signed, caps, agent, dur, id: String(e.request_id || p.request_id || ""), digest, raw: e };
|
|
10228
10533
|
}
|
|
10229
10534
|
async function handleRecord(argv) {
|
|
10230
10535
|
const { readFileSync: readFileSync11, existsSync: existsSync9, writeFileSync: writeFileSync4 } = await import("fs");
|
|
@@ -10340,53 +10645,319 @@ var RECORD_HTML = `<!doctype html><html lang="en"><head><meta charset="utf-8"><m
|
|
|
10340
10645
|
body{margin:0;background:var(--paper);color:var(--ink);font:15px/1.5 system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;-webkit-font-smoothing:antialiased}
|
|
10341
10646
|
.wrap{max-width:1000px;margin:0 auto;padding:26px 22px 60px}
|
|
10342
10647
|
h1{font-size:24px;margin:0 0 4px;letter-spacing:-.012em}
|
|
10343
|
-
.meta{color:var(--faint);font-size:12.5px;font-family:ui-monospace,Menlo,Consolas,monospace}
|
|
10344
|
-
.
|
|
10648
|
+
.meta{color:var(--faint);font-size:12.5px;font-family:ui-monospace,Menlo,Consolas,monospace;display:flex;align-items:center}
|
|
10649
|
+
.pulse{width:7px;height:7px;border-radius:100px;background:var(--g);display:inline-block;margin-left:8px;animation:pl 1.6s ease-in-out infinite}
|
|
10650
|
+
@keyframes pl{0%,100%{opacity:.3}50%{opacity:1}}
|
|
10651
|
+
@media (prefers-reduced-motion:reduce){.pulse{animation:none}}
|
|
10652
|
+
.stats{display:flex;gap:15px;flex-wrap:wrap;align-items:center;margin:14px 0 10px;font-size:13px}
|
|
10653
|
+
.stat{display:flex;align-items:center;gap:6px;color:var(--soft)}
|
|
10654
|
+
.stat b{color:var(--ink);font-weight:680}
|
|
10655
|
+
.dot{width:8px;height:8px;border-radius:100px;display:inline-block}
|
|
10656
|
+
.dot.g{background:var(--g)}.dot.a{background:var(--a)}.dot.r{background:var(--r)}
|
|
10657
|
+
.stat.sig{margin-left:auto;color:var(--g);font-weight:600}
|
|
10658
|
+
.actions{display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin:0 0 16px}
|
|
10659
|
+
.btn{cursor:pointer;font:inherit;font-size:12.5px;padding:7px 13px;border-radius:8px;border:1px solid var(--line);background:#fff;color:var(--ink);transition:border-color .12s}
|
|
10660
|
+
.btn:hover{border-color:var(--ink)}
|
|
10661
|
+
.btn.p{background:var(--ink);color:var(--paper);border-color:var(--ink)}
|
|
10662
|
+
.btn:focus-visible{outline:2px solid var(--ink);outline-offset:2px}
|
|
10663
|
+
.vhint{font-size:12px;color:var(--faint);margin-left:auto;font-family:ui-monospace,Menlo,monospace}
|
|
10664
|
+
.attest{margin:0 0 12px;font-size:12.5px;color:var(--soft);display:flex;gap:8px;align-items:center;flex-wrap:wrap}
|
|
10665
|
+
.cmd{font-family:ui-monospace,Menlo,monospace;font-size:12px;background:#efece4;border:1px solid var(--line);border-radius:6px;padding:3px 8px;color:var(--ink)}
|
|
10666
|
+
.btn2{cursor:pointer;font:inherit;font-size:12px;padding:4px 9px;border-radius:7px;border:1px solid var(--line);background:#fff;color:var(--ink)}
|
|
10667
|
+
.btn2:hover{border-color:var(--ink)}
|
|
10668
|
+
.bar{margin:6px 0 12px}
|
|
10345
10669
|
input{width:100%;padding:10px 13px;border:1px solid var(--line);border-radius:9px;background:#fff;font:inherit}
|
|
10346
10670
|
.chips{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:14px}
|
|
10347
10671
|
.chip{cursor:pointer;font-size:12px;padding:3px 10px;border-radius:100px;border:1px solid var(--line);background:#fff;color:var(--soft)}
|
|
10348
10672
|
.chip.on{background:var(--ink);color:var(--paper);border-color:var(--ink)}
|
|
10349
10673
|
.count{color:var(--faint);font-size:12px;font-family:ui-monospace,Menlo,monospace;margin-bottom:8px}
|
|
10350
10674
|
.row{border:1px solid var(--line);border-radius:9px;background:#fcfbf7;padding:11px 13px;margin-bottom:8px;cursor:pointer}
|
|
10675
|
+
.row.blocked{background:#fbf3f0}.row.held{background:#fbf7ee}
|
|
10351
10676
|
.top{display:flex;gap:9px;align-items:center;flex-wrap:wrap}
|
|
10352
10677
|
.pill{font-size:11px;font-weight:600;padding:2px 9px;border-radius:100px}
|
|
10353
10678
|
.pill.allowed{background:var(--gb);color:var(--g)}.pill.held{background:var(--ab);color:var(--a)}.pill.blocked{background:var(--rb);color:var(--r)}
|
|
10354
10679
|
.tag{font-size:11px;padding:1px 7px;border-radius:100px;background:var(--paper);border:1px solid var(--line);color:var(--faint)}
|
|
10680
|
+
.cap{font-size:10px;padding:1px 6px;border-radius:100px;background:#eef0ea;border:1px solid var(--line);color:var(--soft)}
|
|
10681
|
+
.badge{font-size:10.5px;font-weight:600;padding:1px 7px;border-radius:100px}
|
|
10682
|
+
.badge.sgn{background:var(--gb);color:var(--g)}
|
|
10683
|
+
.badge.log{background:var(--paper);color:var(--faint);border:1px solid var(--line)}
|
|
10684
|
+
.dg{font-size:10.5px;color:var(--faint);font-family:ui-monospace,Menlo,monospace}
|
|
10355
10685
|
.when{margin-left:auto;font-size:12px;color:var(--faint);font-family:ui-monospace,Menlo,monospace}
|
|
10356
10686
|
.det{margin-top:8px;padding-top:8px;border-top:1px solid var(--line);font-size:12px;color:var(--soft);font-family:ui-monospace,Menlo,monospace;white-space:pre-wrap;word-break:break-all;display:none}
|
|
10357
10687
|
.row.open .det{display:block}
|
|
10358
10688
|
.foot{margin-top:22px;color:var(--faint);font-size:12px;line-height:1.6;border-top:1px solid var(--line);padding-top:14px}
|
|
10359
10689
|
.foot b{color:var(--soft)}
|
|
10690
|
+
.viewtoggle{display:inline-flex;border:1px solid var(--line);border-radius:8px;overflow:hidden}
|
|
10691
|
+
.viewtoggle button{border:0;background:#fff;color:var(--soft);font:inherit;font-size:12.5px;padding:7px 12px;cursor:pointer}
|
|
10692
|
+
.viewtoggle button.on{background:var(--ink);color:var(--paper)}
|
|
10693
|
+
.agent{border:1px solid var(--line);border-radius:10px;background:#fcfbf7;margin-bottom:10px;overflow:hidden}
|
|
10694
|
+
.ahead{display:flex;gap:9px;align-items:center;flex-wrap:wrap;padding:11px 13px;cursor:pointer}
|
|
10695
|
+
.atwist{color:var(--faint);font-size:11px;transition:transform .12s;display:inline-block}
|
|
10696
|
+
.agent.open .atwist{transform:rotate(90deg)}
|
|
10697
|
+
.acount{font-size:12px;color:var(--faint)}
|
|
10698
|
+
.akids{display:none;padding:2px 12px 12px 24px;border-top:1px solid var(--line)}
|
|
10699
|
+
.agent.open .akids{display:block}
|
|
10700
|
+
.act{display:flex;gap:10px;align-items:center;flex-wrap:wrap;padding:9px 4px;cursor:pointer;border-bottom:1px solid var(--line)}
|
|
10701
|
+
.akids .act:last-child{border-bottom:0}
|
|
10702
|
+
.act:hover{background:rgba(0,0,0,.02)}
|
|
10703
|
+
.act.blocked{background:#fbf3f0}.act.held{background:#fbf7ee}
|
|
10704
|
+
.act .det{flex-basis:100%}
|
|
10705
|
+
.act.open .det{display:block}
|
|
10706
|
+
.ev{display:flex;gap:8px;align-items:center;font-size:12px;color:var(--faint);padding:5px 10px;margin-top:6px}
|
|
10707
|
+
.evdot{width:6px;height:6px;border-radius:100px;background:var(--faint);display:inline-block}
|
|
10708
|
+
.evre{color:var(--soft)}
|
|
10709
|
+
.badge.blk{background:var(--rb);color:var(--r)}
|
|
10360
10710
|
</style></head><body><div class="wrap">
|
|
10361
10711
|
<h1>Your record</h1>
|
|
10362
|
-
<div class="meta" id="meta"></div>
|
|
10712
|
+
<div class="meta"><span id="meta"></span><span id="live"></span></div>
|
|
10713
|
+
<div class="stats" id="stats"></div>
|
|
10714
|
+
<div class="actions">
|
|
10715
|
+
<div class="viewtoggle"><button id="vlist" class="on" onclick="setView('list')">List</button><button id="vtree" onclick="setView('tree')">Tree</button></div>
|
|
10716
|
+
<button class="btn p" onclick="exportJsonl()">Export receipts (.jsonl)</button>
|
|
10717
|
+
<button class="btn" onclick="exportMd()">Export report (.md)</button>
|
|
10718
|
+
<button class="btn" id="cpv" onclick="copyVerify()">Copy verify command</button>
|
|
10719
|
+
<span class="vhint">verify offline: npx @veritasacta/verify</span>
|
|
10720
|
+
</div>
|
|
10363
10721
|
<div class="bar"><input id="q" placeholder="Search your record: tool, reason, verdict"></div>
|
|
10364
10722
|
<div class="chips" id="chips"></div>
|
|
10365
10723
|
<div class="count" id="count"></div>
|
|
10724
|
+
<div class="attest" id="attest"></div>
|
|
10366
10725
|
<div id="list"></div>
|
|
10367
|
-
<div class="foot">Signed decisions from your own gate.
|
|
10726
|
+
<div class="foot">Signed decisions from your own gate, on this machine. Nothing was uploaded. Each row is Ed25519-signed, and the exports carry the signatures, so anyone you hand them to (an allocator, an auditor, a counterparty) verifies offline with <b>npx @veritasacta/verify</b>, our code removed. For a Merkle-rooted evidence pack: <b>npx protect-mcp bundle</b>. To prove a claim over this record without revealing it (e.g. no egress): <b>npx protect-mcp claim --no net.egress</b>, checked offline with <b>npx protect-mcp verify-claim</b>. protect-mcp governs proposed actions before they run.</div>
|
|
10368
10727
|
</div>
|
|
10369
10728
|
<script>
|
|
10370
|
-
var RECORDS=__DATA__;var META=__META__;var Q="",ACT={};
|
|
10729
|
+
var RECORDS=__DATA__;var META=__META__;var Q="",ACT={},VIEW="list",OPEN={};var NL=String.fromCharCode(10);
|
|
10371
10730
|
function esc(s){return String(s).replace(/[&<>"]/g,function(c){return{"&":"&","<":"<",">":">",'"':"""}[c]})}
|
|
10372
10731
|
function vlabel(v){return v==="allowed"?"Allowed":v==="held"?"Held":"Blocked"}
|
|
10373
10732
|
function when(ts){if(!ts)return"";var d=new Date(ts);return d.toLocaleDateString(undefined,{month:"short",day:"numeric"})+" "+d.toLocaleTimeString(undefined,{hour:"2-digit",minute:"2-digit"})}
|
|
10374
|
-
function
|
|
10375
|
-
function
|
|
10733
|
+
function counts(rows){var c={allowed:0,held:0,blocked:0,signed:0};rows.forEach(function(r){c[r.verdict]=(c[r.verdict]||0)+1;if(r.signed)c.signed++});return c}
|
|
10734
|
+
function fvals(key){var m={};RECORDS.forEach(function(r){var vs=key==="Decision"?[vlabel(r.verdict)]:(key==="Capability"?(r.caps||[]):[r[key.toLowerCase()]]);vs.forEach(function(v){if(v){m[v]=(m[v]||0)+1}})});return Object.keys(m).sort(function(a,b){return m[b]-m[a]}).slice(0,10).map(function(v){return[v,m[v]]})}
|
|
10735
|
+
function match(r){if(ACT.Decision&&vlabel(r.verdict)!==ACT.Decision)return false;if(ACT.Tool&&r.tool!==ACT.Tool)return false;if(ACT.Reason&&r.reason!==ACT.Reason)return false;if(ACT.Capability&&(r.caps||[]).indexOf(ACT.Capability)<0)return false;if(Q){var h=(r.tool+" "+r.reason+" "+vlabel(r.verdict)+" "+r.hook+" "+(r.caps||[]).join(" ")).toLowerCase();if(h.indexOf(Q)<0)return false}return true}
|
|
10736
|
+
function filtered(){return RECORDS.filter(match)}
|
|
10737
|
+
function dl(name,text,type){var b=new Blob([text],{type:type||"text/plain"});var u=URL.createObjectURL(b);var a=document.createElement("a");a.href=u;a.download=name;document.body.appendChild(a);a.click();a.remove();setTimeout(function(){URL.revokeObjectURL(u)},1500)}
|
|
10738
|
+
function stamp(){return new Date().toISOString().replace(/[:.]/g,"-").slice(0,19)}
|
|
10739
|
+
function exportJsonl(){var rows=filtered();if(!rows.length)return;var lines=rows.map(function(r){return JSON.stringify(r.raw||r)}).join(NL);dl("protect-mcp-record-"+stamp()+".jsonl",lines,"application/x-ndjson")}
|
|
10740
|
+
function exportMd(){var rows=filtered();if(!rows.length)return;var c=counts(rows);var head=["# Agent decision record","",rows.length+" decisions from "+META.file,c.allowed+" allowed, "+c.held+" held, "+c.blocked+" blocked, "+c.signed+" signed.","","Generated locally by protect-mcp. These are signed receipts; verify offline with npx @veritasacta/verify (our code removed).","","| When | Decision | Tool | Reason | Hook | Signed |","|---|---|---|---|---|---|"];var body=rows.slice(0,3000).map(function(r){return "| "+(r.ts||"")+" | "+vlabel(r.verdict)+" | "+String(r.tool||"").replace(/\\|/g,"/")+" | "+String(r.reason||"").replace(/\\|/g,"/")+" | "+(r.hook||"")+" | "+(r.signed?"yes":"no")+" |"});dl("protect-mcp-record-"+stamp()+".md",head.concat(body).join(NL)+NL,"text/markdown")}
|
|
10741
|
+
function copyAttest(){var a=document.getElementById("attest");var cmd=a?a.getAttribute("data-cmd"):"";try{navigator.clipboard&&cmd&&navigator.clipboard.writeText(cmd)}catch(e){}var b=document.getElementById("cpa");if(b){var t=b.textContent;b.textContent="Copied";setTimeout(function(){b.textContent=t},1200)}}
|
|
10742
|
+
function copyVerify(){var cmd="npx @veritasacta/verify";try{navigator.clipboard&&navigator.clipboard.writeText(cmd)}catch(e){}var b=document.getElementById("cpv");if(b){var t=b.textContent;b.textContent="Copied";setTimeout(function(){b.textContent=t},1200)}}
|
|
10743
|
+
function renderStats(){var c=counts(RECORDS);var p=[];p.push('<span class="stat"><b>'+RECORDS.length+'</b> decisions</span>');p.push('<span class="stat"><span class="dot g"></span>'+c.allowed+' allowed</span>');if(c.held)p.push('<span class="stat"><span class="dot a"></span>'+c.held+' held</span>');p.push('<span class="stat"><span class="dot r"></span>'+c.blocked+' blocked</span>');p.push('<span class="stat sig">'+c.signed+' signed, verifiable offline</span>');document.getElementById("stats").innerHTML=p.join("")}
|
|
10744
|
+
function renderList(rows){var html="";rows.slice(0,800).forEach(function(r){var sig=r.signed?'<span class="badge sgn">signed</span>':'<span class="badge log">log</span>';var dg=r.digest?'<span class="dg">'+esc(String(r.digest).slice(0,10))+'</span>':'';var ct=(r.caps||[]).map(function(c){return '<span class="cap">'+esc(c)+'</span>'}).join('');var rk="row:"+(r.id||"")+"|"+(r.ts||"");html+='<div class="row '+r.verdict+(OPEN[rk]?" open":"")+'" data-k="'+esc(rk)+'"><div class="top"><span class="pill '+r.verdict+'">'+vlabel(r.verdict)+"</span><b>"+esc(r.tool)+'</b><span class="tag">'+esc(r.reason)+"</span>"+ct+(r.hook?'<span class="tag">'+esc(r.hook)+"</span>":"")+sig+dg+'<span class="when">'+esc(when(r.ts))+'</span></div><div class="det">'+esc(JSON.stringify(r.raw||r,null,2))+"</div></div>"});document.getElementById("list").innerHTML=html||'<p style="color:#8a837a">No records match.</p>';}
|
|
10745
|
+
function isLifecycle(r){var h=r.hook||"";return h==="SessionStart"||h==="SessionEnd"||h==="Stop"||h==="SubagentStart"||h==="SubagentStop"||h==="TaskCreated"||h==="TaskCompleted"||h==="ConfigChange"||h==="Notification"||h==="PreCompact";}
|
|
10746
|
+
function buildTree(rows){var ags={},order=[];rows.forEach(function(r){var a=r.agent||"main agent";if(!ags[a]){ags[a]={name:a,byId:{},items:[],caps:{},blocked:0,actions:0};order.push(a);}var g=ags[a];(r.caps||[]).forEach(function(c){g.caps[c]=(g.caps[c]||0)+1;});if(isLifecycle(r)){g.items.push({t:"e",ts:r.ts,r:r});return;}var id=r.id||("_"+r.ts);var n=g.byId[id];if(!n){n={t:"a",id:id,tool:r.tool,verdict:r.verdict,caps:(r.caps||[]).slice(),ts:r.ts,dur:0,signed:!!r.signed,raw:r.raw};g.byId[id]=n;g.items.push(n);g.actions++;}if(r.hook==="PostToolUse"){if(r.dur)n.dur=r.dur;if(!n.raw)n.raw=r.raw;}else{n.verdict=r.verdict;if((r.caps||[]).length)n.caps=r.caps.slice();n.raw=r.raw;n.ts=r.ts;}if(r.signed)n.signed=true;});order.forEach(function(a){var g=ags[a];g.blocked=g.items.filter(function(it){return it.t==="a"&&it.verdict==="blocked";}).length;g.items.sort(function(x,y){return (x.ts<y.ts)?-1:1;});});return order.map(function(a){return ags[a];});}
|
|
10747
|
+
function renderTree(ags){if(!ags.length){document.getElementById("list").innerHTML='<p style="color:#8a837a">No records match.</p>';return;}var html="",N=0;ags.forEach(function(g,gi){var capstr=Object.keys(g.caps).sort(function(a,b){return g.caps[b]-g.caps[a];}).slice(0,5).map(function(c){return '<span class="cap">'+esc(c)+'</span>';}).join('');var ak="ag:"+g.name;var op=(OPEN.hasOwnProperty(ak)?OPEN[ak]:(ags.length===1||gi===0))?" open":"";html+='<div class="agent'+op+'" data-k="'+esc(ak)+'"><div class="ahead"><span class="atwist">\u25B8</span><b>'+esc(g.name)+'</b><span class="acount">'+g.actions+' action'+(g.actions===1?'':'s')+'</span>'+(g.blocked?'<span class="badge blk">'+g.blocked+' blocked</span>':'')+capstr+'</div><div class="akids">';g.items.forEach(function(it){if(N++>1500)return;if(it.t==="e"){var r=it.r;html+='<div class="ev"><span class="evdot"></span>'+esc(r.hook||r.tool)+' <span class="evre">'+esc(r.reason)+'</span><span class="when">'+esc(when(r.ts))+'</span></div>';}else{var ct=(it.caps||[]).map(function(c){return '<span class="cap">'+esc(c)+'</span>';}).join('');var dur=it.dur?'<span class="dg">'+it.dur+'ms</span>':'';var ik="act:"+it.id;html+='<div class="act '+it.verdict+(OPEN[ik]?" open":"")+'" data-k="'+esc(ik)+'"><span class="pill '+it.verdict+'">'+vlabel(it.verdict)+'</span><b>'+esc(it.tool)+'</b>'+ct+(it.signed?'<span class="badge sgn">signed</span>':'')+dur+'<span class="when">'+esc(when(it.ts))+'</span><div class="det">'+esc(JSON.stringify(it.raw||{},null,2))+'</div></div>';}});html+='</div></div>';});if(N>1500)html+='<p style="color:#8a837a;font-size:12px;margin-top:10px">Showing the first 1500 items. Search or pick a facet to narrow.</p>';document.getElementById("list").innerHTML=html;}
|
|
10748
|
+
function setView(v){VIEW=v;document.getElementById("vlist").className=v==="list"?"on":"";document.getElementById("vtree").className=v==="tree"?"on":"";render();}
|
|
10376
10749
|
function render(){
|
|
10377
10750
|
document.getElementById("meta").textContent=META.count+" decisions from "+META.file+(META.signed?" (signed)":" (decision log)")+" - all local"+(META.live?" \xB7 live, updating":"");
|
|
10378
|
-
|
|
10751
|
+
document.getElementById("live").innerHTML=META.live?'<span class="pulse"></span>':"";
|
|
10752
|
+
renderStats();
|
|
10753
|
+
var chips="";["Decision","Tool","Reason","Capability"].forEach(function(key){fvals(key).forEach(function(p){var on=ACT[key]===p[0];chips+='<span class="chip'+(on?" on":"")+'" data-k="'+key+'" data-v="'+esc(p[0])+'">'+esc(p[0])+" "+p[1]+"</span>"})});
|
|
10379
10754
|
document.getElementById("chips").innerHTML=chips;
|
|
10380
10755
|
var rows=RECORDS.filter(match);
|
|
10381
|
-
document.getElementById("count").textContent=rows.length+" of "+RECORDS.length+" records";
|
|
10382
|
-
var
|
|
10383
|
-
|
|
10756
|
+
document.getElementById("count").textContent=rows.length+" of "+RECORDS.length+" records"+(VIEW==="tree"?" \xB7 grouped by agent":"");
|
|
10757
|
+
var _at=document.getElementById("attest");if(ACT.Capability){var _cmd="npx protect-mcp claim --no "+ACT.Capability;_at.setAttribute("data-cmd",_cmd);_at.innerHTML='Prove it over this record, revealing nothing: <span class="cmd">'+esc(_cmd)+'</span><button class="btn2" id="cpa" onclick="copyAttest()">Copy</button>';}else{_at.innerHTML="";_at.removeAttribute("data-cmd");}
|
|
10758
|
+
if(VIEW==="tree"){renderTree(buildTree(rows));}else{renderList(rows);}}
|
|
10384
10759
|
document.getElementById("q").addEventListener("input",function(e){Q=e.target.value.toLowerCase().trim();render()});
|
|
10385
10760
|
document.getElementById("chips").addEventListener("click",function(e){var c=e.target.closest(".chip");if(!c)return;var k=c.getAttribute("data-k"),v=c.getAttribute("data-v");ACT[k]=ACT[k]===v?undefined:v;render()});
|
|
10386
|
-
document.getElementById("list").addEventListener("click",function(e){var
|
|
10761
|
+
document.getElementById("list").addEventListener("click",function(e){var ah=e.target.closest(".ahead");if(ah){var ag=ah.parentNode;ag.classList.toggle("open");var ak=ag.getAttribute("data-k");if(ak)OPEN[ak]=ag.classList.contains("open");return;}var el=e.target.closest(".act")||e.target.closest(".row");if(el){el.classList.toggle("open");var k=el.getAttribute("data-k");if(k)OPEN[k]=el.classList.contains("open");}});
|
|
10387
10762
|
render();
|
|
10388
|
-
if(META.live){var poll=function(){fetch('/data',{cache:'no-store'}).then(function(r){return r.json()}).then(function(d){
|
|
10763
|
+
if(META.live){var poll=function(){fetch('/data',{cache:'no-store'}).then(function(r){return r.json()}).then(function(d){var nr=d.recs||[];var changed=nr.length!==RECORDS.length;RECORDS=nr;META.count=RECORDS.length;if(typeof d.signed==='boolean')META.signed=d.signed;if(changed)render()}).catch(function(){})};poll();setInterval(poll,2000);}
|
|
10389
10764
|
</script></body></html>`;
|
|
10765
|
+
async function handleClaim(argv) {
|
|
10766
|
+
const { readFileSync: readFileSync11, existsSync: existsSync9, writeFileSync: writeFileSync4 } = await import("fs");
|
|
10767
|
+
const { join: join8 } = await import("path");
|
|
10768
|
+
const { buildClaim: buildClaim2 } = await Promise.resolve().then(() => (init_claim(), claim_exports));
|
|
10769
|
+
let dir = process.cwd();
|
|
10770
|
+
const di = argv.indexOf("--dir");
|
|
10771
|
+
if (di !== -1 && argv[di + 1]) dir = argv[di + 1];
|
|
10772
|
+
let predicate = null;
|
|
10773
|
+
const noIdx = argv.indexOf("--no"), onlyIdx = argv.indexOf("--only"), nvIdx = argv.indexOf("--no-verdict"), cvIdx = argv.indexOf("--count");
|
|
10774
|
+
if (noIdx !== -1 && argv[noIdx + 1]) predicate = { kind: "no_capability", capability: argv[noIdx + 1] };
|
|
10775
|
+
else if (onlyIdx !== -1 && argv[onlyIdx + 1]) predicate = { kind: "only_capabilities", capabilities: argv[onlyIdx + 1].split(",").map((s) => s.trim()).filter(Boolean) };
|
|
10776
|
+
else if (nvIdx !== -1 && argv[nvIdx + 1]) predicate = { kind: "no_verdict", verdict: argv[nvIdx + 1] };
|
|
10777
|
+
else if (cvIdx !== -1 && argv[cvIdx + 1]) predicate = { kind: "count_verdict", verdict: argv[cvIdx + 1] };
|
|
10778
|
+
if (!predicate) {
|
|
10779
|
+
process.stderr.write(`
|
|
10780
|
+
${bold("protect-mcp claim")}
|
|
10781
|
+
|
|
10782
|
+
Attest a signed, position-blind claim over your record:
|
|
10783
|
+
--no <capability> no action used it, e.g. ${dim("--no net.egress")}
|
|
10784
|
+
--only <c1,c2,...> all actions confined to these capabilities
|
|
10785
|
+
--no-verdict <verdict> e.g. ${dim("--no-verdict blocked")}
|
|
10786
|
+
--count <verdict> how many, e.g. ${dim("--count blocked")}
|
|
10787
|
+
--anchor also record the claim digest in the public append-only
|
|
10788
|
+
log so a counterparty can trust it is complete (only the
|
|
10789
|
+
hash is sent; your record stays local)
|
|
10790
|
+
|
|
10791
|
+
Example: ${bold("npx protect-mcp claim --no net.egress --anchor")}
|
|
10792
|
+
|
|
10793
|
+
`);
|
|
10794
|
+
process.exit(0);
|
|
10795
|
+
return;
|
|
10796
|
+
}
|
|
10797
|
+
const keyPath = join8(dir, "keys", "gateway.json");
|
|
10798
|
+
if (!existsSync9(keyPath)) {
|
|
10799
|
+
process.stderr.write(`
|
|
10800
|
+
${bold("protect-mcp claim")}
|
|
10801
|
+
|
|
10802
|
+
No signing key at ${keyPath}. A claim must be signed. Run ${bold("npx protect-mcp init")} first.
|
|
10803
|
+
|
|
10804
|
+
`);
|
|
10805
|
+
process.exit(1);
|
|
10806
|
+
return;
|
|
10807
|
+
}
|
|
10808
|
+
let key;
|
|
10809
|
+
try {
|
|
10810
|
+
key = JSON.parse(readFileSync11(keyPath, "utf-8"));
|
|
10811
|
+
} catch {
|
|
10812
|
+
process.stderr.write(`
|
|
10813
|
+
protect-mcp claim: ${keyPath} is not valid JSON.
|
|
10814
|
+
|
|
10815
|
+
`);
|
|
10816
|
+
process.exit(1);
|
|
10817
|
+
return;
|
|
10818
|
+
}
|
|
10819
|
+
if (!key.privateKey || !key.publicKey) {
|
|
10820
|
+
process.stderr.write(`
|
|
10821
|
+
protect-mcp claim: ${keyPath} is missing privateKey/publicKey.
|
|
10822
|
+
|
|
10823
|
+
`);
|
|
10824
|
+
process.exit(1);
|
|
10825
|
+
return;
|
|
10826
|
+
}
|
|
10827
|
+
const recPath = join8(dir, ".protect-mcp-receipts.jsonl");
|
|
10828
|
+
if (!existsSync9(recPath)) {
|
|
10829
|
+
process.stderr.write(`
|
|
10830
|
+
${bold("protect-mcp claim")}
|
|
10831
|
+
|
|
10832
|
+
No signed receipts in ${dir}. Run the gate with signing on, then try again.
|
|
10833
|
+
|
|
10834
|
+
`);
|
|
10835
|
+
process.exit(0);
|
|
10836
|
+
return;
|
|
10837
|
+
}
|
|
10838
|
+
const receipts = readFileSync11(recPath, "utf-8").split(/\r?\n/).map((l) => l.trim()).filter(Boolean).map((l) => {
|
|
10839
|
+
try {
|
|
10840
|
+
return JSON.parse(l);
|
|
10841
|
+
} catch {
|
|
10842
|
+
return null;
|
|
10843
|
+
}
|
|
10844
|
+
}).filter((x) => x !== null);
|
|
10845
|
+
if (!receipts.length) {
|
|
10846
|
+
process.stderr.write(`
|
|
10847
|
+
protect-mcp claim: no readable receipts in ${recPath}.
|
|
10848
|
+
|
|
10849
|
+
`);
|
|
10850
|
+
process.exit(0);
|
|
10851
|
+
return;
|
|
10852
|
+
}
|
|
10853
|
+
const pack = buildClaim2(receipts, predicate, { privateKey: key.privateKey, publicKey: key.publicKey, kid: key.kid || "gateway", issuer: "protect-mcp" }, (/* @__PURE__ */ new Date()).toISOString());
|
|
10854
|
+
const oi = argv.indexOf("--output");
|
|
10855
|
+
const out = oi !== -1 && argv[oi + 1] ? argv[oi + 1] : join8(dir, "claim-" + Date.now() + ".json");
|
|
10856
|
+
writeFileSync4(out, JSON.stringify(pack, null, 2) + "\n");
|
|
10857
|
+
process.stdout.write(`
|
|
10858
|
+
${bold("\u{1F6E1}\uFE0F Signed claim")}
|
|
10859
|
+
`);
|
|
10860
|
+
process.stdout.write(` ${pack.claim.statement}: ${pack.claim.holds ? green("holds") : yellow("does not hold")} ${dim("(" + pack.claim.matched + " matched of " + pack.scope.total + " decisions)")}
|
|
10861
|
+
`);
|
|
10862
|
+
process.stdout.write(` ${dim("Position-blind: reveals decision categories, never tool inputs, outputs, or data. Ed25519-signed.")}
|
|
10863
|
+
`);
|
|
10864
|
+
process.stdout.write(` Written to ${out}
|
|
10865
|
+
`);
|
|
10866
|
+
process.stdout.write(` Hand it to anyone. They verify offline: ${bold("npx protect-mcp verify-claim " + out)}
|
|
10867
|
+
`);
|
|
10868
|
+
if (argv.indexOf("--anchor") !== -1) {
|
|
10869
|
+
const { anchorClaim: anchorClaim2 } = await Promise.resolve().then(() => (init_claim(), claim_exports));
|
|
10870
|
+
const li = argv.indexOf("--log");
|
|
10871
|
+
const logBase = li !== -1 && argv[li + 1] ? argv[li + 1] : void 0;
|
|
10872
|
+
process.stdout.write(`
|
|
10873
|
+
${dim("Anchoring the claim digest to the public append-only log (only the hash leaves your machine)...")}
|
|
10874
|
+
`);
|
|
10875
|
+
const res = await anchorClaim2(
|
|
10876
|
+
pack,
|
|
10877
|
+
{ privateKey: key.privateKey, publicKey: key.publicKey, kid: key.kid || "gateway", issuer: "protect-mcp" },
|
|
10878
|
+
{ log: logBase, issuedAt: (/* @__PURE__ */ new Date()).toISOString() }
|
|
10879
|
+
);
|
|
10880
|
+
if (res.ok) {
|
|
10881
|
+
const sidecar = out.replace(/\.json$/, "") + ".anchor.json";
|
|
10882
|
+
writeFileSync4(sidecar, JSON.stringify({ log: logBase || "https://scopeblind.com", seq: res.seq, entry_url: res.entry_url, anchored_at: res.anchored_at, claim_digest: res.claim_digest, envelope: res.envelope }, null, 2) + "\n");
|
|
10883
|
+
process.stdout.write(` ${green("Anchored")} as log entry ${bold("#" + res.seq)}${res.already_anchored ? dim(" (already present)") : ""} ${dim(res.entry_url || "")}
|
|
10884
|
+
`);
|
|
10885
|
+
process.stdout.write(` ${dim("A counterparty can now confirm this exact claim existed at " + (res.anchored_at || "this time") + " and cannot be quietly re-cut.")}
|
|
10886
|
+
`);
|
|
10887
|
+
process.stdout.write(` ${dim("Anchor record written to " + sidecar + ". Only the digest was sent; your record stayed local.")}
|
|
10888
|
+
`);
|
|
10889
|
+
process.stdout.write(` ${dim("To anchor as your enrolled org identity (a key a counterparty can pin), see")} ${bold("scopeblind.com/enroll")}
|
|
10890
|
+
`);
|
|
10891
|
+
} else {
|
|
10892
|
+
process.stdout.write(` ${yellow("Anchor skipped")} ${dim("(" + (res.error || "unavailable") + "). The claim above is complete and verifiable offline without it.")}
|
|
10893
|
+
`);
|
|
10894
|
+
}
|
|
10895
|
+
}
|
|
10896
|
+
process.stdout.write(`
|
|
10897
|
+
`);
|
|
10898
|
+
process.exit(0);
|
|
10899
|
+
}
|
|
10900
|
+
async function handleVerifyClaim(argv) {
|
|
10901
|
+
const { readFileSync: readFileSync11, existsSync: existsSync9 } = await import("fs");
|
|
10902
|
+
const { verifyClaim: verifyClaim2 } = await Promise.resolve().then(() => (init_claim(), claim_exports));
|
|
10903
|
+
const file = argv.find((a) => !a.startsWith("--"));
|
|
10904
|
+
if (!file || !existsSync9(file)) {
|
|
10905
|
+
process.stderr.write(`
|
|
10906
|
+
${bold("protect-mcp verify-claim")} <claim.json> [--key <public-hex>]
|
|
10907
|
+
|
|
10908
|
+
Provide a claim pack file.
|
|
10909
|
+
|
|
10910
|
+
`);
|
|
10911
|
+
process.exit(2);
|
|
10912
|
+
return;
|
|
10913
|
+
}
|
|
10914
|
+
let pack;
|
|
10915
|
+
try {
|
|
10916
|
+
pack = JSON.parse(readFileSync11(file, "utf-8"));
|
|
10917
|
+
} catch {
|
|
10918
|
+
process.stderr.write(`
|
|
10919
|
+
protect-mcp verify-claim: ${file} is not valid JSON.
|
|
10920
|
+
|
|
10921
|
+
`);
|
|
10922
|
+
process.exit(2);
|
|
10923
|
+
return;
|
|
10924
|
+
}
|
|
10925
|
+
if (!pack || pack.type !== "scopeblind.claim.v1") {
|
|
10926
|
+
process.stderr.write(`
|
|
10927
|
+
protect-mcp verify-claim: not a scopeblind.claim.v1 pack.
|
|
10928
|
+
|
|
10929
|
+
`);
|
|
10930
|
+
process.exit(2);
|
|
10931
|
+
return;
|
|
10932
|
+
}
|
|
10933
|
+
const ki = argv.indexOf("--key");
|
|
10934
|
+
const v = verifyClaim2(pack, ki !== -1 ? argv[ki + 1] : void 0);
|
|
10935
|
+
const ok = (b) => b ? green("\u2713") : red("\u2717");
|
|
10936
|
+
process.stdout.write(`
|
|
10937
|
+
${bold("protect-mcp verify-claim")}
|
|
10938
|
+
`);
|
|
10939
|
+
process.stdout.write(` Claim: ${pack.claim ? pack.claim.statement : "(none)"}
|
|
10940
|
+
`);
|
|
10941
|
+
process.stdout.write(` Holds: ${v.holds ? green("yes") : yellow("no")} ${dim("(" + v.matched + " matched of " + v.total + " decisions)")}
|
|
10942
|
+
`);
|
|
10943
|
+
process.stdout.write(` Signature: ${ok(v.authentic)} ${v.authentic ? "valid" : "INVALID"} ${dim("issuer kid " + (pack.issuer && pack.issuer.kid || "?"))}
|
|
10944
|
+
`);
|
|
10945
|
+
process.stdout.write(` Commitment: ${ok(v.root_ok)} ${v.root_ok ? "Merkle root matches the " + v.total + " disclosed decisions" : "MISMATCH"}
|
|
10946
|
+
`);
|
|
10947
|
+
process.stdout.write(` Predicate: ${ok(v.predicate_ok)} ${v.predicate_ok ? "recomputed independently and matches" : "MISMATCH"}
|
|
10948
|
+
`);
|
|
10949
|
+
process.stdout.write(`
|
|
10950
|
+
${v.valid ? green("VALID") : red("INVALID")} attestation.
|
|
10951
|
+
`);
|
|
10952
|
+
process.stdout.write(` ${dim("Proves the pack came from the issuer key and the claim is true over the disclosed decision")}
|
|
10953
|
+
`);
|
|
10954
|
+
process.stdout.write(` ${dim("categories (verdict + capabilities), which reveal no tool inputs, outputs, or data. Completeness")}
|
|
10955
|
+
`);
|
|
10956
|
+
process.stdout.write(` ${dim("of the disclosed set is attested by the issuer; pin the key or anchor to the log for more.")}
|
|
10957
|
+
|
|
10958
|
+
`);
|
|
10959
|
+
process.exit(v.valid ? 0 : 1);
|
|
10960
|
+
}
|
|
10390
10961
|
async function handleBundle(argv) {
|
|
10391
10962
|
const { readFileSync: readFileSync11, writeFileSync: writeFileSync4, existsSync: existsSync9 } = await import("fs");
|
|
10392
10963
|
const { join: join8 } = await import("path");
|
|
@@ -11884,6 +12455,14 @@ async function main() {
|
|
|
11884
12455
|
await handleRecord(args.slice(1));
|
|
11885
12456
|
return;
|
|
11886
12457
|
}
|
|
12458
|
+
if (args[0] === "claim") {
|
|
12459
|
+
await handleClaim(args.slice(1));
|
|
12460
|
+
return;
|
|
12461
|
+
}
|
|
12462
|
+
if (args[0] === "verify-claim") {
|
|
12463
|
+
await handleVerifyClaim(args.slice(1));
|
|
12464
|
+
return;
|
|
12465
|
+
}
|
|
11887
12466
|
if (args[0] === "init-hooks") {
|
|
11888
12467
|
await handleInitHooks(args.slice(1));
|
|
11889
12468
|
process.exit(0);
|