protect-mcp 0.9.4 → 0.9.6

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/dist/cli.js CHANGED
@@ -6319,6 +6319,101 @@ var init_hook_patterns = __esm({
6319
6319
  }
6320
6320
  });
6321
6321
 
6322
+ // src/sample.ts
6323
+ var sample_exports = {};
6324
+ __export(sample_exports, {
6325
+ SAMPLE_KID: () => SAMPLE_KID,
6326
+ buildSampleKit: () => buildSampleKit
6327
+ });
6328
+ function signEnvelope(unsigned, privHex) {
6329
+ const msg = new TextEncoder().encode(canonicalJson(unsigned));
6330
+ const signature = bytesToHex(ed25519.sign(msg, hexToBytes(privHex)));
6331
+ return { ...unsigned, signature };
6332
+ }
6333
+ function buildSampleKit(dir, opts) {
6334
+ const receiptsPath = (0, import_node_path7.join)(dir, ".protect-mcp-receipts.jsonl");
6335
+ const keyPath = (0, import_node_path7.join)(dir, "keys", "gateway.json");
6336
+ if (!opts?.force && ((0, import_node_fs10.existsSync)(receiptsPath) || (0, import_node_fs10.existsSync)(keyPath))) {
6337
+ throw Object.assign(
6338
+ new Error(`refusing to overwrite an existing record or signing key in ${dir}`),
6339
+ { code: "SAMPLE_EXISTS" }
6340
+ );
6341
+ }
6342
+ (0, import_node_fs10.mkdirSync)((0, import_node_path7.join)(dir, "keys"), { recursive: true });
6343
+ const priv = ed25519.utils.randomPrivateKey();
6344
+ const privHex = bytesToHex(priv);
6345
+ const pub = bytesToHex(ed25519.getPublicKey(priv));
6346
+ (0, import_node_fs10.writeFileSync)(keyPath, JSON.stringify({ privateKey: privHex, publicKey: pub, kid: SAMPLE_KID }, null, 2));
6347
+ (0, import_node_fs10.writeFileSync)((0, import_node_path7.join)(dir, "keys", ".gitignore"), "# Never commit signing keys\n*.json\n");
6348
+ const now = opts?.now ?? /* @__PURE__ */ new Date();
6349
+ const stamp = (i) => new Date(now.getTime() - (7 - i) * 5 * 6e4).toISOString();
6350
+ const receipt = (i, tool, decision, caps, extra) => {
6351
+ const ts = stamp(i);
6352
+ const payload = {
6353
+ tool,
6354
+ decision,
6355
+ reason_code: decision === "deny" ? "policy_deny" : "policy_ok",
6356
+ policy_digest: SAMPLE_KID,
6357
+ scope: `${tool}-${ts}`,
6358
+ mode: "enforce",
6359
+ request_id: `${tool}-${ts}`,
6360
+ spec: "draft-farley-acta-signed-receipts-01",
6361
+ issuer_certification: "self-signed",
6362
+ public_key: pub,
6363
+ hook_event: "PreToolUse",
6364
+ enrichment: { v: 2, input_digest: sha256Hex4(tool + ts), capabilities: caps, ...extra || {} }
6365
+ };
6366
+ return signEnvelope({
6367
+ v: 2,
6368
+ type: decision === "deny" ? "gateway_restraint" : "decision_receipt",
6369
+ algorithm: "ed25519",
6370
+ kid: SAMPLE_KID,
6371
+ issuer: "protect-mcp",
6372
+ issued_at: ts,
6373
+ payload
6374
+ }, privHex);
6375
+ };
6376
+ const pay = (amount) => ({
6377
+ payment: { amount, asset: "USDC", recipient_digest: sha256Hex4("sample-merchant"), scheme: "exact" }
6378
+ });
6379
+ const rows = [
6380
+ receipt(0, "Read", "allow", ["fs.read"]),
6381
+ receipt(1, "Bash", "allow", ["exec.shell"]),
6382
+ receipt(2, "Write", "allow", ["fs.write"]),
6383
+ receipt(3, "WebFetch", "deny", ["net.egress"]),
6384
+ receipt(4, "x402_pay", "allow", ["financial", "payment"], pay(0.02)),
6385
+ receipt(5, "Read", "allow", ["fs.read", "secret.adjacent"]),
6386
+ receipt(6, "wallet_send_payment", "allow", ["financial", "payment"], pay(12.5)),
6387
+ receipt(7, "Bash", "allow", ["exec.shell", "vcs"])
6388
+ ];
6389
+ (0, import_node_fs10.writeFileSync)(receiptsPath, rows.map((r) => JSON.stringify(r)).join("\n") + "\n");
6390
+ const tampered = rows.map((r) => JSON.parse(JSON.stringify(r)));
6391
+ tampered[3].payload.decision = "allow";
6392
+ (0, import_node_fs10.writeFileSync)((0, import_node_path7.join)(dir, "demo-tampered.jsonl"), tampered.map((r) => JSON.stringify(r)).join("\n") + "\n");
6393
+ return {
6394
+ dir,
6395
+ publicKey: pub,
6396
+ kid: SAMPLE_KID,
6397
+ receipts: rows,
6398
+ paymentsUsd: [0.02, 12.5],
6399
+ files: [".protect-mcp-receipts.jsonl", "demo-tampered.jsonl", "keys/gateway.json"]
6400
+ };
6401
+ }
6402
+ var import_node_fs10, import_node_path7, SAMPLE_KID, sha256Hex4;
6403
+ var init_sample = __esm({
6404
+ "src/sample.ts"() {
6405
+ "use strict";
6406
+ import_node_fs10 = require("fs");
6407
+ import_node_path7 = require("path");
6408
+ init_sha256();
6409
+ init_utils();
6410
+ init_ed25519();
6411
+ init_receipt_enrichment();
6412
+ SAMPLE_KID = "sample-demo";
6413
+ sha256Hex4 = (s) => bytesToHex(sha2562(new TextEncoder().encode(s)));
6414
+ }
6415
+ });
6416
+
6322
6417
  // src/scopeblind-bridge.ts
6323
6418
  function getScopeBlindBridge() {
6324
6419
  if (!singleton) singleton = new ScopeBlindBridge();
@@ -6527,7 +6622,7 @@ function detectSandboxState() {
6527
6622
  }
6528
6623
  if (process.platform === "linux") {
6529
6624
  try {
6530
- const procStatus = (0, import_node_fs10.readFileSync)("/proc/self/status", "utf-8");
6625
+ const procStatus = (0, import_node_fs11.readFileSync)("/proc/self/status", "utf-8");
6531
6626
  if (procStatus.includes("Seccomp: 2")) return "enabled";
6532
6627
  } catch {
6533
6628
  }
@@ -6555,6 +6650,7 @@ async function handlePreToolUse(input, state) {
6555
6650
  ...input.teamName && { team_name: input.teamName },
6556
6651
  ...input.agentType && { agent_type: input.agentType }
6557
6652
  };
6653
+ maybeReloadCedar(state);
6558
6654
  if (state.cedarPolicies) {
6559
6655
  try {
6560
6656
  const cedarDecision = await evaluateCedar(state.cedarPolicies, {
@@ -6592,10 +6688,13 @@ async function handlePreToolUse(input, state) {
6592
6688
  sandbox_state: detectSandboxState(),
6593
6689
  plan_receipt_id: state.activePlanReceiptId || void 0
6594
6690
  });
6691
+ const isDefaultDeny = !reason || reason === "cedar_deny" || /reason":\[\]/.test(reason);
6692
+ const policyRef = state.cedarDir ? ` Policy: ${state.cedarDir}.` : "";
6693
+ const howTo = isDefaultDeny ? ` No permit matched (default-deny, fail-closed).${policyRef} Allow it: npx protect-mcp policy allow ${toolName}` : ` Blocked by an explicit forbid rule.${policyRef} Review: npx protect-mcp policy show`;
6595
6694
  if (denyCount === 1) {
6596
6695
  process.stderr.write(
6597
- `[PROTECT_MCP] No Cedar permit for "${toolName}" \u2014 suggest:
6598
- ${suggestion}
6696
+ `[PROTECT_MCP] Denied "${toolName}" (${isDefaultDeny ? "default-deny" : "forbid"}).
6697
+ Allow with: npx protect-mcp policy allow ${toolName}
6599
6698
  `
6600
6699
  );
6601
6700
  }
@@ -6603,7 +6702,7 @@ async function handlePreToolUse(input, state) {
6603
6702
  hookSpecificOutput: {
6604
6703
  hookEventName: "PreToolUse",
6605
6704
  permissionDecision: "deny",
6606
- permissionDecisionReason: `[ScopeBlind] Denied by Cedar policy. ${reason}. Forbidden: "${toolName}" is not permitted. Try a read-only alternative.` + (denyCount > 1 ? ` (attempt ${denyCount})` : "")
6705
+ permissionDecisionReason: `[ScopeBlind] Denied "${toolName}".${howTo}` + (denyCount > 1 ? ` (attempt ${denyCount})` : "")
6607
6706
  }
6608
6707
  };
6609
6708
  }
@@ -6972,14 +7071,14 @@ function emitDecisionLog(state, entry) {
6972
7071
  process.stderr.write(`[PROTECT_MCP] ${JSON.stringify(log)}
6973
7072
  `);
6974
7073
  try {
6975
- (0, import_node_fs10.appendFileSync)(state.logFilePath, JSON.stringify(log) + "\n");
7074
+ (0, import_node_fs11.appendFileSync)(state.logFilePath, JSON.stringify(log) + "\n");
6976
7075
  } catch {
6977
7076
  }
6978
7077
  if (isSigningEnabled()) {
6979
7078
  const signed = signDecision(log);
6980
7079
  if (signed.signed) {
6981
7080
  try {
6982
- (0, import_node_fs10.appendFileSync)(state.receiptFilePath, signed.signed + "\n");
7081
+ (0, import_node_fs11.appendFileSync)(state.receiptFilePath, signed.signed + "\n");
6983
7082
  } catch {
6984
7083
  }
6985
7084
  state.receiptBuffer.add(log.request_id, signed.signed);
@@ -7003,7 +7102,7 @@ function emitDecisionLog(state, entry) {
7003
7102
  at: new Date(log.timestamp).toISOString()
7004
7103
  });
7005
7104
  try {
7006
- (0, import_node_fs10.appendFileSync)(state.receiptFilePath, tombstone + "\n");
7105
+ (0, import_node_fs11.appendFileSync)(state.receiptFilePath, tombstone + "\n");
7007
7106
  } catch {
7008
7107
  }
7009
7108
  process.stderr.write(`[PROTECT_MCP_SIGNING_FAILURE] ${tombstone}
@@ -7091,8 +7190,8 @@ async function startHookServer(options = {}) {
7091
7190
  }
7092
7191
  }
7093
7192
  if (!jsonPolicy?.signing) {
7094
- const keyPath = (0, import_node_path7.join)(process.cwd(), "keys", "gateway.json");
7095
- if ((0, import_node_fs10.existsSync)(keyPath)) {
7193
+ const keyPath = (0, import_node_path8.join)(process.cwd(), "keys", "gateway.json");
7194
+ if ((0, import_node_fs11.existsSync)(keyPath)) {
7096
7195
  const warnings = await initSigning({ key_path: keyPath, issuer: "protect-mcp", enabled: true });
7097
7196
  for (const w of warnings) {
7098
7197
  process.stderr.write(`[PROTECT_MCP] Warning: ${w}
@@ -7102,6 +7201,9 @@ async function startHookServer(options = {}) {
7102
7201
  }
7103
7202
  const state = {
7104
7203
  cedarPolicies,
7204
+ cedarDir: cedarDir || null,
7205
+ cedarMtimeMs: cedarDir ? newestCedarMtime(cedarDir) : 0,
7206
+ cedarCheckedMs: 0,
7105
7207
  jsonPolicy,
7106
7208
  rateLimitStore: /* @__PURE__ */ new Map(),
7107
7209
  receiptBuffer: new ReceiptBuffer(),
@@ -7114,8 +7216,8 @@ async function startHookServer(options = {}) {
7114
7216
  verbose,
7115
7217
  enforce,
7116
7218
  policyDigest,
7117
- logFilePath: (0, import_node_path7.join)(process.cwd(), LOG_FILE3),
7118
- receiptFilePath: (0, import_node_path7.join)(process.cwd(), RECEIPTS_FILE2),
7219
+ logFilePath: (0, import_node_path8.join)(process.cwd(), LOG_FILE3),
7220
+ receiptFilePath: (0, import_node_path8.join)(process.cwd(), RECEIPTS_FILE2),
7119
7221
  permissionSuggestions: /* @__PURE__ */ new Map(),
7120
7222
  configAlerts: []
7121
7223
  };
@@ -7264,7 +7366,7 @@ async function startHookServer(options = {}) {
7264
7366
  `);
7265
7367
  w(`
7266
7368
  `);
7267
- const hasSlug = process.env.SCOPEBLIND_SLUG || (0, import_node_fs10.existsSync)((0, import_node_path7.join)(process.cwd(), ".scopeblind"));
7369
+ const hasSlug = process.env.SCOPEBLIND_SLUG || (0, import_node_fs11.existsSync)((0, import_node_path8.join)(process.cwd(), ".scopeblind"));
7268
7370
  if (!hasSlug) {
7269
7371
  w(` Dashboard npx protect-mcp connect
7270
7372
  `);
@@ -7292,11 +7394,44 @@ async function startHookServer(options = {}) {
7292
7394
  process.on("SIGTERM", shutdown);
7293
7395
  return server;
7294
7396
  }
7397
+ function newestCedarMtime(dir) {
7398
+ try {
7399
+ let newest = 0;
7400
+ for (const f of (0, import_node_fs11.readdirSync)(dir)) {
7401
+ if (!f.endsWith(".cedar")) continue;
7402
+ const m = (0, import_node_fs11.statSync)((0, import_node_path8.join)(dir, f)).mtimeMs;
7403
+ if (m > newest) newest = m;
7404
+ }
7405
+ return newest;
7406
+ } catch {
7407
+ return 0;
7408
+ }
7409
+ }
7410
+ function maybeReloadCedar(state) {
7411
+ if (!state.cedarDir) return;
7412
+ const nowMs = Date.now();
7413
+ if (nowMs - state.cedarCheckedMs < CEDAR_CHECK_THROTTLE_MS) return;
7414
+ state.cedarCheckedMs = nowMs;
7415
+ const m = newestCedarMtime(state.cedarDir);
7416
+ if (m <= state.cedarMtimeMs) return;
7417
+ try {
7418
+ const reloaded = loadCedarPolicies(state.cedarDir);
7419
+ state.cedarPolicies = reloaded;
7420
+ state.policyDigest = reloaded.digest;
7421
+ state.cedarMtimeMs = m;
7422
+ process.stderr.write(`[PROTECT_MCP] Cedar policy reloaded (digest: ${reloaded.digest}) after on-disk change.
7423
+ `);
7424
+ } catch (err) {
7425
+ process.stderr.write(`[PROTECT_MCP] Cedar reload failed, keeping the previous policy: ${err instanceof Error ? err.message : err}
7426
+ `);
7427
+ state.cedarMtimeMs = m;
7428
+ }
7429
+ }
7295
7430
  function findCedarDir() {
7296
7431
  for (const candidate of ["cedar", "policies", "."]) {
7297
7432
  try {
7298
- if ((0, import_node_fs10.existsSync)(candidate)) {
7299
- const files = (0, import_node_fs10.readdirSync)(candidate, { encoding: "utf-8" });
7433
+ if ((0, import_node_fs11.existsSync)(candidate)) {
7434
+ const files = (0, import_node_fs11.readdirSync)(candidate, { encoding: "utf-8" });
7300
7435
  if (files.some((f) => f.endsWith(".cedar"))) {
7301
7436
  return candidate;
7302
7437
  }
@@ -7317,14 +7452,14 @@ function normalizeHookInput(raw) {
7317
7452
  }
7318
7453
  return result;
7319
7454
  }
7320
- var import_node_http2, import_node_crypto6, import_node_fs10, import_node_path7, DEFAULT_PORT, LOG_FILE3, RECEIPTS_FILE2, PAYLOAD_HASH_THRESHOLD, SNAKE_TO_CAMEL_MAP;
7455
+ var import_node_http2, import_node_crypto6, import_node_fs11, import_node_path8, DEFAULT_PORT, LOG_FILE3, RECEIPTS_FILE2, PAYLOAD_HASH_THRESHOLD, CEDAR_CHECK_THROTTLE_MS, SNAKE_TO_CAMEL_MAP;
7321
7456
  var init_hook_server = __esm({
7322
7457
  "src/hook-server.ts"() {
7323
7458
  "use strict";
7324
7459
  import_node_http2 = require("http");
7325
7460
  import_node_crypto6 = require("crypto");
7326
- import_node_fs10 = require("fs");
7327
- import_node_path7 = require("path");
7461
+ import_node_fs11 = require("fs");
7462
+ import_node_path8 = require("path");
7328
7463
  init_cedar_evaluator();
7329
7464
  init_signing();
7330
7465
  init_policy();
@@ -7336,6 +7471,7 @@ var init_hook_server = __esm({
7336
7471
  LOG_FILE3 = ".protect-mcp-log.jsonl";
7337
7472
  RECEIPTS_FILE2 = ".protect-mcp-receipts.jsonl";
7338
7473
  PAYLOAD_HASH_THRESHOLD = 1024;
7474
+ CEDAR_CHECK_THROTTLE_MS = 2e3;
7339
7475
  SNAKE_TO_CAMEL_MAP = {
7340
7476
  hook_event_name: "hookEventName",
7341
7477
  session_id: "sessionId",
@@ -7550,8 +7686,8 @@ function generateReport(logPath, receiptPath, periodDays) {
7550
7686
  const now = /* @__PURE__ */ new Date();
7551
7687
  const from = new Date(now.getTime() - periodDays * 864e5);
7552
7688
  const entries = [];
7553
- if ((0, import_node_fs11.existsSync)(logPath)) {
7554
- const raw = (0, import_node_fs11.readFileSync)(logPath, "utf-8");
7689
+ if ((0, import_node_fs12.existsSync)(logPath)) {
7690
+ const raw = (0, import_node_fs12.readFileSync)(logPath, "utf-8");
7555
7691
  for (const line of raw.split("\n")) {
7556
7692
  const trimmed = line.trim();
7557
7693
  if (!trimmed) continue;
@@ -7571,8 +7707,8 @@ function generateReport(logPath, receiptPath, periodDays) {
7571
7707
  let receiptsSigned = 0;
7572
7708
  let signerKid = "";
7573
7709
  let signerIssuer = "";
7574
- if ((0, import_node_fs11.existsSync)(receiptPath)) {
7575
- const raw = (0, import_node_fs11.readFileSync)(receiptPath, "utf-8");
7710
+ if ((0, import_node_fs12.existsSync)(receiptPath)) {
7711
+ const raw = (0, import_node_fs12.readFileSync)(receiptPath, "utf-8");
7576
7712
  for (const line of raw.split("\n")) {
7577
7713
  const trimmed = line.trim();
7578
7714
  if (!trimmed) continue;
@@ -7704,11 +7840,11 @@ function formatReportMarkdown(report) {
7704
7840
  lines.push("*Generated by protect-mcp \xB7 scopeblind.com*");
7705
7841
  return lines.join("\n");
7706
7842
  }
7707
- var import_node_fs11;
7843
+ var import_node_fs12;
7708
7844
  var init_report = __esm({
7709
7845
  "src/report.ts"() {
7710
7846
  "use strict";
7711
- import_node_fs11 = require("fs");
7847
+ import_node_fs12 = require("fs");
7712
7848
  }
7713
7849
  });
7714
7850
 
@@ -8752,8 +8888,8 @@ Next: run \`npx protect-mcp dashboard --open\` and review tool inventory, policy
8752
8888
 
8753
8889
  // src/cli.ts
8754
8890
  var import_node_crypto7 = require("crypto");
8755
- var import_node_fs12 = require("fs");
8756
- var import_node_path8 = require("path");
8891
+ var import_node_fs13 = require("fs");
8892
+ var import_node_path9 = require("path");
8757
8893
  var import_node_os = require("os");
8758
8894
  function printHelp() {
8759
8895
  process.stderr.write(`
@@ -8775,14 +8911,17 @@ Usage:
8775
8911
  protect-mcp policy-packs list|show|install [pack] [--dir ./cedar] [--force]
8776
8912
  protect-mcp connect
8777
8913
  protect-mcp init [--dir <path>]
8914
+ protect-mcp sample [--dir <path>] [--force]
8915
+ protect-mcp policy list|show|allow <tool>|deny <tool>|path
8778
8916
  protect-mcp demo
8779
8917
  protect-mcp trace <receipt_id> [--endpoint <url>] [--depth <n>]
8780
8918
  protect-mcp status [--dir <path>]
8781
8919
  protect-mcp digest [--today] [--dir <path>]
8782
8920
  protect-mcp receipts [--last <n>] [--dir <path>]
8783
8921
  protect-mcp record [--dir <path>] [--live] [--no-open]
8784
- protect-mcp claim [--no <cap>] [--only <c,c>] [--count <verdict>] [--dir <path>] [--output <path>]
8785
- protect-mcp verify-claim <claim.json> [--key <public-hex>]
8922
+ protect-mcp claim [--no <cap>] [--only <c,c>] [--count <verdict>] [--payment-under <amount>] [--anchor] [--dir <path>] [--output <path>]
8923
+ protect-mcp verify-claim <claim.json> [--key <public-hex>] [--check-anchor] [--offline]
8924
+ protect-mcp anchor-record [--dir <path>] [--force]
8786
8925
  protect-mcp bundle [--output <path>] [--dir <path>]
8787
8926
  protect-mcp simulate --policy <path> [--log <path>] [--tier <tier>] [--json]
8788
8927
  protect-mcp report [--period <days>d] [--format md|json] [--output <path>] [--dir <path>]
@@ -8796,6 +8935,7 @@ Options:
8796
8935
  --port <port> HTTP server port (default: 3000 for --http, 9377 for serve)
8797
8936
  --verbose Enable debug logging to stderr
8798
8937
  --help Show this help
8938
+ --version Print the installed version
8799
8939
 
8800
8940
  Commands:
8801
8941
  serve Start HTTP hook server for Claude Code integration (port 9377)
@@ -8906,17 +9046,17 @@ function parseArgs(argv) {
8906
9046
  return { policyPath, cedarDir, slug, enforce, verbose, childCommand };
8907
9047
  }
8908
9048
  async function handleInit(argv) {
8909
- const { writeFileSync: writeFileSync4, existsSync: existsSync9, mkdirSync: mkdirSync3 } = await import("fs");
8910
- const { join: join8 } = await import("path");
9049
+ const { writeFileSync: writeFileSync5, existsSync: existsSync10, mkdirSync: mkdirSync4 } = await import("fs");
9050
+ const { join: join9 } = await import("path");
8911
9051
  let dir = process.cwd();
8912
9052
  const dirIdx = argv.indexOf("--dir");
8913
9053
  if (dirIdx !== -1 && argv[dirIdx + 1]) {
8914
9054
  dir = argv[dirIdx + 1];
8915
9055
  }
8916
- const configPath = join8(dir, "protect-mcp.json");
8917
- const keysDir = join8(dir, "keys");
8918
- const keyPath = join8(keysDir, "gateway.json");
8919
- if (existsSync9(configPath)) {
9056
+ const configPath = join9(dir, "protect-mcp.json");
9057
+ const keysDir = join9(dir, "keys");
9058
+ const keyPath = join9(keysDir, "gateway.json");
9059
+ if (existsSync10(configPath)) {
8920
9060
  process.stderr.write(`[PROTECT_MCP] Config already exists at ${configPath}
8921
9061
  `);
8922
9062
  process.stderr.write("[PROTECT_MCP] Delete it first if you want to regenerate.\n");
@@ -8935,19 +9075,19 @@ async function handleInit(argv) {
8935
9075
  kid: "generated"
8936
9076
  };
8937
9077
  }
8938
- if (!existsSync9(keysDir)) {
8939
- mkdirSync3(keysDir, { recursive: true });
9078
+ if (!existsSync10(keysDir)) {
9079
+ mkdirSync4(keysDir, { recursive: true });
8940
9080
  }
8941
- writeFileSync4(keyPath, JSON.stringify({
9081
+ writeFileSync5(keyPath, JSON.stringify({
8942
9082
  privateKey: keypair.privateKey,
8943
9083
  publicKey: keypair.publicKey,
8944
9084
  kid: keypair.kid,
8945
9085
  generated_at: (/* @__PURE__ */ new Date()).toISOString(),
8946
9086
  warning: "KEEP THIS FILE SECRET. Never commit to version control."
8947
9087
  }, null, 2) + "\n");
8948
- const gitignorePath = join8(keysDir, ".gitignore");
8949
- if (!existsSync9(gitignorePath)) {
8950
- writeFileSync4(gitignorePath, "# Never commit signing keys\n*.json\n");
9088
+ const gitignorePath = join9(keysDir, ".gitignore");
9089
+ if (!existsSync10(gitignorePath)) {
9090
+ writeFileSync5(gitignorePath, "# Never commit signing keys\n*.json\n");
8951
9091
  }
8952
9092
  const config = {
8953
9093
  tools: {
@@ -8981,7 +9121,7 @@ async function handleInit(argv) {
8981
9121
  }
8982
9122
  }
8983
9123
  };
8984
- writeFileSync4(configPath, JSON.stringify(config, null, 2) + "\n");
9124
+ writeFileSync5(configPath, JSON.stringify(config, null, 2) + "\n");
8985
9125
  const claudeConfig = {
8986
9126
  "mcpServers": {
8987
9127
  "my-server": {
@@ -9019,8 +9159,8 @@ Add --enforce when ready to block policy violations.
9019
9159
  `);
9020
9160
  }
9021
9161
  async function handleDemo() {
9022
- const { existsSync: existsSync9 } = await import("fs");
9023
- const { join: join8, dirname: dirname3, resolve } = await import("path");
9162
+ const { existsSync: existsSync10 } = await import("fs");
9163
+ const { join: join9, dirname: dirname3, resolve } = await import("path");
9024
9164
  const { realpathSync } = await import("fs");
9025
9165
  const cliPath = resolve(process.argv[1] || "dist/cli.js");
9026
9166
  let cliDir;
@@ -9029,9 +9169,9 @@ async function handleDemo() {
9029
9169
  } catch {
9030
9170
  cliDir = dirname3(cliPath);
9031
9171
  }
9032
- const demoServerPath = join8(cliDir, "demo-server.js");
9033
- const configPath = join8(process.cwd(), "protect-mcp.json");
9034
- const hasConfig = existsSync9(configPath);
9172
+ const demoServerPath = join9(cliDir, "demo-server.js");
9173
+ const configPath = join9(process.cwd(), "protect-mcp.json");
9174
+ const hasConfig = existsSync10(configPath);
9035
9175
  if (!hasConfig) {
9036
9176
  process.stderr.write(`
9037
9177
  ${bold("protect-mcp demo")}
@@ -9105,15 +9245,15 @@ Starting demo server with 5 tools...
9105
9245
  await gateway.start();
9106
9246
  }
9107
9247
  async function handleStatus2(argv) {
9108
- const { readFileSync: readFileSync11, existsSync: existsSync9 } = await import("fs");
9109
- const { join: join8 } = await import("path");
9248
+ const { readFileSync: readFileSync11, existsSync: existsSync10 } = await import("fs");
9249
+ const { join: join9 } = await import("path");
9110
9250
  let dir = process.cwd();
9111
9251
  const dirIdx = argv.indexOf("--dir");
9112
9252
  if (dirIdx !== -1 && argv[dirIdx + 1]) {
9113
9253
  dir = argv[dirIdx + 1];
9114
9254
  }
9115
- const logPath = join8(dir, ".protect-mcp-log.jsonl");
9116
- if (!existsSync9(logPath)) {
9255
+ const logPath = join9(dir, ".protect-mcp-log.jsonl");
9256
+ if (!existsSync10(logPath)) {
9117
9257
  process.stderr.write(`${bold("protect-mcp status")}
9118
9258
 
9119
9259
  `);
@@ -9202,8 +9342,8 @@ ${bold("protect-mcp status")}
9202
9342
  process.stdout.write(` ${reason.padEnd(25)} ${count}
9203
9343
  `);
9204
9344
  }
9205
- const evidencePath = join8(dir, ".protect-mcp-evidence.json");
9206
- if (existsSync9(evidencePath)) {
9345
+ const evidencePath = join9(dir, ".protect-mcp-evidence.json");
9346
+ if (existsSync10(evidencePath)) {
9207
9347
  try {
9208
9348
  const evidenceRaw = readFileSync11(evidencePath, "utf-8");
9209
9349
  const evidence = JSON.parse(evidenceRaw);
@@ -9214,8 +9354,8 @@ ${bold("protect-mcp status")}
9214
9354
  } catch {
9215
9355
  }
9216
9356
  }
9217
- const keyPath = join8(dir, "keys", "gateway.json");
9218
- if (existsSync9(keyPath)) {
9357
+ const keyPath = join9(dir, "keys", "gateway.json");
9358
+ if (existsSync10(keyPath)) {
9219
9359
  try {
9220
9360
  const keyData = JSON.parse(readFileSync11(keyPath, "utf-8"));
9221
9361
  if (keyData.publicKey) {
@@ -9245,7 +9385,7 @@ function commandNeedsValue(argv, flag) {
9245
9385
  return Boolean(value && !value.startsWith("--"));
9246
9386
  }
9247
9387
  function absoluteOrCwd(pathValue) {
9248
- return (0, import_node_path8.resolve)(process.cwd(), pathValue);
9388
+ return (0, import_node_path9.resolve)(process.cwd(), pathValue);
9249
9389
  }
9250
9390
  function shellQuoteArg(arg) {
9251
9391
  if (/^[A-Za-z0-9_./:@%+=,-]+$/.test(arg)) return arg;
@@ -9264,18 +9404,18 @@ function wrapperArgsFor(command, opts) {
9264
9404
  }
9265
9405
  function claudeDesktopConfigPath() {
9266
9406
  if (process.platform === "darwin") {
9267
- return (0, import_node_path8.join)((0, import_node_os.homedir)(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
9407
+ return (0, import_node_path9.join)((0, import_node_os.homedir)(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
9268
9408
  }
9269
9409
  if (process.platform === "win32") {
9270
- return (0, import_node_path8.join)(process.env.APPDATA || (0, import_node_path8.join)((0, import_node_os.homedir)(), "AppData", "Roaming"), "Claude", "claude_desktop_config.json");
9410
+ return (0, import_node_path9.join)(process.env.APPDATA || (0, import_node_path9.join)((0, import_node_os.homedir)(), "AppData", "Roaming"), "Claude", "claude_desktop_config.json");
9271
9411
  }
9272
- return (0, import_node_path8.join)((0, import_node_os.homedir)(), ".config", "Claude", "claude_desktop_config.json");
9412
+ return (0, import_node_path9.join)((0, import_node_os.homedir)(), ".config", "Claude", "claude_desktop_config.json");
9273
9413
  }
9274
9414
  async function ensureLocalConfig(dir = process.cwd()) {
9275
- const { existsSync: existsSync9 } = await import("fs");
9276
- const { join: join8, resolve } = await import("path");
9277
- const configPath = join8(dir, "protect-mcp.json");
9278
- if (!existsSync9(configPath)) {
9415
+ const { existsSync: existsSync10 } = await import("fs");
9416
+ const { join: join9, resolve } = await import("path");
9417
+ const configPath = join9(dir, "protect-mcp.json");
9418
+ if (!existsSync10(configPath)) {
9279
9419
  process.stderr.write(`${bold("protect-mcp wrap")}
9280
9420
 
9281
9421
  No protect-mcp.json found; creating local shadow-mode config first.
@@ -9287,7 +9427,7 @@ No protect-mcp.json found; creating local shadow-mode config first.
9287
9427
  }
9288
9428
  function parseJsonlFile(pathValue) {
9289
9429
  try {
9290
- const raw = (0, import_node_fs12.readFileSync)(pathValue, "utf-8");
9430
+ const raw = (0, import_node_fs13.readFileSync)(pathValue, "utf-8");
9291
9431
  return raw.split("\n").map((line) => line.trim()).filter(Boolean).flatMap((line) => {
9292
9432
  try {
9293
9433
  return [JSON.parse(line)];
@@ -9301,7 +9441,7 @@ function parseJsonlFile(pathValue) {
9301
9441
  }
9302
9442
  function parseJsonlRecords(pathValue) {
9303
9443
  try {
9304
- const raw = (0, import_node_fs12.readFileSync)(pathValue, "utf-8");
9444
+ const raw = (0, import_node_fs13.readFileSync)(pathValue, "utf-8");
9305
9445
  return raw.split("\n").map((line) => line.trim()).filter(Boolean).flatMap((line) => {
9306
9446
  try {
9307
9447
  return [{
@@ -9319,8 +9459,8 @@ function parseJsonlRecords(pathValue) {
9319
9459
  }
9320
9460
  function loadPolicyJson(policyPath) {
9321
9461
  try {
9322
- if (!(0, import_node_fs12.existsSync)(policyPath)) return null;
9323
- return JSON.parse((0, import_node_fs12.readFileSync)(policyPath, "utf-8"));
9462
+ if (!(0, import_node_fs13.existsSync)(policyPath)) return null;
9463
+ return JSON.parse((0, import_node_fs13.readFileSync)(policyPath, "utf-8"));
9324
9464
  } catch {
9325
9465
  return null;
9326
9466
  }
@@ -9465,10 +9605,10 @@ function suggestedGuardrailFor(_tool, risk, reasons) {
9465
9605
  policy: { rate_limit: "100/hour" }
9466
9606
  };
9467
9607
  }
9468
- function buildDashboardSummary(dir, policyPath = (0, import_node_path8.join)(dir, "protect-mcp.json")) {
9469
- const logPath = (0, import_node_path8.join)(dir, ".protect-mcp-log.jsonl");
9470
- const receiptPath = (0, import_node_path8.join)(dir, ".protect-mcp-receipts.jsonl");
9471
- const keyPath = (0, import_node_path8.join)(dir, "keys", "gateway.json");
9608
+ function buildDashboardSummary(dir, policyPath = (0, import_node_path9.join)(dir, "protect-mcp.json")) {
9609
+ const logPath = (0, import_node_path9.join)(dir, ".protect-mcp-log.jsonl");
9610
+ const receiptPath = (0, import_node_path9.join)(dir, ".protect-mcp-receipts.jsonl");
9611
+ const keyPath = (0, import_node_path9.join)(dir, "keys", "gateway.json");
9472
9612
  const entries = parseJsonlFile(logPath);
9473
9613
  const receiptRecords = parseJsonlRecords(receiptPath);
9474
9614
  const receipts = receiptRecords.map((record) => record.value);
@@ -9513,9 +9653,9 @@ function buildDashboardSummary(dir, policyPath = (0, import_node_path8.join)(dir
9513
9653
  const pendingApprovals = entries.filter((e) => e.decision === "require_approval").slice(-25).reverse();
9514
9654
  const chains = buildReceiptChains(entries, receiptRecords);
9515
9655
  let key = null;
9516
- if ((0, import_node_fs12.existsSync)(keyPath)) {
9656
+ if ((0, import_node_fs13.existsSync)(keyPath)) {
9517
9657
  try {
9518
- const parsed = JSON.parse((0, import_node_fs12.readFileSync)(keyPath, "utf-8"));
9658
+ const parsed = JSON.parse((0, import_node_fs13.readFileSync)(keyPath, "utf-8"));
9519
9659
  key = {
9520
9660
  kid: parsed.kid || null,
9521
9661
  issuer: parsed.issuer || "protect-mcp",
@@ -9532,10 +9672,10 @@ function buildDashboardSummary(dir, policyPath = (0, import_node_path8.join)(dir
9532
9672
  receipts: receiptPath,
9533
9673
  key: keyPath,
9534
9674
  policy: policyPath,
9535
- log_exists: (0, import_node_fs12.existsSync)(logPath),
9536
- receipts_exist: (0, import_node_fs12.existsSync)(receiptPath),
9537
- key_exists: (0, import_node_fs12.existsSync)(keyPath),
9538
- policy_exists: (0, import_node_fs12.existsSync)(policyPath)
9675
+ log_exists: (0, import_node_fs13.existsSync)(logPath),
9676
+ receipts_exist: (0, import_node_fs13.existsSync)(receiptPath),
9677
+ key_exists: (0, import_node_fs13.existsSync)(keyPath),
9678
+ policy_exists: (0, import_node_fs13.existsSync)(policyPath)
9539
9679
  },
9540
9680
  totals: {
9541
9681
  decisions: entries.length,
@@ -9572,7 +9712,7 @@ function buildDashboardSummary(dir, policyPath = (0, import_node_path8.join)(dir
9572
9712
  }))
9573
9713
  },
9574
9714
  connector_pilots: {
9575
- directory: (0, import_node_path8.join)(dir, ".protect-mcp", "connectors"),
9715
+ directory: (0, import_node_path9.join)(dir, ".protect-mcp", "connectors"),
9576
9716
  installed: readInstalledConnectorPilots(dir),
9577
9717
  doctor: connectorDoctor(dir),
9578
9718
  available: CONNECTOR_PILOTS.map((pilot) => ({
@@ -9950,7 +10090,7 @@ async function handleDashboard(argv) {
9950
10090
  const { resolve } = await import("path");
9951
10091
  const port = commandNeedsValue(argv, "--port") ? parseInt(flagValue(argv, "--port") || "9877", 10) : 9877;
9952
10092
  const dir = resolve(commandNeedsValue(argv, "--dir") ? flagValue(argv, "--dir") || process.cwd() : process.cwd());
9953
- const policyPath = resolve(flagValue(argv, "--policy") || (0, import_node_path8.join)(dir, "protect-mcp.json"));
10093
+ const policyPath = resolve(flagValue(argv, "--policy") || (0, import_node_path9.join)(dir, "protect-mcp.json"));
9954
10094
  const approvalEndpoint = flagValue(argv, "--approval-endpoint");
9955
10095
  const approvalNonce = flagValue(argv, "--approval-nonce");
9956
10096
  const open = argv.includes("--open");
@@ -10155,32 +10295,32 @@ function writeToolPolicy(policyPath, tool, action) {
10155
10295
  tools,
10156
10296
  default_tier: existing.default_tier || "unknown"
10157
10297
  };
10158
- (0, import_node_fs12.writeFileSync)(policyPath, JSON.stringify(next, null, 2) + "\n");
10298
+ (0, import_node_fs13.writeFileSync)(policyPath, JSON.stringify(next, null, 2) + "\n");
10159
10299
  return next;
10160
10300
  }
10161
10301
  function policyPackDirectory(dir) {
10162
- return (0, import_node_path8.join)(dir, "cedar");
10302
+ return (0, import_node_path9.join)(dir, "cedar");
10163
10303
  }
10164
10304
  function installedPolicyPackIds(dir) {
10165
10305
  const cedarDir = policyPackDirectory(dir);
10166
10306
  return POLICY_PACKS.filter(
10167
- (pack) => pack.files.every((file) => (0, import_node_fs12.existsSync)((0, import_node_path8.join)(cedarDir, file.path)))
10307
+ (pack) => pack.files.every((file) => (0, import_node_fs13.existsSync)((0, import_node_path9.join)(cedarDir, file.path)))
10168
10308
  ).map((pack) => pack.id);
10169
10309
  }
10170
10310
  function installPolicyPackToDir(dir, packId, force = false) {
10171
10311
  const packs = packId === "all" ? POLICY_PACKS : [getPolicyPack(packId)].filter(Boolean);
10172
10312
  if (packs.length === 0) throw new Error(`Unknown policy pack: ${packId}`);
10173
10313
  const outDir = policyPackDirectory(dir);
10174
- (0, import_node_fs12.mkdirSync)(outDir, { recursive: true });
10314
+ (0, import_node_fs13.mkdirSync)(outDir, { recursive: true });
10175
10315
  const written = [];
10176
10316
  for (const pack of packs) {
10177
10317
  for (const file of pack.files) {
10178
- const outPath = (0, import_node_path8.join)(outDir, file.path);
10179
- if ((0, import_node_fs12.existsSync)(outPath) && !force) {
10318
+ const outPath = (0, import_node_path9.join)(outDir, file.path);
10319
+ if ((0, import_node_fs13.existsSync)(outPath) && !force) {
10180
10320
  throw new Error(`Refusing to overwrite ${outPath}. Pass force=true if intentional.`);
10181
10321
  }
10182
- (0, import_node_fs12.mkdirSync)((0, import_node_path8.dirname)(outPath), { recursive: true });
10183
- (0, import_node_fs12.writeFileSync)(outPath, file.contents.endsWith("\n") ? file.contents : `${file.contents}
10322
+ (0, import_node_fs13.mkdirSync)((0, import_node_path9.dirname)(outPath), { recursive: true });
10323
+ (0, import_node_fs13.writeFileSync)(outPath, file.contents.endsWith("\n") ? file.contents : `${file.contents}
10184
10324
  `);
10185
10325
  written.push(outPath);
10186
10326
  }
@@ -10188,19 +10328,19 @@ function installPolicyPackToDir(dir, packId, force = false) {
10188
10328
  return { dir: outDir, written, packs: packs.map((pack) => pack.id) };
10189
10329
  }
10190
10330
  function dashboardRegistryStatus(dir) {
10191
- const identityPath = (0, import_node_path8.join)(dir, ".protect-mcp-org.json");
10192
- const registryPath = (0, import_node_path8.join)(dir, ".protect-mcp-registry.json");
10193
- const verifierPath = (0, import_node_path8.join)(dir, "scopeblind-verifier.html");
10194
- const identity = (0, import_node_fs12.existsSync)(identityPath) ? (() => {
10331
+ const identityPath = (0, import_node_path9.join)(dir, ".protect-mcp-org.json");
10332
+ const registryPath = (0, import_node_path9.join)(dir, ".protect-mcp-registry.json");
10333
+ const verifierPath = (0, import_node_path9.join)(dir, "scopeblind-verifier.html");
10334
+ const identity = (0, import_node_fs13.existsSync)(identityPath) ? (() => {
10195
10335
  try {
10196
- return JSON.parse((0, import_node_fs12.readFileSync)(identityPath, "utf-8"));
10336
+ return JSON.parse((0, import_node_fs13.readFileSync)(identityPath, "utf-8"));
10197
10337
  } catch {
10198
10338
  return null;
10199
10339
  }
10200
10340
  })() : null;
10201
- const registry = (0, import_node_fs12.existsSync)(registryPath) ? (() => {
10341
+ const registry = (0, import_node_fs13.existsSync)(registryPath) ? (() => {
10202
10342
  try {
10203
- return JSON.parse((0, import_node_fs12.readFileSync)(registryPath, "utf-8"));
10343
+ return JSON.parse((0, import_node_fs13.readFileSync)(registryPath, "utf-8"));
10204
10344
  } catch {
10205
10345
  return null;
10206
10346
  }
@@ -10208,9 +10348,9 @@ function dashboardRegistryStatus(dir) {
10208
10348
  const anchors = Array.isArray(registry?.anchors) ? registry.anchors : [];
10209
10349
  const hosted = anchors.some((anchor) => anchor.timestamp_source === "scopeblind-hosted");
10210
10350
  return {
10211
- identity_exists: (0, import_node_fs12.existsSync)(identityPath),
10212
- registry_exists: (0, import_node_fs12.existsSync)(registryPath),
10213
- verifier_exists: (0, import_node_fs12.existsSync)(verifierPath),
10351
+ identity_exists: (0, import_node_fs13.existsSync)(identityPath),
10352
+ registry_exists: (0, import_node_fs13.existsSync)(registryPath),
10353
+ verifier_exists: (0, import_node_fs13.existsSync)(verifierPath),
10214
10354
  identity_path: identityPath,
10215
10355
  registry_path: registryPath,
10216
10356
  verifier_path: verifierPath,
@@ -10231,13 +10371,13 @@ async function readJsonBody(req) {
10231
10371
  }
10232
10372
  async function buildAuditBundleForDir(dir) {
10233
10373
  const { createAuditBundle: createAuditBundle2 } = await Promise.resolve().then(() => (init_bundle(), bundle_exports));
10234
- const receiptPath = (0, import_node_path8.join)(dir, ".protect-mcp-receipts.jsonl");
10235
- const keyPath = (0, import_node_path8.join)(dir, "keys", "gateway.json");
10236
- if (!(0, import_node_fs12.existsSync)(receiptPath)) throw new Error("No receipt file found.");
10237
- if (!(0, import_node_fs12.existsSync)(keyPath)) throw new Error("No signing key found.");
10374
+ const receiptPath = (0, import_node_path9.join)(dir, ".protect-mcp-receipts.jsonl");
10375
+ const keyPath = (0, import_node_path9.join)(dir, "keys", "gateway.json");
10376
+ if (!(0, import_node_fs13.existsSync)(receiptPath)) throw new Error("No receipt file found.");
10377
+ if (!(0, import_node_fs13.existsSync)(keyPath)) throw new Error("No signing key found.");
10238
10378
  const receipts = parseJsonlFile(receiptPath);
10239
10379
  if (receipts.length === 0) throw new Error("No signed receipts found.");
10240
- const keyData = JSON.parse((0, import_node_fs12.readFileSync)(keyPath, "utf-8"));
10380
+ const keyData = JSON.parse((0, import_node_fs13.readFileSync)(keyPath, "utf-8"));
10241
10381
  return createAuditBundle2({
10242
10382
  tenant: keyData.issuer || "protect-mcp",
10243
10383
  receipts,
@@ -10255,17 +10395,17 @@ function collectSelectiveDisclosurePackages(dir) {
10255
10395
  const out = [];
10256
10396
  const seen = /* @__PURE__ */ new Set();
10257
10397
  const candidates = [];
10258
- const receiptsDir = (0, import_node_path8.join)(dir, "receipts");
10259
- if ((0, import_node_fs12.existsSync)(receiptsDir)) {
10260
- for (const name of (0, import_node_fs12.readdirSync)(receiptsDir)) {
10398
+ const receiptsDir = (0, import_node_path9.join)(dir, "receipts");
10399
+ if ((0, import_node_fs13.existsSync)(receiptsDir)) {
10400
+ for (const name of (0, import_node_fs13.readdirSync)(receiptsDir)) {
10261
10401
  if (name.includes("selective-disclosure") && name.endsWith(".json")) {
10262
- candidates.push((0, import_node_path8.join)(receiptsDir, name));
10402
+ candidates.push((0, import_node_path9.join)(receiptsDir, name));
10263
10403
  }
10264
10404
  }
10265
10405
  }
10266
- const jsonlPath = (0, import_node_path8.join)(dir, ".protect-mcp-selective-disclosures.jsonl");
10267
- if ((0, import_node_fs12.existsSync)(jsonlPath)) {
10268
- for (const line of (0, import_node_fs12.readFileSync)(jsonlPath, "utf-8").split("\n").map((s) => s.trim()).filter(Boolean)) {
10406
+ const jsonlPath = (0, import_node_path9.join)(dir, ".protect-mcp-selective-disclosures.jsonl");
10407
+ if ((0, import_node_fs13.existsSync)(jsonlPath)) {
10408
+ for (const line of (0, import_node_fs13.readFileSync)(jsonlPath, "utf-8").split("\n").map((s) => s.trim()).filter(Boolean)) {
10269
10409
  try {
10270
10410
  const parsed = JSON.parse(line);
10271
10411
  addSelectiveDisclosure(out, seen, parsed);
@@ -10275,7 +10415,7 @@ function collectSelectiveDisclosurePackages(dir) {
10275
10415
  }
10276
10416
  for (const path of candidates) {
10277
10417
  try {
10278
- const parsed = JSON.parse((0, import_node_fs12.readFileSync)(path, "utf-8"));
10418
+ const parsed = JSON.parse((0, import_node_fs13.readFileSync)(path, "utf-8"));
10279
10419
  addSelectiveDisclosure(out, seen, parsed);
10280
10420
  } catch {
10281
10421
  }
@@ -10308,7 +10448,7 @@ async function recordApprovalResolution(opts) {
10308
10448
  takeover_note: opts.body.takeover_note || void 0,
10309
10449
  payload_hash: opts.body.payload_hash || void 0
10310
10450
  };
10311
- (0, import_node_fs12.appendFileSync)((0, import_node_path8.join)(opts.dir, ".protect-mcp-approval-resolutions.jsonl"), JSON.stringify(record) + "\n");
10451
+ (0, import_node_fs13.appendFileSync)((0, import_node_path9.join)(opts.dir, ".protect-mcp-approval-resolutions.jsonl"), JSON.stringify(record) + "\n");
10312
10452
  let forwarded = null;
10313
10453
  if (resolution === "approve" && opts.approvalEndpoint && opts.approvalNonce) {
10314
10454
  const endpoint = opts.approvalEndpoint.replace(/\/$/, "") + "/approve";
@@ -10331,7 +10471,7 @@ async function recordApprovalResolution(opts) {
10331
10471
  return { recorded: true, resolution: record, forwarded };
10332
10472
  }
10333
10473
  async function handleRecommend(argv) {
10334
- const { writeFileSync: writeFileSync4 } = await import("fs");
10474
+ const { writeFileSync: writeFileSync5 } = await import("fs");
10335
10475
  const { resolve } = await import("path");
10336
10476
  const dir = resolve(commandNeedsValue(argv, "--dir") ? flagValue(argv, "--dir") || process.cwd() : process.cwd());
10337
10477
  const outputPath = resolve(flagValue(argv, "--output") || "protect-mcp.recommended.json");
@@ -10380,7 +10520,7 @@ Dry run only. Write the policy with:
10380
10520
  process.stdout.write(dim(body));
10381
10521
  return;
10382
10522
  }
10383
- writeFileSync4(outputPath, body);
10523
+ writeFileSync5(outputPath, body);
10384
10524
  process.stdout.write(`
10385
10525
  ${green("\u2713 Wrote recommended policy")}
10386
10526
  `);
@@ -10393,7 +10533,7 @@ ${green("\u2713 Wrote recommended policy")}
10393
10533
  `);
10394
10534
  }
10395
10535
  async function handleWrap(argv) {
10396
- const { existsSync: existsSync9, readFileSync: readFileSync11, writeFileSync: writeFileSync4 } = await import("fs");
10536
+ const { existsSync: existsSync10, readFileSync: readFileSync11, writeFileSync: writeFileSync5 } = await import("fs");
10397
10537
  const { resolve } = await import("path");
10398
10538
  const configFlag = flagValue(argv, "--config");
10399
10539
  const cedarFlag = flagValue(argv, "--cedar");
@@ -10430,7 +10570,7 @@ ${bold("protect-mcp wrap")}
10430
10570
  return;
10431
10571
  }
10432
10572
  const claudePath = resolve(flagValue(argv, "--path") || claudeDesktopConfigPath());
10433
- if (!claudeDesktop && !existsSync9(claudePath)) {
10573
+ if (!claudeDesktop && !existsSync10(claudePath)) {
10434
10574
  process.stdout.write(`
10435
10575
  ${bold("protect-mcp wrap")}
10436
10576
 
@@ -10447,7 +10587,7 @@ ${bold("protect-mcp wrap")}
10447
10587
  `);
10448
10588
  return;
10449
10589
  }
10450
- if (!existsSync9(claudePath)) {
10590
+ if (!existsSync10(claudePath)) {
10451
10591
  process.stderr.write(`protect-mcp wrap: Claude Desktop config not found at ${claudePath}
10452
10592
  `);
10453
10593
  process.exit(1);
@@ -10518,8 +10658,8 @@ Dry run only. Apply with:
10518
10658
  return;
10519
10659
  }
10520
10660
  const backupPath = `${claudePath}.bak.${Date.now()}`;
10521
- writeFileSync4(backupPath, readFileSync11(claudePath, "utf-8"));
10522
- writeFileSync4(claudePath, JSON.stringify(next, null, 2) + "\n");
10661
+ writeFileSync5(backupPath, readFileSync11(claudePath, "utf-8"));
10662
+ writeFileSync5(claudePath, JSON.stringify(next, null, 2) + "\n");
10523
10663
  process.stdout.write(`
10524
10664
  ${green("\u2713 Claude Desktop config updated")}
10525
10665
  `);
@@ -10545,14 +10685,14 @@ function yellow(s) {
10545
10685
  return process.env.NO_COLOR ? s : `\x1B[33m${s}\x1B[0m`;
10546
10686
  }
10547
10687
  async function handleDigest(argv) {
10548
- const { readFileSync: readFileSync11, existsSync: existsSync9 } = await import("fs");
10549
- const { join: join8 } = await import("path");
10688
+ const { readFileSync: readFileSync11, existsSync: existsSync10 } = await import("fs");
10689
+ const { join: join9 } = await import("path");
10550
10690
  let dir = process.cwd();
10551
10691
  const dirIdx = argv.indexOf("--dir");
10552
10692
  if (dirIdx !== -1 && argv[dirIdx + 1]) dir = argv[dirIdx + 1];
10553
10693
  const today = argv.includes("--today");
10554
- const logPath = join8(dir, ".protect-mcp-log.jsonl");
10555
- if (!existsSync9(logPath)) {
10694
+ const logPath = join9(dir, ".protect-mcp-log.jsonl");
10695
+ if (!existsSync10(logPath)) {
10556
10696
  process.stderr.write(`${bold("protect-mcp digest")}
10557
10697
 
10558
10698
  No log file found. Run protect-mcp first.
@@ -10636,15 +10776,15 @@ ${bold("\u{1F6E1}\uFE0F Agent Daily Digest")}
10636
10776
  `);
10637
10777
  }
10638
10778
  async function handleReceipts2(argv) {
10639
- const { readFileSync: readFileSync11, existsSync: existsSync9 } = await import("fs");
10640
- const { join: join8 } = await import("path");
10779
+ const { readFileSync: readFileSync11, existsSync: existsSync10 } = await import("fs");
10780
+ const { join: join9 } = await import("path");
10641
10781
  let dir = process.cwd();
10642
10782
  const dirIdx = argv.indexOf("--dir");
10643
10783
  if (dirIdx !== -1 && argv[dirIdx + 1]) dir = argv[dirIdx + 1];
10644
10784
  const lastIdx = argv.indexOf("--last");
10645
10785
  const count = lastIdx !== -1 && argv[lastIdx + 1] ? parseInt(argv[lastIdx + 1], 10) : 20;
10646
- const receiptsPath = join8(dir, ".protect-mcp-receipts.jsonl");
10647
- if (!existsSync9(receiptsPath)) {
10786
+ const receiptsPath = join9(dir, ".protect-mcp-receipts.jsonl");
10787
+ if (!existsSync10(receiptsPath)) {
10648
10788
  process.stderr.write(`${bold("protect-mcp receipts")}
10649
10789
 
10650
10790
  No signed receipt file found. Run protect-mcp with signing enabled first.
@@ -10678,19 +10818,19 @@ async function pkgVersion() {
10678
10818
  if (_pkgV) return _pkgV;
10679
10819
  let v = "0.0.0";
10680
10820
  try {
10681
- const { readFileSync: readFileSync11, existsSync: existsSync9, realpathSync } = await import("fs");
10682
- const { dirname: dirname3, join: join8, resolve } = await import("path");
10821
+ const { readFileSync: readFileSync11, existsSync: existsSync10, realpathSync } = await import("fs");
10822
+ const { dirname: dirname3, join: join9, resolve } = await import("path");
10683
10823
  let base = "";
10684
10824
  try {
10685
10825
  base = dirname3(realpathSync(resolve(process.argv[1] || "")));
10686
10826
  } catch {
10687
10827
  }
10688
10828
  const candidates = [
10689
- base ? join8(base, "..", "package.json") : "",
10690
- base ? join8(base, "package.json") : ""
10829
+ base ? join9(base, "..", "package.json") : "",
10830
+ base ? join9(base, "package.json") : ""
10691
10831
  ].filter(Boolean);
10692
10832
  for (const p of candidates) {
10693
- if (existsSync9(p)) {
10833
+ if (existsSync10(p)) {
10694
10834
  const parsed = JSON.parse(readFileSync11(p, "utf-8"));
10695
10835
  if (parsed && parsed.name === "protect-mcp" && parsed.version) {
10696
10836
  v = parsed.version;
@@ -10727,16 +10867,16 @@ function mapRecordEntry(e) {
10727
10867
  return { ts, tool, verdict, reason, hook, signed, caps, agent, dur, id: String(e.request_id || p.request_id || ""), digest, raw: e };
10728
10868
  }
10729
10869
  async function handleRecord(argv) {
10730
- const { readFileSync: readFileSync11, existsSync: existsSync9, writeFileSync: writeFileSync4 } = await import("fs");
10731
- const { join: join8 } = await import("path");
10870
+ const { readFileSync: readFileSync11, existsSync: existsSync10, writeFileSync: writeFileSync5 } = await import("fs");
10871
+ const { join: join9 } = await import("path");
10732
10872
  const osMod = await import("os");
10733
10873
  const cp = await import("child_process");
10734
10874
  let dir = process.cwd();
10735
10875
  const di = argv.indexOf("--dir");
10736
10876
  if (di !== -1 && argv[di + 1]) dir = argv[di + 1];
10737
- const recPath = join8(dir, ".protect-mcp-receipts.jsonl");
10738
- const logPath = join8(dir, ".protect-mcp-log.jsonl");
10739
- const pick = () => existsSync9(recPath) ? recPath : existsSync9(logPath) ? logPath : null;
10877
+ const recPath = join9(dir, ".protect-mcp-receipts.jsonl");
10878
+ const logPath = join9(dir, ".protect-mcp-log.jsonl");
10879
+ const pick = () => existsSync10(recPath) ? recPath : existsSync10(logPath) ? logPath : null;
10740
10880
  const chosen = pick();
10741
10881
  if (!chosen) {
10742
10882
  process.stderr.write(`
@@ -10761,7 +10901,7 @@ Start the gate with ${bold("npx protect-mcp serve")}, use your agent, then run t
10761
10901
  let pinnedKey = "";
10762
10902
  let pinnedKid = "";
10763
10903
  try {
10764
- const kd = JSON.parse(readFileSync11(join8(dir, "keys", "gateway.json"), "utf-8"));
10904
+ const kd = JSON.parse(readFileSync11(join9(dir, "keys", "gateway.json"), "utf-8"));
10765
10905
  if (kd && typeof kd.publicKey === "string" && /^[0-9a-f]{64}$/i.test(kd.publicKey)) {
10766
10906
  pinnedKey = kd.publicKey;
10767
10907
  pinnedKid = typeof kd.kid === "string" ? kd.kid : "";
@@ -10822,8 +10962,8 @@ ${bold("\u{1F6E1}\uFE0F Your record")} ${dim("\xB7")} live at ${url}
10822
10962
  const recs = readRecs(chosen);
10823
10963
  const meta = { file: chosen, signed: chosen === recPath, count: recs.length, live: false, pinned_key: pinnedKey, pinned_kid: pinnedKid };
10824
10964
  const html = RECORD_HTML.replace("__DATA__", () => JSON.stringify(recs)).replace("__META__", () => JSON.stringify(meta));
10825
- const out = join8(osMod.tmpdir(), "protect-mcp-record-" + Date.now() + ".html");
10826
- writeFileSync4(out, html);
10965
+ const out = join9(osMod.tmpdir(), "protect-mcp-record-" + Date.now() + ".html");
10966
+ writeFileSync5(out, html);
10827
10967
  openTarget(out);
10828
10968
  process.stdout.write(`
10829
10969
  ${bold("\u{1F6E1}\uFE0F Your record")} ${dim("\xB7")} ${recs.length} decision${recs.length === 1 ? "" : "s"}, all on this machine
@@ -10996,8 +11136,8 @@ render();kickVerify();
10996
11136
  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();kickVerify()}}).catch(function(){})};poll();setInterval(poll,2000);}
10997
11137
  </script></body></html>`;
10998
11138
  async function handleClaim(argv) {
10999
- const { readFileSync: readFileSync11, existsSync: existsSync9, writeFileSync: writeFileSync4 } = await import("fs");
11000
- const { join: join8 } = await import("path");
11139
+ const { readFileSync: readFileSync11, existsSync: existsSync10, writeFileSync: writeFileSync5 } = await import("fs");
11140
+ const { join: join9 } = await import("path");
11001
11141
  const { buildClaim: buildClaim2 } = await Promise.resolve().then(() => (init_claim(), claim_exports));
11002
11142
  let dir = process.cwd();
11003
11143
  const di = argv.indexOf("--dir");
@@ -11030,8 +11170,8 @@ Example: ${bold("npx protect-mcp claim --no net.egress --anchor")}
11030
11170
  process.exit(0);
11031
11171
  return;
11032
11172
  }
11033
- const keyPath = join8(dir, "keys", "gateway.json");
11034
- if (!existsSync9(keyPath)) {
11173
+ const keyPath = join9(dir, "keys", "gateway.json");
11174
+ if (!existsSync10(keyPath)) {
11035
11175
  process.stderr.write(`
11036
11176
  ${bold("protect-mcp claim")}
11037
11177
 
@@ -11060,8 +11200,8 @@ protect-mcp claim: ${keyPath} is missing privateKey/publicKey.
11060
11200
  process.exit(1);
11061
11201
  return;
11062
11202
  }
11063
- const recPath = join8(dir, ".protect-mcp-receipts.jsonl");
11064
- if (!existsSync9(recPath)) {
11203
+ const recPath = join9(dir, ".protect-mcp-receipts.jsonl");
11204
+ if (!existsSync10(recPath)) {
11065
11205
  process.stderr.write(`
11066
11206
  ${bold("protect-mcp claim")}
11067
11207
 
@@ -11088,8 +11228,8 @@ protect-mcp claim: no readable receipts in ${recPath}.
11088
11228
  }
11089
11229
  const pack = buildClaim2(receipts, predicate, { privateKey: key.privateKey, publicKey: key.publicKey, kid: key.kid || "gateway", issuer: "protect-mcp" }, (/* @__PURE__ */ new Date()).toISOString());
11090
11230
  const oi = argv.indexOf("--output");
11091
- const out = oi !== -1 && argv[oi + 1] ? argv[oi + 1] : join8(dir, "claim-" + Date.now() + ".json");
11092
- writeFileSync4(out, JSON.stringify(pack, null, 2) + "\n");
11231
+ const out = oi !== -1 && argv[oi + 1] ? argv[oi + 1] : join9(dir, "claim-" + Date.now() + ".json");
11232
+ writeFileSync5(out, JSON.stringify(pack, null, 2) + "\n");
11093
11233
  process.stdout.write(`
11094
11234
  ${bold("\u{1F6E1}\uFE0F Signed claim")}
11095
11235
  `);
@@ -11115,7 +11255,7 @@ ${bold("\u{1F6E1}\uFE0F Signed claim")}
11115
11255
  );
11116
11256
  if (res.ok) {
11117
11257
  const sidecar = out.replace(/\.json$/, "") + ".anchor.json";
11118
- 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");
11258
+ writeFileSync5(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");
11119
11259
  process.stdout.write(` ${green("Anchored")} as log entry ${bold("#" + res.seq)}${res.already_anchored ? dim(" (already present)") : ""} ${dim(res.entry_url || "")}
11120
11260
  `);
11121
11261
  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.")}
@@ -11144,16 +11284,16 @@ ${bold("\u{1F6E1}\uFE0F Signed claim")}
11144
11284
  process.exit(0);
11145
11285
  }
11146
11286
  async function handleAnchorRecord(argv) {
11147
- const { readFileSync: readFileSync11, existsSync: existsSync9, appendFileSync: appendFileSync3 } = await import("fs");
11148
- const { join: join8 } = await import("path");
11287
+ const { readFileSync: readFileSync11, existsSync: existsSync10, appendFileSync: appendFileSync3 } = await import("fs");
11288
+ const { join: join9 } = await import("path");
11149
11289
  const { anchorRecordCheckpoint: anchorRecordCheckpoint2, buildRecordCheckpoint: buildRecordCheckpoint2, lookupPinnedIdentity: lookupPinnedIdentity2 } = await Promise.resolve().then(() => (init_claim(), claim_exports));
11150
11290
  let dir = process.cwd();
11151
11291
  const di = argv.indexOf("--dir");
11152
11292
  if (di !== -1 && argv[di + 1]) dir = argv[di + 1];
11153
11293
  const li = argv.indexOf("--log");
11154
11294
  const logBase = li !== -1 && argv[li + 1] ? argv[li + 1] : void 0;
11155
- const keyPath = join8(dir, "keys", "gateway.json");
11156
- if (!existsSync9(keyPath)) {
11295
+ const keyPath = join9(dir, "keys", "gateway.json");
11296
+ if (!existsSync10(keyPath)) {
11157
11297
  process.stderr.write(`
11158
11298
  ${bold("protect-mcp anchor-record")}
11159
11299
 
@@ -11182,8 +11322,8 @@ protect-mcp anchor-record: ${keyPath} is missing privateKey/publicKey.
11182
11322
  process.exit(1);
11183
11323
  return;
11184
11324
  }
11185
- const recPath = join8(dir, ".protect-mcp-receipts.jsonl");
11186
- if (!existsSync9(recPath)) {
11325
+ const recPath = join9(dir, ".protect-mcp-receipts.jsonl");
11326
+ if (!existsSync10(recPath)) {
11187
11327
  process.stderr.write(`
11188
11328
  ${bold("protect-mcp anchor-record")}
11189
11329
 
@@ -11209,9 +11349,9 @@ protect-mcp anchor-record: no readable receipts in ${recPath}.
11209
11349
  return;
11210
11350
  }
11211
11351
  const claimKey = { privateKey: key.privateKey, publicKey: key.publicKey, kid: key.kid || "gateway", issuer: "protect-mcp" };
11212
- const historyPath = join8(dir, ".protect-mcp-anchors.jsonl");
11352
+ const historyPath = join9(dir, ".protect-mcp-anchors.jsonl");
11213
11353
  const preview = buildRecordCheckpoint2(receipts, claimKey, "preview");
11214
- if (!argv.includes("--force") && existsSync9(historyPath)) {
11354
+ if (!argv.includes("--force") && existsSync10(historyPath)) {
11215
11355
  const lines = readFileSync11(historyPath, "utf-8").split(/\r?\n/).filter(Boolean);
11216
11356
  const last = lines.length ? (() => {
11217
11357
  try {
@@ -11270,10 +11410,10 @@ ${bold("\u{1F6E1}\uFE0F Record checkpoint")}
11270
11410
  process.exit(0);
11271
11411
  }
11272
11412
  async function handleVerifyClaim(argv) {
11273
- const { readFileSync: readFileSync11, existsSync: existsSync9 } = await import("fs");
11413
+ const { readFileSync: readFileSync11, existsSync: existsSync10 } = await import("fs");
11274
11414
  const { verifyClaim: verifyClaim2 } = await Promise.resolve().then(() => (init_claim(), claim_exports));
11275
11415
  const file = argv.find((a) => !a.startsWith("--"));
11276
- if (!file || !existsSync9(file)) {
11416
+ if (!file || !existsSync10(file)) {
11277
11417
  process.stderr.write(`
11278
11418
  ${bold("protect-mcp verify-claim")} <claim.json> [--key <public-hex>]
11279
11419
 
@@ -11322,7 +11462,7 @@ ${bold("protect-mcp verify-claim")}
11322
11462
  const sidecarPath = ai !== -1 && argv[ai + 1] ? argv[ai + 1] : file.replace(/\.json$/, "") + ".anchor.json";
11323
11463
  const requireAnchor = argv.includes("--check-anchor");
11324
11464
  let anchorOk = true;
11325
- if (existsSync9(sidecarPath)) {
11465
+ if (existsSync10(sidecarPath)) {
11326
11466
  const { checkClaimAnchor: checkClaimAnchor2 } = await Promise.resolve().then(() => (init_claim(), claim_exports));
11327
11467
  let sidecar = null;
11328
11468
  try {
@@ -11393,24 +11533,24 @@ ${bold("protect-mcp verify-claim")}
11393
11533
  process.exit(finalValid ? 0 : 1);
11394
11534
  }
11395
11535
  async function handleBundle(argv) {
11396
- const { readFileSync: readFileSync11, writeFileSync: writeFileSync4, existsSync: existsSync9 } = await import("fs");
11397
- const { join: join8 } = await import("path");
11536
+ const { readFileSync: readFileSync11, writeFileSync: writeFileSync5, existsSync: existsSync10 } = await import("fs");
11537
+ const { join: join9 } = await import("path");
11398
11538
  const { createAuditBundle: createAuditBundle2 } = await Promise.resolve().then(() => (init_bundle(), bundle_exports));
11399
11539
  let dir = process.cwd();
11400
11540
  const dirIdx = argv.indexOf("--dir");
11401
11541
  if (dirIdx !== -1 && argv[dirIdx + 1]) dir = argv[dirIdx + 1];
11402
11542
  const outputIdx = argv.indexOf("--output");
11403
- const outputPath = outputIdx !== -1 && argv[outputIdx + 1] ? argv[outputIdx + 1] : join8(dir, "audit-bundle.json");
11404
- const receiptsPath = join8(dir, ".protect-mcp-receipts.jsonl");
11405
- const keyPath = join8(dir, "keys", "gateway.json");
11406
- if (!existsSync9(receiptsPath)) {
11543
+ const outputPath = outputIdx !== -1 && argv[outputIdx + 1] ? argv[outputIdx + 1] : join9(dir, "audit-bundle.json");
11544
+ const receiptsPath = join9(dir, ".protect-mcp-receipts.jsonl");
11545
+ const keyPath = join9(dir, "keys", "gateway.json");
11546
+ if (!existsSync10(receiptsPath)) {
11407
11547
  process.stderr.write(`${bold("protect-mcp bundle")}
11408
11548
 
11409
11549
  No signed receipt file found. Run protect-mcp with signing enabled first.
11410
11550
  `);
11411
11551
  process.exit(0);
11412
11552
  }
11413
- if (!existsSync9(keyPath)) {
11553
+ if (!existsSync10(keyPath)) {
11414
11554
  process.stderr.write(`${bold("protect-mcp bundle")}
11415
11555
 
11416
11556
  No key file found at ${keyPath}
@@ -11431,7 +11571,7 @@ No key file found at ${keyPath}
11431
11571
  use: "sig"
11432
11572
  }]
11433
11573
  });
11434
- writeFileSync4(outputPath, JSON.stringify(bundle, null, 2) + "\n");
11574
+ writeFileSync5(outputPath, JSON.stringify(bundle, null, 2) + "\n");
11435
11575
  process.stdout.write(`
11436
11576
  ${bold("protect-mcp bundle")}
11437
11577
 
@@ -11447,8 +11587,8 @@ ${bold("protect-mcp bundle")}
11447
11587
  `);
11448
11588
  }
11449
11589
  async function createSandbox() {
11450
- const { mkdirSync: mkdirSync3, writeFileSync: writeFileSync4, existsSync: existsSync9, readFileSync: readFileSync11 } = await import("fs");
11451
- const { join: join8 } = await import("path");
11590
+ const { mkdirSync: mkdirSync4, writeFileSync: writeFileSync5, existsSync: existsSync10, readFileSync: readFileSync11 } = await import("fs");
11591
+ const { join: join9 } = await import("path");
11452
11592
  const { homedir } = await import("os");
11453
11593
  let response;
11454
11594
  try {
@@ -11478,19 +11618,19 @@ async function createSandbox() {
11478
11618
  return null;
11479
11619
  }
11480
11620
  const dashboardUrl = `https://scopeblind.com/t/${data.slug}`;
11481
- const configDir = join8(homedir(), ".protect-mcp");
11482
- if (!existsSync9(configDir)) {
11483
- mkdirSync3(configDir, { recursive: true });
11621
+ const configDir = join9(homedir(), ".protect-mcp");
11622
+ if (!existsSync10(configDir)) {
11623
+ mkdirSync4(configDir, { recursive: true });
11484
11624
  }
11485
- const configPath = join8(configDir, "config.json");
11625
+ const configPath = join9(configDir, "config.json");
11486
11626
  let existing = {};
11487
- if (existsSync9(configPath)) {
11627
+ if (existsSync10(configPath)) {
11488
11628
  try {
11489
11629
  existing = JSON.parse(readFileSync11(configPath, "utf-8"));
11490
11630
  } catch {
11491
11631
  }
11492
11632
  }
11493
- writeFileSync4(configPath, JSON.stringify({
11633
+ writeFileSync5(configPath, JSON.stringify({
11494
11634
  ...existing,
11495
11635
  sandbox_slug: data.slug,
11496
11636
  dashboard_url: dashboardUrl
@@ -11523,10 +11663,10 @@ ${"\u2500".repeat(50)}
11523
11663
  }
11524
11664
  async function handleQuickstart(argv) {
11525
11665
  const connectFlag = argv.includes("--connect");
11526
- const { mkdtempSync, writeFileSync: writeFileSync4, existsSync: existsSync9, mkdirSync: mkdirSync3, readFileSync: readFileSync11 } = await import("fs");
11527
- const { join: join8 } = await import("path");
11666
+ const { mkdtempSync, writeFileSync: writeFileSync5, existsSync: existsSync10, mkdirSync: mkdirSync4, readFileSync: readFileSync11 } = await import("fs");
11667
+ const { join: join9 } = await import("path");
11528
11668
  const { tmpdir } = await import("os");
11529
- const dir = mkdtempSync(join8(tmpdir(), "protect-mcp-quickstart-"));
11669
+ const dir = mkdtempSync(join9(tmpdir(), "protect-mcp-quickstart-"));
11530
11670
  process.stdout.write(`
11531
11671
  ${bold("protect-mcp quickstart")}
11532
11672
  `);
@@ -11551,8 +11691,8 @@ ${bold("protect-mcp quickstart")}
11551
11691
  Working dir: ${dir}
11552
11692
 
11553
11693
  `);
11554
- const keysDir = join8(dir, "keys");
11555
- mkdirSync3(keysDir, { recursive: true });
11694
+ const keysDir = join9(dir, "keys");
11695
+ mkdirSync4(keysDir, { recursive: true });
11556
11696
  const { randomBytes: randomBytes4 } = await import("crypto");
11557
11697
  let keypair;
11558
11698
  try {
@@ -11572,13 +11712,13 @@ ${bold("protect-mcp quickstart")}
11572
11712
  kid: `quickstart-${Date.now()}`
11573
11713
  };
11574
11714
  }
11575
- writeFileSync4(join8(keysDir, "gateway.json"), JSON.stringify({
11715
+ writeFileSync5(join9(keysDir, "gateway.json"), JSON.stringify({
11576
11716
  privateKey: keypair.privateKey,
11577
11717
  publicKey: keypair.publicKey,
11578
11718
  kid: keypair.kid,
11579
11719
  generated_at: (/* @__PURE__ */ new Date()).toISOString()
11580
11720
  }, null, 2) + "\n");
11581
- const configPath = join8(dir, "protect-mcp.json");
11721
+ const configPath = join9(dir, "protect-mcp.json");
11582
11722
  const config = {
11583
11723
  tools: {
11584
11724
  "*": { rate_limit: "100/hour" },
@@ -11586,12 +11726,12 @@ ${bold("protect-mcp quickstart")}
11586
11726
  },
11587
11727
  default_tier: "unknown",
11588
11728
  signing: {
11589
- key_path: join8(keysDir, "gateway.json"),
11729
+ key_path: join9(keysDir, "gateway.json"),
11590
11730
  issuer: "protect-mcp-quickstart",
11591
11731
  enabled: true
11592
11732
  }
11593
11733
  };
11594
- writeFileSync4(configPath, JSON.stringify(config, null, 2) + "\n");
11734
+ writeFileSync5(configPath, JSON.stringify(config, null, 2) + "\n");
11595
11735
  process.stdout.write(` \u2713 Keypair generated (kid: ${keypair.kid})
11596
11736
  `);
11597
11737
  process.stdout.write(` \u2713 Policy created (shadow mode, all tools logged)
@@ -11606,7 +11746,7 @@ ${bold("protect-mcp quickstart")}
11606
11746
  const dashboardUrl = await createSandbox();
11607
11747
  if (dashboardUrl) {
11608
11748
  const updatedConfig = { ...config, dashboard_url: dashboardUrl };
11609
- writeFileSync4(configPath, JSON.stringify(updatedConfig, null, 2) + "\n");
11749
+ writeFileSync5(configPath, JSON.stringify(updatedConfig, null, 2) + "\n");
11610
11750
  process.stdout.write(green(` \u2713 Dashboard created: ${dashboardUrl}
11611
11751
  `));
11612
11752
  process.stdout.write(` Receipts will be uploaded automatically.
@@ -11642,7 +11782,7 @@ ${bold("protect-mcp quickstart")}
11642
11782
  }
11643
11783
  async function handleRegistry(argv) {
11644
11784
  const subcommand = argv[0] || "status";
11645
- const dir = (0, import_node_path8.resolve)(flagValue(argv, "--dir") || process.cwd());
11785
+ const dir = (0, import_node_path9.resolve)(flagValue(argv, "--dir") || process.cwd());
11646
11786
  const orgName = flagValue(argv, "--org") || process.env.SCOPEBLIND_ORG;
11647
11787
  const orgId = flagValue(argv, "--org-id") || process.env.SCOPEBLIND_ORG_ID;
11648
11788
  const billingAccountId = flagValue(argv, "--billing-account") || process.env.SCOPEBLIND_BILLING_ACCOUNT;
@@ -11724,14 +11864,14 @@ ${bold("protect-mcp registry anchor")}
11724
11864
  return;
11725
11865
  }
11726
11866
  if (subcommand === "status") {
11727
- const registryPath = (0, import_node_path8.join)(dir, registryMod.REGISTRY_FILE);
11728
- const identityPath = (0, import_node_path8.join)(dir, registryMod.ORG_IDENTITY_FILE);
11867
+ const registryPath = (0, import_node_path9.join)(dir, registryMod.REGISTRY_FILE);
11868
+ const identityPath = (0, import_node_path9.join)(dir, registryMod.ORG_IDENTITY_FILE);
11729
11869
  process.stdout.write(`
11730
11870
  ${bold("protect-mcp registry status")}
11731
11871
 
11732
11872
  `);
11733
- if ((0, import_node_fs12.existsSync)(identityPath)) {
11734
- const identity = JSON.parse((0, import_node_fs12.readFileSync)(identityPath, "utf-8"));
11873
+ if ((0, import_node_fs13.existsSync)(identityPath)) {
11874
+ const identity = JSON.parse((0, import_node_fs13.readFileSync)(identityPath, "utf-8"));
11735
11875
  process.stdout.write(` Org: ${identity.org_name || "unknown"}
11736
11876
  `);
11737
11877
  process.stdout.write(` Org ID: ${identity.org_id || "unknown"}
@@ -11742,8 +11882,8 @@ ${bold("protect-mcp registry status")}
11742
11882
  process.stdout.write(` Org identity: ${yellow("missing")} (${identityPath})
11743
11883
  `);
11744
11884
  }
11745
- if ((0, import_node_fs12.existsSync)(registryPath)) {
11746
- const registry = JSON.parse((0, import_node_fs12.readFileSync)(registryPath, "utf-8"));
11885
+ if ((0, import_node_fs13.existsSync)(registryPath)) {
11886
+ const registry = JSON.parse((0, import_node_fs13.readFileSync)(registryPath, "utf-8"));
11747
11887
  const hosted = Array.isArray(registry.anchors) && registry.anchors.some((a) => a.timestamp_source === "scopeblind-hosted");
11748
11888
  process.stdout.write(` Registry: ${registryPath}
11749
11889
  `);
@@ -11753,7 +11893,7 @@ ${bold("protect-mcp registry status")}
11753
11893
  `);
11754
11894
  process.stdout.write(` Boundary: ${hosted ? green("hosted digest anchor") : yellow("local preview only")}
11755
11895
  `);
11756
- process.stdout.write(` Verifier page: ${(0, import_node_path8.join)(dir, registryMod.VERIFIER_PAGE_FILE)}
11896
+ process.stdout.write(` Verifier page: ${(0, import_node_path9.join)(dir, registryMod.VERIFIER_PAGE_FILE)}
11757
11897
  `);
11758
11898
  } else {
11759
11899
  process.stdout.write(` Registry: ${yellow("missing")} (${registryPath})
@@ -11781,10 +11921,10 @@ async function handleKillerDemo(argv) {
11781
11921
  verifySelectiveDisclosurePackage: verifySelectiveDisclosurePackage2
11782
11922
  } = await Promise.resolve().then(() => (init_signing_committed(), signing_committed_exports));
11783
11923
  const registryMod = await Promise.resolve().then(() => (init_receipt_registry(), receipt_registry_exports));
11784
- const dir = (0, import_node_path8.resolve)(flagValue(argv, "--dir") || mkdtempSync((0, import_node_path8.join)(tmpdir(), "scopeblind-killer-demo-")));
11785
- (0, import_node_fs12.mkdirSync)(dir, { recursive: true });
11786
- (0, import_node_fs12.mkdirSync)((0, import_node_path8.join)(dir, "keys"), { recursive: true });
11787
- (0, import_node_fs12.mkdirSync)((0, import_node_path8.join)(dir, "receipts"), { recursive: true });
11924
+ const dir = (0, import_node_path9.resolve)(flagValue(argv, "--dir") || mkdtempSync((0, import_node_path9.join)(tmpdir(), "scopeblind-killer-demo-")));
11925
+ (0, import_node_fs13.mkdirSync)(dir, { recursive: true });
11926
+ (0, import_node_fs13.mkdirSync)((0, import_node_path9.join)(dir, "keys"), { recursive: true });
11927
+ (0, import_node_fs13.mkdirSync)((0, import_node_path9.join)(dir, "receipts"), { recursive: true });
11788
11928
  const privateKeyBytes = randomBytes4(32);
11789
11929
  const publicKeyBytes = ed255192.getPublicKey(privateKeyBytes);
11790
11930
  const keypair = {
@@ -11793,14 +11933,14 @@ async function handleKillerDemo(argv) {
11793
11933
  kid: `killer-demo-${Date.now()}`,
11794
11934
  issuer: "scopeblind-killer-demo"
11795
11935
  };
11796
- const keyPath = (0, import_node_path8.join)(dir, "keys", "gateway.json");
11797
- (0, import_node_fs12.writeFileSync)(keyPath, JSON.stringify({
11936
+ const keyPath = (0, import_node_path9.join)(dir, "keys", "gateway.json");
11937
+ (0, import_node_fs13.writeFileSync)(keyPath, JSON.stringify({
11798
11938
  ...keypair,
11799
11939
  generated_at: (/* @__PURE__ */ new Date()).toISOString(),
11800
11940
  warning: "Demo key only. Do not use for production."
11801
11941
  }, null, 2) + "\n");
11802
- const shadowConfigPath = (0, import_node_path8.join)(dir, "protect-mcp.shadow.json");
11803
- const policyPackPath = (0, import_node_path8.join)(dir, "protect-mcp.policy-pack.json");
11942
+ const shadowConfigPath = (0, import_node_path9.join)(dir, "protect-mcp.shadow.json");
11943
+ const policyPackPath = (0, import_node_path9.join)(dir, "protect-mcp.policy-pack.json");
11804
11944
  const config = {
11805
11945
  tools: { "*": { rate_limit: "100/hour" } },
11806
11946
  default_tier: "signed-known",
@@ -11819,11 +11959,11 @@ async function handleKillerDemo(argv) {
11819
11959
  signing: { key_path: keyPath, issuer: keypair.issuer, enabled: true },
11820
11960
  notes: ["Demo policy pack: approvals for GitHub, email, and PMS booking; destructive tools blocked."]
11821
11961
  };
11822
- (0, import_node_fs12.writeFileSync)(shadowConfigPath, JSON.stringify(config, null, 2) + "\n");
11823
- (0, import_node_fs12.writeFileSync)(policyPackPath, JSON.stringify(policyPack, null, 2) + "\n");
11962
+ (0, import_node_fs13.writeFileSync)(shadowConfigPath, JSON.stringify(config, null, 2) + "\n");
11963
+ (0, import_node_fs13.writeFileSync)(policyPackPath, JSON.stringify(policyPack, null, 2) + "\n");
11824
11964
  await initSigning({ enabled: true, key_path: keyPath, issuer: keypair.issuer });
11825
- const logPath = (0, import_node_path8.join)(dir, ".protect-mcp-log.jsonl");
11826
- const receiptPath = (0, import_node_path8.join)(dir, ".protect-mcp-receipts.jsonl");
11965
+ const logPath = (0, import_node_path9.join)(dir, ".protect-mcp-log.jsonl");
11966
+ const receiptPath = (0, import_node_path9.join)(dir, ".protect-mcp-receipts.jsonl");
11827
11967
  const shadowCalls = [
11828
11968
  { tool: "read_file", input: { path: "/research/macro-notes.md" }, reason: "observe_mode" },
11829
11969
  { tool: "github_create_pr", input: { repo: "scopeblind/legate", branch: "agent/pms-adapter", title: "Wire mock PMS adapter" }, reason: "observe_mode" },
@@ -11832,7 +11972,7 @@ async function handleKillerDemo(argv) {
11832
11972
  ];
11833
11973
  for (const [idx, call] of shadowCalls.entries()) {
11834
11974
  const requestId2 = `demo-shadow-${idx + 1}`;
11835
- (0, import_node_fs12.appendFileSync)(logPath, JSON.stringify({
11975
+ (0, import_node_fs13.appendFileSync)(logPath, JSON.stringify({
11836
11976
  v: 2,
11837
11977
  tool: call.tool,
11838
11978
  decision: "allow",
@@ -11867,8 +12007,8 @@ async function handleKillerDemo(argv) {
11867
12007
  policy_digest: (0, import_node_crypto7.createHash)("sha256").update(JSON.stringify(policyPack)).digest("hex").slice(0, 16),
11868
12008
  action_readback: readback
11869
12009
  };
11870
- (0, import_node_fs12.appendFileSync)(logPath, JSON.stringify(requireApprovalEntry) + "\n");
11871
- (0, import_node_fs12.appendFileSync)((0, import_node_path8.join)(dir, ".protect-mcp-approval-resolutions.jsonl"), JSON.stringify({
12010
+ (0, import_node_fs13.appendFileSync)(logPath, JSON.stringify(requireApprovalEntry) + "\n");
12011
+ (0, import_node_fs13.appendFileSync)((0, import_node_path9.join)(dir, ".protect-mcp-approval-resolutions.jsonl"), JSON.stringify({
11872
12012
  type: "scopeblind.approval_resolution.v1",
11873
12013
  at: (/* @__PURE__ */ new Date()).toISOString(),
11874
12014
  request_id: requestId,
@@ -11888,11 +12028,11 @@ async function handleKillerDemo(argv) {
11888
12028
  truncated: false
11889
12029
  }
11890
12030
  };
11891
- (0, import_node_fs12.appendFileSync)(logPath, JSON.stringify(executedEntry) + "\n");
12031
+ (0, import_node_fs13.appendFileSync)(logPath, JSON.stringify(executedEntry) + "\n");
11892
12032
  const signed = signDecision(executedEntry);
11893
12033
  if (!signed.signed) throw new Error(`demo signing failed: ${signed.warning || signed.error || "unknown"}`);
11894
- (0, import_node_fs12.appendFileSync)(receiptPath, signed.signed + "\n");
11895
- (0, import_node_fs12.writeFileSync)((0, import_node_path8.join)(dir, "receipts", "approved-pms-booking.receipt.json"), JSON.stringify(JSON.parse(signed.signed), null, 2) + "\n");
12034
+ (0, import_node_fs13.appendFileSync)(receiptPath, signed.signed + "\n");
12035
+ (0, import_node_fs13.writeFileSync)((0, import_node_path9.join)(dir, "receipts", "approved-pms-booking.receipt.json"), JSON.stringify(JSON.parse(signed.signed), null, 2) + "\n");
11896
12036
  const receiptArtifact = JSON.parse(signed.signed);
11897
12037
  const tamperedArtifact = JSON.parse(signed.signed);
11898
12038
  if (tamperedArtifact.payload && typeof tamperedArtifact.payload === "object") {
@@ -11903,7 +12043,7 @@ async function handleKillerDemo(argv) {
11903
12043
  }
11904
12044
  const validOriginal = artifacts.verifyArtifact(receiptArtifact, keypair.publicKey);
11905
12045
  const validTampered = artifacts.verifyArtifact(tamperedArtifact, keypair.publicKey);
11906
- (0, import_node_fs12.writeFileSync)((0, import_node_path8.join)(dir, "receipts", "tampered.receipt.json"), JSON.stringify(tamperedArtifact, null, 2) + "\n");
12046
+ (0, import_node_fs13.writeFileSync)((0, import_node_path9.join)(dir, "receipts", "tampered.receipt.json"), JSON.stringify(tamperedArtifact, null, 2) + "\n");
11907
12047
  const committed = signCommittedDecision2(
11908
12048
  executedEntry,
11909
12049
  ["tool", "payload_digest", "swarm"],
@@ -11915,11 +12055,11 @@ async function handleKillerDemo(argv) {
11915
12055
  const committedReceipt = JSON.parse(committed.signed);
11916
12056
  const disclosurePackage = createSelectiveDisclosurePackage2(committedReceipt, ["tool"], committed.openings);
11917
12057
  const disclosureVerification = verifySelectiveDisclosurePackage2(committedReceipt, disclosurePackage);
11918
- (0, import_node_fs12.appendFileSync)(receiptPath, committed.signed + "\n");
11919
- (0, import_node_fs12.writeFileSync)((0, import_node_path8.join)(dir, "receipts", "selective-disclosure.receipt.json"), JSON.stringify(committedReceipt, null, 2) + "\n");
11920
- (0, import_node_fs12.writeFileSync)((0, import_node_path8.join)(dir, "receipts", "selective-disclosure.package.json"), JSON.stringify(disclosurePackage, null, 2) + "\n");
11921
- (0, import_node_fs12.writeFileSync)((0, import_node_path8.join)(dir, "receipts", "selective-disclosure.tool-only.json"), JSON.stringify(disclosurePackage, null, 2) + "\n");
11922
- (0, import_node_fs12.writeFileSync)((0, import_node_path8.join)(dir, "verification-results.json"), JSON.stringify({
12058
+ (0, import_node_fs13.appendFileSync)(receiptPath, committed.signed + "\n");
12059
+ (0, import_node_fs13.writeFileSync)((0, import_node_path9.join)(dir, "receipts", "selective-disclosure.receipt.json"), JSON.stringify(committedReceipt, null, 2) + "\n");
12060
+ (0, import_node_fs13.writeFileSync)((0, import_node_path9.join)(dir, "receipts", "selective-disclosure.package.json"), JSON.stringify(disclosurePackage, null, 2) + "\n");
12061
+ (0, import_node_fs13.writeFileSync)((0, import_node_path9.join)(dir, "receipts", "selective-disclosure.tool-only.json"), JSON.stringify(disclosurePackage, null, 2) + "\n");
12062
+ (0, import_node_fs13.writeFileSync)((0, import_node_path9.join)(dir, "verification-results.json"), JSON.stringify({
11923
12063
  original_receipt_valid: validOriginal,
11924
12064
  tampered_receipt_valid: validTampered,
11925
12065
  selective_disclosure_valid: disclosureVerification.valid,
@@ -12002,19 +12142,19 @@ async function handleKillerDemo(argv) {
12002
12142
  "No raw prompt, payload, output, private key, or raw receipt is uploaded by the registry flow. Hosted mode submits receipt digests, request ids, org public keys, and billing account metadata only.",
12003
12143
  ""
12004
12144
  ].join("\n");
12005
- (0, import_node_fs12.writeFileSync)((0, import_node_path8.join)(dir, "DEMO-RUNBOOK.md"), runbook);
12006
- (0, import_node_fs12.writeFileSync)((0, import_node_path8.join)(dir, "demo-summary.json"), JSON.stringify({
12145
+ (0, import_node_fs13.writeFileSync)((0, import_node_path9.join)(dir, "DEMO-RUNBOOK.md"), runbook);
12146
+ (0, import_node_fs13.writeFileSync)((0, import_node_path9.join)(dir, "demo-summary.json"), JSON.stringify({
12007
12147
  dir,
12008
12148
  dashboard_command: `npx protect-mcp dashboard --dir ${dir} --policy ${policyPackPath} --open`,
12009
12149
  policy_pack: policyPackPath,
12010
- receipt: (0, import_node_path8.join)(dir, "receipts", "approved-pms-booking.receipt.json"),
12011
- tampered_receipt: (0, import_node_path8.join)(dir, "receipts", "tampered.receipt.json"),
12012
- selective_disclosure_receipt: (0, import_node_path8.join)(dir, "receipts", "selective-disclosure.receipt.json"),
12013
- selective_disclosure_package: (0, import_node_path8.join)(dir, "receipts", "selective-disclosure.tool-only.json"),
12014
- verification_results: (0, import_node_path8.join)(dir, "verification-results.json"),
12150
+ receipt: (0, import_node_path9.join)(dir, "receipts", "approved-pms-booking.receipt.json"),
12151
+ tampered_receipt: (0, import_node_path9.join)(dir, "receipts", "tampered.receipt.json"),
12152
+ selective_disclosure_receipt: (0, import_node_path9.join)(dir, "receipts", "selective-disclosure.receipt.json"),
12153
+ selective_disclosure_package: (0, import_node_path9.join)(dir, "receipts", "selective-disclosure.tool-only.json"),
12154
+ verification_results: (0, import_node_path9.join)(dir, "verification-results.json"),
12015
12155
  registry: registry.registryPath,
12016
12156
  verifier_page: registry.verifierPath,
12017
- runbook: (0, import_node_path8.join)(dir, "DEMO-RUNBOOK.md"),
12157
+ runbook: (0, import_node_path9.join)(dir, "DEMO-RUNBOOK.md"),
12018
12158
  original_valid: validOriginal.valid,
12019
12159
  tampered_valid: validTampered.valid,
12020
12160
  selective_disclosure_valid: disclosureVerification.valid
@@ -12027,9 +12167,9 @@ ${bold("protect-mcp killer-demo")}
12027
12167
  `);
12028
12168
  process.stdout.write(` Dashboard: ${dim(`npx protect-mcp dashboard --dir ${dir} --policy ${policyPackPath} --open`)}
12029
12169
  `);
12030
- process.stdout.write(` Runbook: ${(0, import_node_path8.join)(dir, "DEMO-RUNBOOK.md")}
12170
+ process.stdout.write(` Runbook: ${(0, import_node_path9.join)(dir, "DEMO-RUNBOOK.md")}
12031
12171
  `);
12032
- process.stdout.write(` Signed receipt: ${(0, import_node_path8.join)(dir, "receipts", "approved-pms-booking.receipt.json")}
12172
+ process.stdout.write(` Signed receipt: ${(0, import_node_path9.join)(dir, "receipts", "approved-pms-booking.receipt.json")}
12033
12173
  `);
12034
12174
  process.stdout.write(` Tamper check: original=${validOriginal.valid ? green("valid") : red("invalid")} tampered=${validTampered.valid ? red("valid") : green("invalid")}
12035
12175
  `);
@@ -12049,8 +12189,8 @@ async function handleVerifyDisclosure(argv) {
12049
12189
  process.exit(1);
12050
12190
  }
12051
12191
  const { verifySelectiveDisclosurePackage: verifySelectiveDisclosurePackage2 } = await Promise.resolve().then(() => (init_signing_committed(), signing_committed_exports));
12052
- const receipt = JSON.parse((0, import_node_fs12.readFileSync)((0, import_node_path8.resolve)(receiptPath), "utf-8"));
12053
- const disclosure = JSON.parse((0, import_node_fs12.readFileSync)((0, import_node_path8.resolve)(disclosurePath), "utf-8"));
12192
+ const receipt = JSON.parse((0, import_node_fs13.readFileSync)((0, import_node_path9.resolve)(receiptPath), "utf-8"));
12193
+ const disclosure = JSON.parse((0, import_node_fs13.readFileSync)((0, import_node_path9.resolve)(disclosurePath), "utf-8"));
12054
12194
  const result = verifySelectiveDisclosurePackage2(receipt, disclosure);
12055
12195
  process.stdout.write(`
12056
12196
  ${bold("protect-mcp verify-disclosure")}
@@ -12086,7 +12226,7 @@ ${red("Errors:")}
12086
12226
  async function handlePolicyPacks(argv) {
12087
12227
  const subcommand = argv[0] || "list";
12088
12228
  const packArg = argv[1];
12089
- const dir = (0, import_node_path8.resolve)(flagValue(argv, "--dir") || "./cedar");
12229
+ const dir = (0, import_node_path9.resolve)(flagValue(argv, "--dir") || "./cedar");
12090
12230
  const force = argv.includes("--force");
12091
12231
  if (subcommand === "list") {
12092
12232
  process.stdout.write(`
@@ -12141,17 +12281,17 @@ ${bold(pack.name)} (${pack.id})
12141
12281
  `);
12142
12282
  process.exit(1);
12143
12283
  }
12144
- (0, import_node_fs12.mkdirSync)(dir, { recursive: true });
12284
+ (0, import_node_fs13.mkdirSync)(dir, { recursive: true });
12145
12285
  const written = [];
12146
12286
  for (const pack of packs) {
12147
12287
  for (const file of pack.files) {
12148
- const outPath = (0, import_node_path8.join)(dir, file.path);
12149
- if ((0, import_node_fs12.existsSync)(outPath) && !force) {
12288
+ const outPath = (0, import_node_path9.join)(dir, file.path);
12289
+ if ((0, import_node_fs13.existsSync)(outPath) && !force) {
12150
12290
  process.stderr.write(`Refusing to overwrite ${outPath}. Re-run with --force if intentional.
12151
12291
  `);
12152
12292
  process.exit(1);
12153
12293
  }
12154
- (0, import_node_fs12.writeFileSync)(outPath, file.contents.endsWith("\n") ? file.contents : `${file.contents}
12294
+ (0, import_node_fs13.writeFileSync)(outPath, file.contents.endsWith("\n") ? file.contents : `${file.contents}
12155
12295
  `);
12156
12296
  written.push(outPath);
12157
12297
  }
@@ -12176,7 +12316,7 @@ Next: ${dim(`protect-mcp serve --cedar ${dir}`)} for shadow mode, then add ${dim
12176
12316
  async function handleConnectors(argv) {
12177
12317
  const subcommand = argv[0] || "list";
12178
12318
  const pilotArg = argv[1];
12179
- const dir = (0, import_node_path8.resolve)(flagValue(argv, "--dir") || process.cwd());
12319
+ const dir = (0, import_node_path9.resolve)(flagValue(argv, "--dir") || process.cwd());
12180
12320
  const force = argv.includes("--force");
12181
12321
  if (subcommand === "list") {
12182
12322
  process.stdout.write(`
@@ -12418,11 +12558,11 @@ ${"\u2500".repeat(60)}
12418
12558
  `);
12419
12559
  }
12420
12560
  async function traceLocal(receiptId) {
12421
- const { readFileSync: readFileSync11, existsSync: existsSync9 } = await import("fs");
12422
- const { join: join8 } = await import("path");
12561
+ const { readFileSync: readFileSync11, existsSync: existsSync10 } = await import("fs");
12562
+ const { join: join9 } = await import("path");
12423
12563
  const dir = process.cwd();
12424
- const receiptsDir = join8(dir, ".protect-mcp", "receipts");
12425
- if (!existsSync9(receiptsDir)) {
12564
+ const receiptsDir = join9(dir, ".protect-mcp", "receipts");
12565
+ if (!existsSync10(receiptsDir)) {
12426
12566
  process.stdout.write(` No local receipts found in ${receiptsDir}
12427
12567
 
12428
12568
  `);
@@ -12436,7 +12576,7 @@ async function traceLocal(receiptId) {
12436
12576
  const receipts = [];
12437
12577
  for (const file of files) {
12438
12578
  try {
12439
- const content = readFileSync11(join8(receiptsDir, file), "utf-8");
12579
+ const content = readFileSync11(join9(receiptsDir, file), "utf-8");
12440
12580
  const receipt = JSON.parse(content);
12441
12581
  receipts.push(receipt);
12442
12582
  } catch {
@@ -12493,8 +12633,8 @@ function getTypeEmoji(type) {
12493
12633
  }
12494
12634
  }
12495
12635
  async function handleInitHooks(argv) {
12496
- const { writeFileSync: writeFileSync4, existsSync: existsSync9, mkdirSync: mkdirSync3, readFileSync: readFileSync11 } = await import("fs");
12497
- const { join: join8 } = await import("path");
12636
+ const { writeFileSync: writeFileSync5, existsSync: existsSync10, mkdirSync: mkdirSync4, readFileSync: readFileSync11 } = await import("fs");
12637
+ const { join: join9 } = await import("path");
12498
12638
  const { generateHookSettings: generateHookSettings2, generateSampleCedarPolicy: generateSampleCedarPolicy2, generateVerifyReceiptSkill: generateVerifyReceiptSkill2 } = await Promise.resolve().then(() => (init_hook_patterns(), hook_patterns_exports));
12499
12639
  let dir = process.cwd();
12500
12640
  const dirIdx = argv.indexOf("--dir");
@@ -12508,13 +12648,13 @@ ${bold("protect-mcp init-hooks")}
12508
12648
  process.stdout.write(`${"\u2500".repeat(55)}
12509
12649
 
12510
12650
  `);
12511
- const claudeDir = join8(dir, ".claude");
12512
- const settingsPath = join8(claudeDir, "settings.json");
12651
+ const claudeDir = join9(dir, ".claude");
12652
+ const settingsPath = join9(claudeDir, "settings.json");
12513
12653
  let existingSettings = {};
12514
- if (!existsSync9(claudeDir)) {
12515
- mkdirSync3(claudeDir, { recursive: true });
12654
+ if (!existsSync10(claudeDir)) {
12655
+ mkdirSync4(claudeDir, { recursive: true });
12516
12656
  }
12517
- if (existsSync9(settingsPath)) {
12657
+ if (existsSync10(settingsPath)) {
12518
12658
  try {
12519
12659
  existingSettings = JSON.parse(readFileSync11(settingsPath, "utf-8"));
12520
12660
  } catch {
@@ -12530,7 +12670,7 @@ ${bold("protect-mcp init-hooks")}
12530
12670
  ...hookSettings.hooks
12531
12671
  }
12532
12672
  };
12533
- writeFileSync4(settingsPath, JSON.stringify(mergedSettings, null, 2) + "\n");
12673
+ writeFileSync5(settingsPath, JSON.stringify(mergedSettings, null, 2) + "\n");
12534
12674
  process.stdout.write(` ${green("\u2713")} ${settingsPath}
12535
12675
  `);
12536
12676
  process.stdout.write(` Hook URL: ${dim(hookUrl)}
@@ -12538,26 +12678,26 @@ ${bold("protect-mcp init-hooks")}
12538
12678
  process.stdout.write(` Events: PreToolUse, PostToolUse, SubagentStart/Stop, Task, Session, Config, Stop
12539
12679
 
12540
12680
  `);
12541
- const keysDir = join8(dir, "keys");
12542
- const keyPath = join8(keysDir, "gateway.json");
12543
- if (!existsSync9(keyPath)) {
12544
- if (!existsSync9(keysDir)) mkdirSync3(keysDir, { recursive: true });
12681
+ const keysDir = join9(dir, "keys");
12682
+ const keyPath = join9(keysDir, "gateway.json");
12683
+ if (!existsSync10(keyPath)) {
12684
+ if (!existsSync10(keysDir)) mkdirSync4(keysDir, { recursive: true });
12545
12685
  const { randomBytes: rb } = await import("crypto");
12546
12686
  try {
12547
12687
  const { ed25519: ed255192 } = await Promise.resolve().then(() => (init_ed25519(), ed25519_exports));
12548
12688
  const { bytesToHex: bytesToHex2 } = await Promise.resolve().then(() => (init_utils(), utils_exports));
12549
12689
  const privateKey = rb(32);
12550
12690
  const publicKey = ed255192.getPublicKey(privateKey);
12551
- writeFileSync4(keyPath, JSON.stringify({
12691
+ writeFileSync5(keyPath, JSON.stringify({
12552
12692
  privateKey: bytesToHex2(privateKey),
12553
12693
  publicKey: bytesToHex2(publicKey),
12554
12694
  kid: `hook-${Date.now()}`,
12555
12695
  generated_at: (/* @__PURE__ */ new Date()).toISOString(),
12556
12696
  warning: "KEEP THIS FILE SECRET. Never commit to version control."
12557
12697
  }, null, 2) + "\n");
12558
- const gitignorePath = join8(keysDir, ".gitignore");
12559
- if (!existsSync9(gitignorePath)) {
12560
- writeFileSync4(gitignorePath, "# Never commit signing keys\n*.json\n");
12698
+ const gitignorePath = join9(keysDir, ".gitignore");
12699
+ if (!existsSync10(gitignorePath)) {
12700
+ writeFileSync5(gitignorePath, "# Never commit signing keys\n*.json\n");
12561
12701
  }
12562
12702
  process.stdout.write(` ${green("\u2713")} ${keyPath} (Ed25519 keypair)
12563
12703
 
@@ -12572,11 +12712,11 @@ ${bold("protect-mcp init-hooks")}
12572
12712
 
12573
12713
  `);
12574
12714
  }
12575
- const policiesDir = join8(dir, "policies");
12576
- const cedarPath = join8(policiesDir, "agent.cedar");
12577
- if (!existsSync9(cedarPath)) {
12578
- if (!existsSync9(policiesDir)) mkdirSync3(policiesDir, { recursive: true });
12579
- writeFileSync4(cedarPath, generateSampleCedarPolicy2());
12715
+ const policiesDir = join9(dir, "policies");
12716
+ const cedarPath = join9(policiesDir, "agent.cedar");
12717
+ if (!existsSync10(cedarPath)) {
12718
+ if (!existsSync10(policiesDir)) mkdirSync4(policiesDir, { recursive: true });
12719
+ writeFileSync5(cedarPath, generateSampleCedarPolicy2());
12580
12720
  process.stdout.write(` ${green("\u2713")} ${cedarPath}
12581
12721
  `);
12582
12722
  process.stdout.write(` Edit to customize tool permissions. Cedar deny is AUTHORITATIVE.
@@ -12587,8 +12727,8 @@ ${bold("protect-mcp init-hooks")}
12587
12727
 
12588
12728
  `);
12589
12729
  }
12590
- const configPath = join8(dir, "protect-mcp.json");
12591
- if (!existsSync9(configPath)) {
12730
+ const configPath = join9(dir, "protect-mcp.json");
12731
+ if (!existsSync10(configPath)) {
12592
12732
  const config = {
12593
12733
  tools: { "*": { rate_limit: "100/hour" } },
12594
12734
  default_tier: "unknown",
@@ -12598,16 +12738,16 @@ ${bold("protect-mcp init-hooks")}
12598
12738
  enabled: true
12599
12739
  }
12600
12740
  };
12601
- writeFileSync4(configPath, JSON.stringify(config, null, 2) + "\n");
12741
+ writeFileSync5(configPath, JSON.stringify(config, null, 2) + "\n");
12602
12742
  process.stdout.write(` ${green("\u2713")} ${configPath}
12603
12743
 
12604
12744
  `);
12605
12745
  }
12606
- const skillsDir = join8(dir, ".claude", "skills", "verify-receipt");
12607
- const skillPath = join8(skillsDir, "SKILL.md");
12608
- if (!existsSync9(skillPath)) {
12609
- mkdirSync3(skillsDir, { recursive: true });
12610
- writeFileSync4(skillPath, generateVerifyReceiptSkill2());
12746
+ const skillsDir = join9(dir, ".claude", "skills", "verify-receipt");
12747
+ const skillPath = join9(skillsDir, "SKILL.md");
12748
+ if (!existsSync10(skillPath)) {
12749
+ mkdirSync4(skillsDir, { recursive: true });
12750
+ writeFileSync5(skillPath, generateVerifyReceiptSkill2());
12611
12751
  process.stdout.write(` ${green("\u2713")} ${skillPath}
12612
12752
  `);
12613
12753
  process.stdout.write(` Use ${dim("/verify-receipt")} in Claude Code to check audit trails.
@@ -12663,13 +12803,13 @@ ${bold("protect-mcp init-hooks")}
12663
12803
  }
12664
12804
  async function sendInstallTelemetry() {
12665
12805
  try {
12666
- const { existsSync: existsSync9, mkdirSync: mkdirSync3, writeFileSync: writeFileSync4, readFileSync: readFileSync11 } = await import("fs");
12667
- const { join: join8, dirname: dirname3 } = await import("path");
12806
+ const { existsSync: existsSync10, mkdirSync: mkdirSync4, writeFileSync: writeFileSync5, readFileSync: readFileSync11 } = await import("fs");
12807
+ const { join: join9, dirname: dirname3 } = await import("path");
12668
12808
  const { homedir } = await import("os");
12669
12809
  const { fileURLToPath } = await import("url");
12670
- const markerDir = join8(homedir(), ".protect-mcp");
12671
- const markerFile = join8(markerDir, ".telemetry-sent");
12672
- if (existsSync9(markerFile) || process.env.PROTECT_MCP_TELEMETRY === "off") {
12810
+ const markerDir = join9(homedir(), ".protect-mcp");
12811
+ const markerFile = join9(markerDir, ".telemetry-sent");
12812
+ if (existsSync10(markerFile) || process.env.PROTECT_MCP_TELEMETRY === "off") {
12673
12813
  return;
12674
12814
  }
12675
12815
  const version = await pkgVersion();
@@ -12689,10 +12829,10 @@ async function sendInstallTelemetry() {
12689
12829
  signal: controller.signal
12690
12830
  }).catch(() => {
12691
12831
  }).finally(() => clearTimeout(timeout));
12692
- if (!existsSync9(markerDir)) {
12693
- mkdirSync3(markerDir, { recursive: true });
12832
+ if (!existsSync10(markerDir)) {
12833
+ mkdirSync4(markerDir, { recursive: true });
12694
12834
  }
12695
- writeFileSync4(markerFile, String(Date.now()), "utf-8");
12835
+ writeFileSync5(markerFile, String(Date.now()), "utf-8");
12696
12836
  process.stderr.write(
12697
12837
  "[protect-mcp] Thanks for installing! Anonymous telemetry sent (disable: PROTECT_MCP_TELEMETRY=off)\n[protect-mcp] Free dashboard: npx protect-mcp connect | https://scopeblind.com\n"
12698
12838
  );
@@ -12708,8 +12848,8 @@ function loadPolicyArg(argv) {
12708
12848
  const policyFile = flagValue(argv, "--policy");
12709
12849
  try {
12710
12850
  if (cedarDir) return loadCedarPolicies(cedarDir);
12711
- if (policyFile && (0, import_node_fs12.existsSync)(policyFile)) {
12712
- return policySetFromSource((0, import_node_fs12.readFileSync)(policyFile, "utf-8"), (0, import_node_path8.basename)(policyFile));
12851
+ if (policyFile && (0, import_node_fs13.existsSync)(policyFile)) {
12852
+ return policySetFromSource((0, import_node_fs13.readFileSync)(policyFile, "utf-8"), (0, import_node_path9.basename)(policyFile));
12713
12853
  }
12714
12854
  } catch {
12715
12855
  }
@@ -12810,7 +12950,7 @@ async function handleSign(argv) {
12810
12950
  if (m.tool) tool = m.tool;
12811
12951
  }
12812
12952
  }
12813
- if (keyPath && (0, import_node_fs12.existsSync)(keyPath)) {
12953
+ if (keyPath && (0, import_node_fs13.existsSync)(keyPath)) {
12814
12954
  try {
12815
12955
  await initSigning({ enabled: true, key_path: keyPath });
12816
12956
  } catch {
@@ -12827,12 +12967,12 @@ async function handleSign(argv) {
12827
12967
  timestamp: Date.now()
12828
12968
  });
12829
12969
  try {
12830
- (0, import_node_fs12.mkdirSync)(receiptsDir, { recursive: true });
12970
+ (0, import_node_fs13.mkdirSync)(receiptsDir, { recursive: true });
12831
12971
  } catch {
12832
12972
  }
12833
12973
  const line = signed.signed ?? JSON.stringify({ tool, request_id: requestId, signed: false, note: signed.warning || "no signer configured" });
12834
12974
  try {
12835
- (0, import_node_fs12.appendFileSync)((0, import_node_path8.join)(receiptsDir, "receipts.jsonl"), line + "\n");
12975
+ (0, import_node_fs13.appendFileSync)((0, import_node_path9.join)(receiptsDir, "receipts.jsonl"), line + "\n");
12836
12976
  } catch {
12837
12977
  }
12838
12978
  if (format === "hermes") {
@@ -12842,12 +12982,200 @@ async function handleSign(argv) {
12842
12982
  process.stdout.write(JSON.stringify({ signed: Boolean(signed.signed), artifact_type: signed.artifact_type, request_id: requestId }) + "\n");
12843
12983
  process.exit(0);
12844
12984
  }
12985
+ async function handleSample(argv) {
12986
+ const dir = flagValue(argv, "--dir") || process.cwd();
12987
+ const { buildSampleKit: buildSampleKit2 } = await Promise.resolve().then(() => (init_sample(), sample_exports));
12988
+ let kit;
12989
+ try {
12990
+ kit = buildSampleKit2(dir, { force: argv.includes("--force") });
12991
+ } catch (err) {
12992
+ if (err?.code === "SAMPLE_EXISTS") {
12993
+ process.stderr.write(
12994
+ "\nprotect-mcp sample: this folder already has a record or signing key.\nThis command seeds a LABELED SAMPLE record and will not touch a real one.\nRun it in an empty folder, or pass --force to overwrite.\n\n"
12995
+ );
12996
+ process.exit(1);
12997
+ }
12998
+ throw err;
12999
+ }
13000
+ process.stdout.write(`
13001
+ ${bold("\u{1F6E1} Sample record seeded")} \xB7 8 decisions (1 blocked, 2 payments), signed with a fresh key ${dim(`(kid ${kit.kid})`)}
13002
+
13003
+ `);
13004
+ process.stdout.write(" .protect-mcp-receipts.jsonl the signed sample record\n");
13005
+ process.stdout.write(" demo-tampered.jsonl the same record with ONE decision edited after signing\n");
13006
+ process.stdout.write(` keys/gateway.json sample keypair ${dim("(never commit)")}
13007
+
13008
+ `);
13009
+ process.stdout.write(`${bold("Replay the demo")} ${dim("(the film: legate.scopeblind.com/record)")}
13010
+ `);
13011
+ process.stdout.write(" npx protect-mcp record\n");
13012
+ process.stdout.write(" npx protect-mcp claim --payment-under 100 --anchor --output payments-under-100.json\n");
13013
+ process.stdout.write(" npx protect-mcp verify-claim payments-under-100.json\n");
13014
+ process.stdout.write(" npx protect-mcp anchor-record\n\n");
13015
+ process.stdout.write(`${dim("Drop demo-tampered.jsonl into the record page to watch tampering get caught.")}
13016
+ `);
13017
+ process.stdout.write(`${dim("Everything runs locally; --anchor publishes only a digest to the public log.")}
13018
+
13019
+ `);
13020
+ process.exit(0);
13021
+ }
13022
+ async function handlePolicy(argv) {
13023
+ const { readFileSync: rf, writeFileSync: wf, existsSync: ex, readdirSync: rd, appendFileSync: af } = await import("fs");
13024
+ const { join: pj } = await import("path");
13025
+ const { createHash: createHash6 } = await import("crypto");
13026
+ const sub = argv[0] || "list";
13027
+ const findDir = () => {
13028
+ for (const c of ["cedar", "policies", "."]) {
13029
+ try {
13030
+ if (ex(c) && rd(c).some((f) => f.endsWith(".cedar"))) return c;
13031
+ } catch {
13032
+ }
13033
+ }
13034
+ return null;
13035
+ };
13036
+ const dir = findDir();
13037
+ const cedarFiles = dir ? rd(dir).filter((f) => f.endsWith(".cedar")).sort() : [];
13038
+ const readAll = () => cedarFiles.map((f) => rf(pj(dir, f), "utf-8")).join("\n\n");
13039
+ const digestOf = (src) => createHash6("sha256").update(src).digest("hex").slice(0, 16);
13040
+ const stripComments = (src) => src.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\/\/[^\n]*/g, "");
13041
+ const targetFile = cedarFiles.includes("agent.cedar") ? "agent.cedar" : cedarFiles[0];
13042
+ if (sub === "path") {
13043
+ if (!dir) {
13044
+ process.stdout.write("No Cedar policy directory found (looked in ./cedar, ./policies, .).\n");
13045
+ process.exit(0);
13046
+ }
13047
+ process.stdout.write(`${pj(dir, targetFile)}
13048
+ `);
13049
+ process.exit(0);
13050
+ }
13051
+ if (sub === "show") {
13052
+ if (!dir) {
13053
+ process.stderr.write("No Cedar policy found. Run: npx protect-mcp init-hooks\n");
13054
+ process.exit(1);
13055
+ }
13056
+ const src = readAll();
13057
+ process.stdout.write(`${bold("Cedar policy")} ${dim(`(${cedarFiles.length} file${cedarFiles.length === 1 ? "" : "s"} in ${dir}, digest ${digestOf(src)})`)}
13058
+
13059
+ `);
13060
+ process.stdout.write(src.endsWith("\n") ? src : src + "\n");
13061
+ process.exit(0);
13062
+ }
13063
+ if (sub === "allow" || sub === "deny") {
13064
+ const tool = argv[1];
13065
+ if (!tool) {
13066
+ process.stderr.write(`Usage: npx protect-mcp policy ${sub} <ToolName>
13067
+ `);
13068
+ process.exit(1);
13069
+ }
13070
+ if (!/^[A-Za-z0-9_.:-]+$/.test(tool)) {
13071
+ process.stderr.write(`Refusing: "${tool}" is not a valid tool name.
13072
+ `);
13073
+ process.exit(1);
13074
+ }
13075
+ if (!dir) {
13076
+ process.stderr.write("No Cedar policy found. Run: npx protect-mcp init-hooks\n");
13077
+ process.exit(1);
13078
+ }
13079
+ const effect = sub === "allow" ? "permit" : "forbid";
13080
+ const before = readAll();
13081
+ const ruleRe = new RegExp(`${effect}\\s*\\([^)]*resource\\s*==\\s*Tool::"${tool.replace(/[.*+?^${}()|[\\]\\\\]/g, "\\$&")}"`, "s");
13082
+ if (ruleRe.test(stripComments(before))) {
13083
+ process.stdout.write(`${dim("No change:")} a ${effect} rule for ${bold(tool)} already exists in ${targetFile}.
13084
+ `);
13085
+ process.exit(0);
13086
+ }
13087
+ const block = `
13088
+ // ${effect === "permit" ? "Allow" : "Block"} ${tool} (added by \`protect-mcp policy ${sub}\`)
13089
+ ${effect}(
13090
+ principal,
13091
+ action == Action::"MCP::Tool::call",
13092
+ resource == Tool::"${tool}"
13093
+ );
13094
+ `;
13095
+ af(pj(dir, targetFile), block);
13096
+ const after = readAll();
13097
+ process.stdout.write(`${bold(sub === "allow" ? "\u2713 Allowed" : "\u2713 Denied")} ${tool}
13098
+ `);
13099
+ process.stdout.write(` ${dim(`appended to ${pj(dir, targetFile)}`)}
13100
+ `);
13101
+ process.stdout.write(` ${dim(`policy digest ${digestOf(before)} \u2192 ${digestOf(after)}`)}
13102
+ `);
13103
+ process.stdout.write(`
13104
+ ${dim("A running gate (protect-mcp serve) hot-reloads on this change; no restart needed. The one-shot hook path re-reads per call.")}
13105
+ `);
13106
+ process.exit(0);
13107
+ }
13108
+ if (sub === "list") {
13109
+ if (!dir) {
13110
+ process.stderr.write("No Cedar policy found. Run: npx protect-mcp init-hooks\n");
13111
+ process.exit(1);
13112
+ }
13113
+ const src = readAll();
13114
+ const active = stripComments(src);
13115
+ const named = (effect) => {
13116
+ const set = /* @__PURE__ */ new Set();
13117
+ const re = new RegExp(`${effect}\\s*\\([\\s\\S]*?resource\\s*==\\s*Tool::"([^"]+)"[\\s\\S]*?\\);`, "g");
13118
+ let m;
13119
+ while (m = re.exec(active)) set.add(m[1]);
13120
+ return set;
13121
+ };
13122
+ const permitted = named("permit"), forbidden = named("forbid");
13123
+ const seen = /* @__PURE__ */ new Map();
13124
+ try {
13125
+ const logPath = pj(process.cwd(), ".protect-mcp-log.jsonl");
13126
+ if (ex(logPath)) {
13127
+ for (const line of rf(logPath, "utf-8").split("\n")) {
13128
+ if (!line.trim()) continue;
13129
+ try {
13130
+ const e = JSON.parse(line);
13131
+ if (!e.tool) continue;
13132
+ const rec = seen.get(e.tool) || { allow: 0, deny: 0 };
13133
+ if (e.decision === "deny") rec.deny++;
13134
+ else rec.allow++;
13135
+ seen.set(e.tool, rec);
13136
+ } catch {
13137
+ }
13138
+ }
13139
+ }
13140
+ } catch {
13141
+ }
13142
+ process.stdout.write(`${bold("Cedar policy")} ${dim(`(${dir}, digest ${digestOf(src)}) \xB7 default-deny, fail-closed`)}
13143
+
13144
+ `);
13145
+ const allTools = /* @__PURE__ */ new Set([...permitted, ...forbidden, ...seen.keys()]);
13146
+ if (allTools.size === 0) {
13147
+ process.stdout.write(dim(" No rules and no decisions logged yet.\n"));
13148
+ process.exit(0);
13149
+ }
13150
+ const rows = [...allTools].sort().map((t) => {
13151
+ const label = forbidden.has(t) ? "forbid" : permitted.has(t) ? "permit" : "default-deny";
13152
+ const colored = forbidden.has(t) ? red(label) : permitted.has(t) ? green(label) : yellow(label);
13153
+ const pad = " ".repeat(Math.max(1, 13 - label.length));
13154
+ const s = seen.get(t);
13155
+ const hits = s ? dim(` ${s.allow} allowed, ${s.deny} denied`) : "";
13156
+ return ` ${colored}${pad}${bold(t)}${hits}`;
13157
+ });
13158
+ process.stdout.write(rows.join("\n") + "\n");
13159
+ process.stdout.write(`
13160
+ ${dim("Allow a tool: npx protect-mcp policy allow <ToolName> \xB7 Block one: policy deny <ToolName>")}
13161
+ `);
13162
+ process.exit(0);
13163
+ }
13164
+ process.stderr.write("Usage: npx protect-mcp policy <list|show|allow <tool>|deny <tool>|path>\n");
13165
+ process.exit(1);
13166
+ }
12845
13167
  async function main() {
12846
13168
  sendInstallTelemetry().catch(() => {
12847
13169
  });
12848
13170
  const args = process.argv.slice(2);
12849
13171
  process.env.PROTECT_MCP_VERSION = process.env.PROTECT_MCP_VERSION || await pkgVersion();
12850
- if (args.length === 0 || args.includes("--help") || args.includes("-h")) {
13172
+ const preSep = args.includes("--") ? args.slice(0, args.indexOf("--")) : args;
13173
+ if (args[0] === "version" || preSep.includes("--version") || preSep.includes("-V")) {
13174
+ process.stdout.write(`${process.env.PROTECT_MCP_VERSION || "unknown"}
13175
+ `);
13176
+ process.exit(0);
13177
+ }
13178
+ if (args.length === 0 || args[0] === "help" || preSep.includes("--help") || preSep.includes("-h")) {
12851
13179
  printHelp();
12852
13180
  process.exit(0);
12853
13181
  }
@@ -12901,6 +13229,14 @@ async function main() {
12901
13229
  await handleAnchorRecord(args.slice(1));
12902
13230
  return;
12903
13231
  }
13232
+ if (args[0] === "sample") {
13233
+ await handleSample(args.slice(1));
13234
+ return;
13235
+ }
13236
+ if (args[0] === "policy") {
13237
+ await handlePolicy(args.slice(1));
13238
+ return;
13239
+ }
12904
13240
  if (args[0] === "init-hooks") {
12905
13241
  await handleInitHooks(args.slice(1));
12906
13242
  process.exit(0);
@@ -13005,10 +13341,10 @@ async function main() {
13005
13341
  let cedarPolicySet = null;
13006
13342
  let effectiveCedarDir = cedarDir;
13007
13343
  if (!effectiveCedarDir && !policyPath) {
13008
- const { existsSync: existsSync9, readdirSync: readdirSync4 } = await import("fs");
13344
+ const { existsSync: existsSync10, readdirSync: readdirSync4 } = await import("fs");
13009
13345
  for (const candidate of ["cedar", "policies", "."]) {
13010
13346
  try {
13011
- if (existsSync9(candidate) && readdirSync4(candidate).some((f) => f.endsWith(".cedar"))) {
13347
+ if (existsSync10(candidate) && readdirSync4(candidate).some((f) => f.endsWith(".cedar"))) {
13012
13348
  effectiveCedarDir = candidate;
13013
13349
  process.stderr.write(`[PROTECT_MCP] Auto-detected Cedar policies in ./${candidate}/
13014
13350
  `);
@@ -13119,8 +13455,8 @@ async function handleSimulate(args) {
13119
13455
  process.stderr.write("Usage: protect-mcp simulate --policy <path> [--log <path>] [--tier <tier>] [--json]\n");
13120
13456
  process.exit(1);
13121
13457
  }
13122
- const { existsSync: existsSync9 } = await import("fs");
13123
- if (!existsSync9(logPath)) {
13458
+ const { existsSync: existsSync10 } = await import("fs");
13459
+ if (!existsSync10(logPath)) {
13124
13460
  process.stderr.write(`Log file not found: ${logPath}
13125
13461
  `);
13126
13462
  process.stderr.write("Run protect-mcp in shadow mode first to generate a log file.\n");
@@ -13142,8 +13478,8 @@ async function handleSimulate(args) {
13142
13478
  }
13143
13479
  }
13144
13480
  async function handleDoctor() {
13145
- const { existsSync: existsSync9, readFileSync: readFileSync11, readdirSync: readdirSync4 } = await import("fs");
13146
- const { join: join8 } = await import("path");
13481
+ const { existsSync: existsSync10, readFileSync: readFileSync11, readdirSync: readdirSync4 } = await import("fs");
13482
+ const { join: join9 } = await import("path");
13147
13483
  const { execSync } = await import("child_process");
13148
13484
  const green2 = (s) => `\x1B[32m\u2713\x1B[0m ${s}`;
13149
13485
  const red2 = (s) => `\x1B[31m\u2717\x1B[0m ${s}`;
@@ -13162,8 +13498,8 @@ async function handleDoctor() {
13162
13498
  `));
13163
13499
  issues++;
13164
13500
  }
13165
- const configPath = join8(process.cwd(), "scopeblind.config.json");
13166
- if (existsSync9(configPath)) {
13501
+ const configPath = join9(process.cwd(), "scopeblind.config.json");
13502
+ if (existsSync10(configPath)) {
13167
13503
  try {
13168
13504
  const config = JSON.parse(readFileSync11(configPath, "utf-8"));
13169
13505
  if (config.signing?.private_key || config.signing?.key_file) {
@@ -13182,7 +13518,7 @@ async function handleDoctor() {
13182
13518
  let policyFound = false;
13183
13519
  for (const dir of ["cedar", "policies", "."]) {
13184
13520
  try {
13185
- if (existsSync9(dir) && readdirSync4(dir).some((f) => f.endsWith(".cedar"))) {
13521
+ if (existsSync10(dir) && readdirSync4(dir).some((f) => f.endsWith(".cedar"))) {
13186
13522
  process.stdout.write(green2(`Cedar policies found in ./${dir}/
13187
13523
  `));
13188
13524
  policyFound = true;
@@ -13193,7 +13529,7 @@ async function handleDoctor() {
13193
13529
  }
13194
13530
  if (!policyFound) {
13195
13531
  for (const name of ["policy.json", "protect-mcp.policy.json", "scopeblind-policy.json"]) {
13196
- if (existsSync9(name)) {
13532
+ if (existsSync10(name)) {
13197
13533
  process.stdout.write(green2(`JSON policy found: ${name}
13198
13534
  `));
13199
13535
  policyFound = true;
@@ -13214,9 +13550,9 @@ async function handleDoctor() {
13214
13550
  } catch {
13215
13551
  process.stdout.write(dim2(" Cedar WASM not installed\n"));
13216
13552
  }
13217
- const logFile = join8(process.cwd(), "protect-mcp-decisions.jsonl");
13218
- const receiptFile = join8(process.cwd(), "protect-mcp-receipts.jsonl");
13219
- if (existsSync9(logFile)) {
13553
+ const logFile = join9(process.cwd(), "protect-mcp-decisions.jsonl");
13554
+ const receiptFile = join9(process.cwd(), "protect-mcp-receipts.jsonl");
13555
+ if (existsSync10(logFile)) {
13220
13556
  try {
13221
13557
  const lines = readFileSync11(logFile, "utf-8").trim().split("\n").length;
13222
13558
  process.stdout.write(green2(`Decision log: ${lines} entries
@@ -13227,7 +13563,7 @@ async function handleDoctor() {
13227
13563
  } else {
13228
13564
  process.stdout.write(dim2(" No decision log yet, will be created on first tool call\n"));
13229
13565
  }
13230
- if (existsSync9(receiptFile)) {
13566
+ if (existsSync10(receiptFile)) {
13231
13567
  try {
13232
13568
  const lines = readFileSync11(receiptFile, "utf-8").trim().split("\n").length;
13233
13569
  process.stdout.write(green2(`Receipt file: ${lines} signed receipts
@@ -13298,9 +13634,9 @@ async function handleReport(args) {
13298
13634
  }
13299
13635
  }
13300
13636
  const { generateReport: generateReport2, formatReportMarkdown: formatReportMarkdown2 } = await Promise.resolve().then(() => (init_report(), report_exports));
13301
- const { join: join8 } = await import("path");
13302
- const logPath = join8(dir, ".protect-mcp-log.jsonl");
13303
- const receiptPath = join8(dir, ".protect-mcp-receipts.jsonl");
13637
+ const { join: join9 } = await import("path");
13638
+ const logPath = join9(dir, ".protect-mcp-log.jsonl");
13639
+ const receiptPath = join9(dir, ".protect-mcp-receipts.jsonl");
13304
13640
  const report = generateReport2(logPath, receiptPath, period);
13305
13641
  let output;
13306
13642
  if (format === "md") {
@@ -13309,8 +13645,8 @@ async function handleReport(args) {
13309
13645
  output = JSON.stringify(report, null, 2);
13310
13646
  }
13311
13647
  if (outputPath) {
13312
- const { writeFileSync: writeFileSync4 } = await import("fs");
13313
- writeFileSync4(outputPath, output, "utf-8");
13648
+ const { writeFileSync: writeFileSync5 } = await import("fs");
13649
+ writeFileSync5(outputPath, output, "utf-8");
13314
13650
  process.stderr.write(`Report written to ${outputPath}
13315
13651
  `);
13316
13652
  } else {