protect-mcp 0.9.4 → 0.9.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/README.md +19 -0
- package/dist/cli.js +442 -297
- package/dist/cli.mjs +53 -3
- package/dist/sample-6LRP73ZD.mjs +99 -0
- package/package.json +1 -1
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,
|
|
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
|
}
|
|
@@ -6972,14 +7067,14 @@ function emitDecisionLog(state, entry) {
|
|
|
6972
7067
|
process.stderr.write(`[PROTECT_MCP] ${JSON.stringify(log)}
|
|
6973
7068
|
`);
|
|
6974
7069
|
try {
|
|
6975
|
-
(0,
|
|
7070
|
+
(0, import_node_fs11.appendFileSync)(state.logFilePath, JSON.stringify(log) + "\n");
|
|
6976
7071
|
} catch {
|
|
6977
7072
|
}
|
|
6978
7073
|
if (isSigningEnabled()) {
|
|
6979
7074
|
const signed = signDecision(log);
|
|
6980
7075
|
if (signed.signed) {
|
|
6981
7076
|
try {
|
|
6982
|
-
(0,
|
|
7077
|
+
(0, import_node_fs11.appendFileSync)(state.receiptFilePath, signed.signed + "\n");
|
|
6983
7078
|
} catch {
|
|
6984
7079
|
}
|
|
6985
7080
|
state.receiptBuffer.add(log.request_id, signed.signed);
|
|
@@ -7003,7 +7098,7 @@ function emitDecisionLog(state, entry) {
|
|
|
7003
7098
|
at: new Date(log.timestamp).toISOString()
|
|
7004
7099
|
});
|
|
7005
7100
|
try {
|
|
7006
|
-
(0,
|
|
7101
|
+
(0, import_node_fs11.appendFileSync)(state.receiptFilePath, tombstone + "\n");
|
|
7007
7102
|
} catch {
|
|
7008
7103
|
}
|
|
7009
7104
|
process.stderr.write(`[PROTECT_MCP_SIGNING_FAILURE] ${tombstone}
|
|
@@ -7091,8 +7186,8 @@ async function startHookServer(options = {}) {
|
|
|
7091
7186
|
}
|
|
7092
7187
|
}
|
|
7093
7188
|
if (!jsonPolicy?.signing) {
|
|
7094
|
-
const keyPath = (0,
|
|
7095
|
-
if ((0,
|
|
7189
|
+
const keyPath = (0, import_node_path8.join)(process.cwd(), "keys", "gateway.json");
|
|
7190
|
+
if ((0, import_node_fs11.existsSync)(keyPath)) {
|
|
7096
7191
|
const warnings = await initSigning({ key_path: keyPath, issuer: "protect-mcp", enabled: true });
|
|
7097
7192
|
for (const w of warnings) {
|
|
7098
7193
|
process.stderr.write(`[PROTECT_MCP] Warning: ${w}
|
|
@@ -7114,8 +7209,8 @@ async function startHookServer(options = {}) {
|
|
|
7114
7209
|
verbose,
|
|
7115
7210
|
enforce,
|
|
7116
7211
|
policyDigest,
|
|
7117
|
-
logFilePath: (0,
|
|
7118
|
-
receiptFilePath: (0,
|
|
7212
|
+
logFilePath: (0, import_node_path8.join)(process.cwd(), LOG_FILE3),
|
|
7213
|
+
receiptFilePath: (0, import_node_path8.join)(process.cwd(), RECEIPTS_FILE2),
|
|
7119
7214
|
permissionSuggestions: /* @__PURE__ */ new Map(),
|
|
7120
7215
|
configAlerts: []
|
|
7121
7216
|
};
|
|
@@ -7264,7 +7359,7 @@ async function startHookServer(options = {}) {
|
|
|
7264
7359
|
`);
|
|
7265
7360
|
w(`
|
|
7266
7361
|
`);
|
|
7267
|
-
const hasSlug = process.env.SCOPEBLIND_SLUG || (0,
|
|
7362
|
+
const hasSlug = process.env.SCOPEBLIND_SLUG || (0, import_node_fs11.existsSync)((0, import_node_path8.join)(process.cwd(), ".scopeblind"));
|
|
7268
7363
|
if (!hasSlug) {
|
|
7269
7364
|
w(` Dashboard npx protect-mcp connect
|
|
7270
7365
|
`);
|
|
@@ -7295,8 +7390,8 @@ async function startHookServer(options = {}) {
|
|
|
7295
7390
|
function findCedarDir() {
|
|
7296
7391
|
for (const candidate of ["cedar", "policies", "."]) {
|
|
7297
7392
|
try {
|
|
7298
|
-
if ((0,
|
|
7299
|
-
const files = (0,
|
|
7393
|
+
if ((0, import_node_fs11.existsSync)(candidate)) {
|
|
7394
|
+
const files = (0, import_node_fs11.readdirSync)(candidate, { encoding: "utf-8" });
|
|
7300
7395
|
if (files.some((f) => f.endsWith(".cedar"))) {
|
|
7301
7396
|
return candidate;
|
|
7302
7397
|
}
|
|
@@ -7317,14 +7412,14 @@ function normalizeHookInput(raw) {
|
|
|
7317
7412
|
}
|
|
7318
7413
|
return result;
|
|
7319
7414
|
}
|
|
7320
|
-
var import_node_http2, import_node_crypto6,
|
|
7415
|
+
var import_node_http2, import_node_crypto6, import_node_fs11, import_node_path8, DEFAULT_PORT, LOG_FILE3, RECEIPTS_FILE2, PAYLOAD_HASH_THRESHOLD, SNAKE_TO_CAMEL_MAP;
|
|
7321
7416
|
var init_hook_server = __esm({
|
|
7322
7417
|
"src/hook-server.ts"() {
|
|
7323
7418
|
"use strict";
|
|
7324
7419
|
import_node_http2 = require("http");
|
|
7325
7420
|
import_node_crypto6 = require("crypto");
|
|
7326
|
-
|
|
7327
|
-
|
|
7421
|
+
import_node_fs11 = require("fs");
|
|
7422
|
+
import_node_path8 = require("path");
|
|
7328
7423
|
init_cedar_evaluator();
|
|
7329
7424
|
init_signing();
|
|
7330
7425
|
init_policy();
|
|
@@ -7550,8 +7645,8 @@ function generateReport(logPath, receiptPath, periodDays) {
|
|
|
7550
7645
|
const now = /* @__PURE__ */ new Date();
|
|
7551
7646
|
const from = new Date(now.getTime() - periodDays * 864e5);
|
|
7552
7647
|
const entries = [];
|
|
7553
|
-
if ((0,
|
|
7554
|
-
const raw = (0,
|
|
7648
|
+
if ((0, import_node_fs12.existsSync)(logPath)) {
|
|
7649
|
+
const raw = (0, import_node_fs12.readFileSync)(logPath, "utf-8");
|
|
7555
7650
|
for (const line of raw.split("\n")) {
|
|
7556
7651
|
const trimmed = line.trim();
|
|
7557
7652
|
if (!trimmed) continue;
|
|
@@ -7571,8 +7666,8 @@ function generateReport(logPath, receiptPath, periodDays) {
|
|
|
7571
7666
|
let receiptsSigned = 0;
|
|
7572
7667
|
let signerKid = "";
|
|
7573
7668
|
let signerIssuer = "";
|
|
7574
|
-
if ((0,
|
|
7575
|
-
const raw = (0,
|
|
7669
|
+
if ((0, import_node_fs12.existsSync)(receiptPath)) {
|
|
7670
|
+
const raw = (0, import_node_fs12.readFileSync)(receiptPath, "utf-8");
|
|
7576
7671
|
for (const line of raw.split("\n")) {
|
|
7577
7672
|
const trimmed = line.trim();
|
|
7578
7673
|
if (!trimmed) continue;
|
|
@@ -7704,11 +7799,11 @@ function formatReportMarkdown(report) {
|
|
|
7704
7799
|
lines.push("*Generated by protect-mcp \xB7 scopeblind.com*");
|
|
7705
7800
|
return lines.join("\n");
|
|
7706
7801
|
}
|
|
7707
|
-
var
|
|
7802
|
+
var import_node_fs12;
|
|
7708
7803
|
var init_report = __esm({
|
|
7709
7804
|
"src/report.ts"() {
|
|
7710
7805
|
"use strict";
|
|
7711
|
-
|
|
7806
|
+
import_node_fs12 = require("fs");
|
|
7712
7807
|
}
|
|
7713
7808
|
});
|
|
7714
7809
|
|
|
@@ -8752,8 +8847,8 @@ Next: run \`npx protect-mcp dashboard --open\` and review tool inventory, policy
|
|
|
8752
8847
|
|
|
8753
8848
|
// src/cli.ts
|
|
8754
8849
|
var import_node_crypto7 = require("crypto");
|
|
8755
|
-
var
|
|
8756
|
-
var
|
|
8850
|
+
var import_node_fs13 = require("fs");
|
|
8851
|
+
var import_node_path9 = require("path");
|
|
8757
8852
|
var import_node_os = require("os");
|
|
8758
8853
|
function printHelp() {
|
|
8759
8854
|
process.stderr.write(`
|
|
@@ -8775,14 +8870,16 @@ Usage:
|
|
|
8775
8870
|
protect-mcp policy-packs list|show|install [pack] [--dir ./cedar] [--force]
|
|
8776
8871
|
protect-mcp connect
|
|
8777
8872
|
protect-mcp init [--dir <path>]
|
|
8873
|
+
protect-mcp sample [--dir <path>] [--force]
|
|
8778
8874
|
protect-mcp demo
|
|
8779
8875
|
protect-mcp trace <receipt_id> [--endpoint <url>] [--depth <n>]
|
|
8780
8876
|
protect-mcp status [--dir <path>]
|
|
8781
8877
|
protect-mcp digest [--today] [--dir <path>]
|
|
8782
8878
|
protect-mcp receipts [--last <n>] [--dir <path>]
|
|
8783
8879
|
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>]
|
|
8880
|
+
protect-mcp claim [--no <cap>] [--only <c,c>] [--count <verdict>] [--payment-under <amount>] [--anchor] [--dir <path>] [--output <path>]
|
|
8881
|
+
protect-mcp verify-claim <claim.json> [--key <public-hex>] [--check-anchor] [--offline]
|
|
8882
|
+
protect-mcp anchor-record [--dir <path>] [--force]
|
|
8786
8883
|
protect-mcp bundle [--output <path>] [--dir <path>]
|
|
8787
8884
|
protect-mcp simulate --policy <path> [--log <path>] [--tier <tier>] [--json]
|
|
8788
8885
|
protect-mcp report [--period <days>d] [--format md|json] [--output <path>] [--dir <path>]
|
|
@@ -8796,6 +8893,7 @@ Options:
|
|
|
8796
8893
|
--port <port> HTTP server port (default: 3000 for --http, 9377 for serve)
|
|
8797
8894
|
--verbose Enable debug logging to stderr
|
|
8798
8895
|
--help Show this help
|
|
8896
|
+
--version Print the installed version
|
|
8799
8897
|
|
|
8800
8898
|
Commands:
|
|
8801
8899
|
serve Start HTTP hook server for Claude Code integration (port 9377)
|
|
@@ -8906,17 +9004,17 @@ function parseArgs(argv) {
|
|
|
8906
9004
|
return { policyPath, cedarDir, slug, enforce, verbose, childCommand };
|
|
8907
9005
|
}
|
|
8908
9006
|
async function handleInit(argv) {
|
|
8909
|
-
const { writeFileSync:
|
|
8910
|
-
const { join:
|
|
9007
|
+
const { writeFileSync: writeFileSync5, existsSync: existsSync10, mkdirSync: mkdirSync4 } = await import("fs");
|
|
9008
|
+
const { join: join9 } = await import("path");
|
|
8911
9009
|
let dir = process.cwd();
|
|
8912
9010
|
const dirIdx = argv.indexOf("--dir");
|
|
8913
9011
|
if (dirIdx !== -1 && argv[dirIdx + 1]) {
|
|
8914
9012
|
dir = argv[dirIdx + 1];
|
|
8915
9013
|
}
|
|
8916
|
-
const configPath =
|
|
8917
|
-
const keysDir =
|
|
8918
|
-
const keyPath =
|
|
8919
|
-
if (
|
|
9014
|
+
const configPath = join9(dir, "protect-mcp.json");
|
|
9015
|
+
const keysDir = join9(dir, "keys");
|
|
9016
|
+
const keyPath = join9(keysDir, "gateway.json");
|
|
9017
|
+
if (existsSync10(configPath)) {
|
|
8920
9018
|
process.stderr.write(`[PROTECT_MCP] Config already exists at ${configPath}
|
|
8921
9019
|
`);
|
|
8922
9020
|
process.stderr.write("[PROTECT_MCP] Delete it first if you want to regenerate.\n");
|
|
@@ -8935,19 +9033,19 @@ async function handleInit(argv) {
|
|
|
8935
9033
|
kid: "generated"
|
|
8936
9034
|
};
|
|
8937
9035
|
}
|
|
8938
|
-
if (!
|
|
8939
|
-
|
|
9036
|
+
if (!existsSync10(keysDir)) {
|
|
9037
|
+
mkdirSync4(keysDir, { recursive: true });
|
|
8940
9038
|
}
|
|
8941
|
-
|
|
9039
|
+
writeFileSync5(keyPath, JSON.stringify({
|
|
8942
9040
|
privateKey: keypair.privateKey,
|
|
8943
9041
|
publicKey: keypair.publicKey,
|
|
8944
9042
|
kid: keypair.kid,
|
|
8945
9043
|
generated_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8946
9044
|
warning: "KEEP THIS FILE SECRET. Never commit to version control."
|
|
8947
9045
|
}, null, 2) + "\n");
|
|
8948
|
-
const gitignorePath =
|
|
8949
|
-
if (!
|
|
8950
|
-
|
|
9046
|
+
const gitignorePath = join9(keysDir, ".gitignore");
|
|
9047
|
+
if (!existsSync10(gitignorePath)) {
|
|
9048
|
+
writeFileSync5(gitignorePath, "# Never commit signing keys\n*.json\n");
|
|
8951
9049
|
}
|
|
8952
9050
|
const config = {
|
|
8953
9051
|
tools: {
|
|
@@ -8981,7 +9079,7 @@ async function handleInit(argv) {
|
|
|
8981
9079
|
}
|
|
8982
9080
|
}
|
|
8983
9081
|
};
|
|
8984
|
-
|
|
9082
|
+
writeFileSync5(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
8985
9083
|
const claudeConfig = {
|
|
8986
9084
|
"mcpServers": {
|
|
8987
9085
|
"my-server": {
|
|
@@ -9019,8 +9117,8 @@ Add --enforce when ready to block policy violations.
|
|
|
9019
9117
|
`);
|
|
9020
9118
|
}
|
|
9021
9119
|
async function handleDemo() {
|
|
9022
|
-
const { existsSync:
|
|
9023
|
-
const { join:
|
|
9120
|
+
const { existsSync: existsSync10 } = await import("fs");
|
|
9121
|
+
const { join: join9, dirname: dirname3, resolve } = await import("path");
|
|
9024
9122
|
const { realpathSync } = await import("fs");
|
|
9025
9123
|
const cliPath = resolve(process.argv[1] || "dist/cli.js");
|
|
9026
9124
|
let cliDir;
|
|
@@ -9029,9 +9127,9 @@ async function handleDemo() {
|
|
|
9029
9127
|
} catch {
|
|
9030
9128
|
cliDir = dirname3(cliPath);
|
|
9031
9129
|
}
|
|
9032
|
-
const demoServerPath =
|
|
9033
|
-
const configPath =
|
|
9034
|
-
const hasConfig =
|
|
9130
|
+
const demoServerPath = join9(cliDir, "demo-server.js");
|
|
9131
|
+
const configPath = join9(process.cwd(), "protect-mcp.json");
|
|
9132
|
+
const hasConfig = existsSync10(configPath);
|
|
9035
9133
|
if (!hasConfig) {
|
|
9036
9134
|
process.stderr.write(`
|
|
9037
9135
|
${bold("protect-mcp demo")}
|
|
@@ -9105,15 +9203,15 @@ Starting demo server with 5 tools...
|
|
|
9105
9203
|
await gateway.start();
|
|
9106
9204
|
}
|
|
9107
9205
|
async function handleStatus2(argv) {
|
|
9108
|
-
const { readFileSync: readFileSync11, existsSync:
|
|
9109
|
-
const { join:
|
|
9206
|
+
const { readFileSync: readFileSync11, existsSync: existsSync10 } = await import("fs");
|
|
9207
|
+
const { join: join9 } = await import("path");
|
|
9110
9208
|
let dir = process.cwd();
|
|
9111
9209
|
const dirIdx = argv.indexOf("--dir");
|
|
9112
9210
|
if (dirIdx !== -1 && argv[dirIdx + 1]) {
|
|
9113
9211
|
dir = argv[dirIdx + 1];
|
|
9114
9212
|
}
|
|
9115
|
-
const logPath =
|
|
9116
|
-
if (!
|
|
9213
|
+
const logPath = join9(dir, ".protect-mcp-log.jsonl");
|
|
9214
|
+
if (!existsSync10(logPath)) {
|
|
9117
9215
|
process.stderr.write(`${bold("protect-mcp status")}
|
|
9118
9216
|
|
|
9119
9217
|
`);
|
|
@@ -9202,8 +9300,8 @@ ${bold("protect-mcp status")}
|
|
|
9202
9300
|
process.stdout.write(` ${reason.padEnd(25)} ${count}
|
|
9203
9301
|
`);
|
|
9204
9302
|
}
|
|
9205
|
-
const evidencePath =
|
|
9206
|
-
if (
|
|
9303
|
+
const evidencePath = join9(dir, ".protect-mcp-evidence.json");
|
|
9304
|
+
if (existsSync10(evidencePath)) {
|
|
9207
9305
|
try {
|
|
9208
9306
|
const evidenceRaw = readFileSync11(evidencePath, "utf-8");
|
|
9209
9307
|
const evidence = JSON.parse(evidenceRaw);
|
|
@@ -9214,8 +9312,8 @@ ${bold("protect-mcp status")}
|
|
|
9214
9312
|
} catch {
|
|
9215
9313
|
}
|
|
9216
9314
|
}
|
|
9217
|
-
const keyPath =
|
|
9218
|
-
if (
|
|
9315
|
+
const keyPath = join9(dir, "keys", "gateway.json");
|
|
9316
|
+
if (existsSync10(keyPath)) {
|
|
9219
9317
|
try {
|
|
9220
9318
|
const keyData = JSON.parse(readFileSync11(keyPath, "utf-8"));
|
|
9221
9319
|
if (keyData.publicKey) {
|
|
@@ -9245,7 +9343,7 @@ function commandNeedsValue(argv, flag) {
|
|
|
9245
9343
|
return Boolean(value && !value.startsWith("--"));
|
|
9246
9344
|
}
|
|
9247
9345
|
function absoluteOrCwd(pathValue) {
|
|
9248
|
-
return (0,
|
|
9346
|
+
return (0, import_node_path9.resolve)(process.cwd(), pathValue);
|
|
9249
9347
|
}
|
|
9250
9348
|
function shellQuoteArg(arg) {
|
|
9251
9349
|
if (/^[A-Za-z0-9_./:@%+=,-]+$/.test(arg)) return arg;
|
|
@@ -9264,18 +9362,18 @@ function wrapperArgsFor(command, opts) {
|
|
|
9264
9362
|
}
|
|
9265
9363
|
function claudeDesktopConfigPath() {
|
|
9266
9364
|
if (process.platform === "darwin") {
|
|
9267
|
-
return (0,
|
|
9365
|
+
return (0, import_node_path9.join)((0, import_node_os.homedir)(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
|
|
9268
9366
|
}
|
|
9269
9367
|
if (process.platform === "win32") {
|
|
9270
|
-
return (0,
|
|
9368
|
+
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
9369
|
}
|
|
9272
|
-
return (0,
|
|
9370
|
+
return (0, import_node_path9.join)((0, import_node_os.homedir)(), ".config", "Claude", "claude_desktop_config.json");
|
|
9273
9371
|
}
|
|
9274
9372
|
async function ensureLocalConfig(dir = process.cwd()) {
|
|
9275
|
-
const { existsSync:
|
|
9276
|
-
const { join:
|
|
9277
|
-
const configPath =
|
|
9278
|
-
if (!
|
|
9373
|
+
const { existsSync: existsSync10 } = await import("fs");
|
|
9374
|
+
const { join: join9, resolve } = await import("path");
|
|
9375
|
+
const configPath = join9(dir, "protect-mcp.json");
|
|
9376
|
+
if (!existsSync10(configPath)) {
|
|
9279
9377
|
process.stderr.write(`${bold("protect-mcp wrap")}
|
|
9280
9378
|
|
|
9281
9379
|
No protect-mcp.json found; creating local shadow-mode config first.
|
|
@@ -9287,7 +9385,7 @@ No protect-mcp.json found; creating local shadow-mode config first.
|
|
|
9287
9385
|
}
|
|
9288
9386
|
function parseJsonlFile(pathValue) {
|
|
9289
9387
|
try {
|
|
9290
|
-
const raw = (0,
|
|
9388
|
+
const raw = (0, import_node_fs13.readFileSync)(pathValue, "utf-8");
|
|
9291
9389
|
return raw.split("\n").map((line) => line.trim()).filter(Boolean).flatMap((line) => {
|
|
9292
9390
|
try {
|
|
9293
9391
|
return [JSON.parse(line)];
|
|
@@ -9301,7 +9399,7 @@ function parseJsonlFile(pathValue) {
|
|
|
9301
9399
|
}
|
|
9302
9400
|
function parseJsonlRecords(pathValue) {
|
|
9303
9401
|
try {
|
|
9304
|
-
const raw = (0,
|
|
9402
|
+
const raw = (0, import_node_fs13.readFileSync)(pathValue, "utf-8");
|
|
9305
9403
|
return raw.split("\n").map((line) => line.trim()).filter(Boolean).flatMap((line) => {
|
|
9306
9404
|
try {
|
|
9307
9405
|
return [{
|
|
@@ -9319,8 +9417,8 @@ function parseJsonlRecords(pathValue) {
|
|
|
9319
9417
|
}
|
|
9320
9418
|
function loadPolicyJson(policyPath) {
|
|
9321
9419
|
try {
|
|
9322
|
-
if (!(0,
|
|
9323
|
-
return JSON.parse((0,
|
|
9420
|
+
if (!(0, import_node_fs13.existsSync)(policyPath)) return null;
|
|
9421
|
+
return JSON.parse((0, import_node_fs13.readFileSync)(policyPath, "utf-8"));
|
|
9324
9422
|
} catch {
|
|
9325
9423
|
return null;
|
|
9326
9424
|
}
|
|
@@ -9465,10 +9563,10 @@ function suggestedGuardrailFor(_tool, risk, reasons) {
|
|
|
9465
9563
|
policy: { rate_limit: "100/hour" }
|
|
9466
9564
|
};
|
|
9467
9565
|
}
|
|
9468
|
-
function buildDashboardSummary(dir, policyPath = (0,
|
|
9469
|
-
const logPath = (0,
|
|
9470
|
-
const receiptPath = (0,
|
|
9471
|
-
const keyPath = (0,
|
|
9566
|
+
function buildDashboardSummary(dir, policyPath = (0, import_node_path9.join)(dir, "protect-mcp.json")) {
|
|
9567
|
+
const logPath = (0, import_node_path9.join)(dir, ".protect-mcp-log.jsonl");
|
|
9568
|
+
const receiptPath = (0, import_node_path9.join)(dir, ".protect-mcp-receipts.jsonl");
|
|
9569
|
+
const keyPath = (0, import_node_path9.join)(dir, "keys", "gateway.json");
|
|
9472
9570
|
const entries = parseJsonlFile(logPath);
|
|
9473
9571
|
const receiptRecords = parseJsonlRecords(receiptPath);
|
|
9474
9572
|
const receipts = receiptRecords.map((record) => record.value);
|
|
@@ -9513,9 +9611,9 @@ function buildDashboardSummary(dir, policyPath = (0, import_node_path8.join)(dir
|
|
|
9513
9611
|
const pendingApprovals = entries.filter((e) => e.decision === "require_approval").slice(-25).reverse();
|
|
9514
9612
|
const chains = buildReceiptChains(entries, receiptRecords);
|
|
9515
9613
|
let key = null;
|
|
9516
|
-
if ((0,
|
|
9614
|
+
if ((0, import_node_fs13.existsSync)(keyPath)) {
|
|
9517
9615
|
try {
|
|
9518
|
-
const parsed = JSON.parse((0,
|
|
9616
|
+
const parsed = JSON.parse((0, import_node_fs13.readFileSync)(keyPath, "utf-8"));
|
|
9519
9617
|
key = {
|
|
9520
9618
|
kid: parsed.kid || null,
|
|
9521
9619
|
issuer: parsed.issuer || "protect-mcp",
|
|
@@ -9532,10 +9630,10 @@ function buildDashboardSummary(dir, policyPath = (0, import_node_path8.join)(dir
|
|
|
9532
9630
|
receipts: receiptPath,
|
|
9533
9631
|
key: keyPath,
|
|
9534
9632
|
policy: policyPath,
|
|
9535
|
-
log_exists: (0,
|
|
9536
|
-
receipts_exist: (0,
|
|
9537
|
-
key_exists: (0,
|
|
9538
|
-
policy_exists: (0,
|
|
9633
|
+
log_exists: (0, import_node_fs13.existsSync)(logPath),
|
|
9634
|
+
receipts_exist: (0, import_node_fs13.existsSync)(receiptPath),
|
|
9635
|
+
key_exists: (0, import_node_fs13.existsSync)(keyPath),
|
|
9636
|
+
policy_exists: (0, import_node_fs13.existsSync)(policyPath)
|
|
9539
9637
|
},
|
|
9540
9638
|
totals: {
|
|
9541
9639
|
decisions: entries.length,
|
|
@@ -9572,7 +9670,7 @@ function buildDashboardSummary(dir, policyPath = (0, import_node_path8.join)(dir
|
|
|
9572
9670
|
}))
|
|
9573
9671
|
},
|
|
9574
9672
|
connector_pilots: {
|
|
9575
|
-
directory: (0,
|
|
9673
|
+
directory: (0, import_node_path9.join)(dir, ".protect-mcp", "connectors"),
|
|
9576
9674
|
installed: readInstalledConnectorPilots(dir),
|
|
9577
9675
|
doctor: connectorDoctor(dir),
|
|
9578
9676
|
available: CONNECTOR_PILOTS.map((pilot) => ({
|
|
@@ -9950,7 +10048,7 @@ async function handleDashboard(argv) {
|
|
|
9950
10048
|
const { resolve } = await import("path");
|
|
9951
10049
|
const port = commandNeedsValue(argv, "--port") ? parseInt(flagValue(argv, "--port") || "9877", 10) : 9877;
|
|
9952
10050
|
const dir = resolve(commandNeedsValue(argv, "--dir") ? flagValue(argv, "--dir") || process.cwd() : process.cwd());
|
|
9953
|
-
const policyPath = resolve(flagValue(argv, "--policy") || (0,
|
|
10051
|
+
const policyPath = resolve(flagValue(argv, "--policy") || (0, import_node_path9.join)(dir, "protect-mcp.json"));
|
|
9954
10052
|
const approvalEndpoint = flagValue(argv, "--approval-endpoint");
|
|
9955
10053
|
const approvalNonce = flagValue(argv, "--approval-nonce");
|
|
9956
10054
|
const open = argv.includes("--open");
|
|
@@ -10155,32 +10253,32 @@ function writeToolPolicy(policyPath, tool, action) {
|
|
|
10155
10253
|
tools,
|
|
10156
10254
|
default_tier: existing.default_tier || "unknown"
|
|
10157
10255
|
};
|
|
10158
|
-
(0,
|
|
10256
|
+
(0, import_node_fs13.writeFileSync)(policyPath, JSON.stringify(next, null, 2) + "\n");
|
|
10159
10257
|
return next;
|
|
10160
10258
|
}
|
|
10161
10259
|
function policyPackDirectory(dir) {
|
|
10162
|
-
return (0,
|
|
10260
|
+
return (0, import_node_path9.join)(dir, "cedar");
|
|
10163
10261
|
}
|
|
10164
10262
|
function installedPolicyPackIds(dir) {
|
|
10165
10263
|
const cedarDir = policyPackDirectory(dir);
|
|
10166
10264
|
return POLICY_PACKS.filter(
|
|
10167
|
-
(pack) => pack.files.every((file) => (0,
|
|
10265
|
+
(pack) => pack.files.every((file) => (0, import_node_fs13.existsSync)((0, import_node_path9.join)(cedarDir, file.path)))
|
|
10168
10266
|
).map((pack) => pack.id);
|
|
10169
10267
|
}
|
|
10170
10268
|
function installPolicyPackToDir(dir, packId, force = false) {
|
|
10171
10269
|
const packs = packId === "all" ? POLICY_PACKS : [getPolicyPack(packId)].filter(Boolean);
|
|
10172
10270
|
if (packs.length === 0) throw new Error(`Unknown policy pack: ${packId}`);
|
|
10173
10271
|
const outDir = policyPackDirectory(dir);
|
|
10174
|
-
(0,
|
|
10272
|
+
(0, import_node_fs13.mkdirSync)(outDir, { recursive: true });
|
|
10175
10273
|
const written = [];
|
|
10176
10274
|
for (const pack of packs) {
|
|
10177
10275
|
for (const file of pack.files) {
|
|
10178
|
-
const outPath = (0,
|
|
10179
|
-
if ((0,
|
|
10276
|
+
const outPath = (0, import_node_path9.join)(outDir, file.path);
|
|
10277
|
+
if ((0, import_node_fs13.existsSync)(outPath) && !force) {
|
|
10180
10278
|
throw new Error(`Refusing to overwrite ${outPath}. Pass force=true if intentional.`);
|
|
10181
10279
|
}
|
|
10182
|
-
(0,
|
|
10183
|
-
(0,
|
|
10280
|
+
(0, import_node_fs13.mkdirSync)((0, import_node_path9.dirname)(outPath), { recursive: true });
|
|
10281
|
+
(0, import_node_fs13.writeFileSync)(outPath, file.contents.endsWith("\n") ? file.contents : `${file.contents}
|
|
10184
10282
|
`);
|
|
10185
10283
|
written.push(outPath);
|
|
10186
10284
|
}
|
|
@@ -10188,19 +10286,19 @@ function installPolicyPackToDir(dir, packId, force = false) {
|
|
|
10188
10286
|
return { dir: outDir, written, packs: packs.map((pack) => pack.id) };
|
|
10189
10287
|
}
|
|
10190
10288
|
function dashboardRegistryStatus(dir) {
|
|
10191
|
-
const identityPath = (0,
|
|
10192
|
-
const registryPath = (0,
|
|
10193
|
-
const verifierPath = (0,
|
|
10194
|
-
const identity = (0,
|
|
10289
|
+
const identityPath = (0, import_node_path9.join)(dir, ".protect-mcp-org.json");
|
|
10290
|
+
const registryPath = (0, import_node_path9.join)(dir, ".protect-mcp-registry.json");
|
|
10291
|
+
const verifierPath = (0, import_node_path9.join)(dir, "scopeblind-verifier.html");
|
|
10292
|
+
const identity = (0, import_node_fs13.existsSync)(identityPath) ? (() => {
|
|
10195
10293
|
try {
|
|
10196
|
-
return JSON.parse((0,
|
|
10294
|
+
return JSON.parse((0, import_node_fs13.readFileSync)(identityPath, "utf-8"));
|
|
10197
10295
|
} catch {
|
|
10198
10296
|
return null;
|
|
10199
10297
|
}
|
|
10200
10298
|
})() : null;
|
|
10201
|
-
const registry = (0,
|
|
10299
|
+
const registry = (0, import_node_fs13.existsSync)(registryPath) ? (() => {
|
|
10202
10300
|
try {
|
|
10203
|
-
return JSON.parse((0,
|
|
10301
|
+
return JSON.parse((0, import_node_fs13.readFileSync)(registryPath, "utf-8"));
|
|
10204
10302
|
} catch {
|
|
10205
10303
|
return null;
|
|
10206
10304
|
}
|
|
@@ -10208,9 +10306,9 @@ function dashboardRegistryStatus(dir) {
|
|
|
10208
10306
|
const anchors = Array.isArray(registry?.anchors) ? registry.anchors : [];
|
|
10209
10307
|
const hosted = anchors.some((anchor) => anchor.timestamp_source === "scopeblind-hosted");
|
|
10210
10308
|
return {
|
|
10211
|
-
identity_exists: (0,
|
|
10212
|
-
registry_exists: (0,
|
|
10213
|
-
verifier_exists: (0,
|
|
10309
|
+
identity_exists: (0, import_node_fs13.existsSync)(identityPath),
|
|
10310
|
+
registry_exists: (0, import_node_fs13.existsSync)(registryPath),
|
|
10311
|
+
verifier_exists: (0, import_node_fs13.existsSync)(verifierPath),
|
|
10214
10312
|
identity_path: identityPath,
|
|
10215
10313
|
registry_path: registryPath,
|
|
10216
10314
|
verifier_path: verifierPath,
|
|
@@ -10231,13 +10329,13 @@ async function readJsonBody(req) {
|
|
|
10231
10329
|
}
|
|
10232
10330
|
async function buildAuditBundleForDir(dir) {
|
|
10233
10331
|
const { createAuditBundle: createAuditBundle2 } = await Promise.resolve().then(() => (init_bundle(), bundle_exports));
|
|
10234
|
-
const receiptPath = (0,
|
|
10235
|
-
const keyPath = (0,
|
|
10236
|
-
if (!(0,
|
|
10237
|
-
if (!(0,
|
|
10332
|
+
const receiptPath = (0, import_node_path9.join)(dir, ".protect-mcp-receipts.jsonl");
|
|
10333
|
+
const keyPath = (0, import_node_path9.join)(dir, "keys", "gateway.json");
|
|
10334
|
+
if (!(0, import_node_fs13.existsSync)(receiptPath)) throw new Error("No receipt file found.");
|
|
10335
|
+
if (!(0, import_node_fs13.existsSync)(keyPath)) throw new Error("No signing key found.");
|
|
10238
10336
|
const receipts = parseJsonlFile(receiptPath);
|
|
10239
10337
|
if (receipts.length === 0) throw new Error("No signed receipts found.");
|
|
10240
|
-
const keyData = JSON.parse((0,
|
|
10338
|
+
const keyData = JSON.parse((0, import_node_fs13.readFileSync)(keyPath, "utf-8"));
|
|
10241
10339
|
return createAuditBundle2({
|
|
10242
10340
|
tenant: keyData.issuer || "protect-mcp",
|
|
10243
10341
|
receipts,
|
|
@@ -10255,17 +10353,17 @@ function collectSelectiveDisclosurePackages(dir) {
|
|
|
10255
10353
|
const out = [];
|
|
10256
10354
|
const seen = /* @__PURE__ */ new Set();
|
|
10257
10355
|
const candidates = [];
|
|
10258
|
-
const receiptsDir = (0,
|
|
10259
|
-
if ((0,
|
|
10260
|
-
for (const name of (0,
|
|
10356
|
+
const receiptsDir = (0, import_node_path9.join)(dir, "receipts");
|
|
10357
|
+
if ((0, import_node_fs13.existsSync)(receiptsDir)) {
|
|
10358
|
+
for (const name of (0, import_node_fs13.readdirSync)(receiptsDir)) {
|
|
10261
10359
|
if (name.includes("selective-disclosure") && name.endsWith(".json")) {
|
|
10262
|
-
candidates.push((0,
|
|
10360
|
+
candidates.push((0, import_node_path9.join)(receiptsDir, name));
|
|
10263
10361
|
}
|
|
10264
10362
|
}
|
|
10265
10363
|
}
|
|
10266
|
-
const jsonlPath = (0,
|
|
10267
|
-
if ((0,
|
|
10268
|
-
for (const line of (0,
|
|
10364
|
+
const jsonlPath = (0, import_node_path9.join)(dir, ".protect-mcp-selective-disclosures.jsonl");
|
|
10365
|
+
if ((0, import_node_fs13.existsSync)(jsonlPath)) {
|
|
10366
|
+
for (const line of (0, import_node_fs13.readFileSync)(jsonlPath, "utf-8").split("\n").map((s) => s.trim()).filter(Boolean)) {
|
|
10269
10367
|
try {
|
|
10270
10368
|
const parsed = JSON.parse(line);
|
|
10271
10369
|
addSelectiveDisclosure(out, seen, parsed);
|
|
@@ -10275,7 +10373,7 @@ function collectSelectiveDisclosurePackages(dir) {
|
|
|
10275
10373
|
}
|
|
10276
10374
|
for (const path of candidates) {
|
|
10277
10375
|
try {
|
|
10278
|
-
const parsed = JSON.parse((0,
|
|
10376
|
+
const parsed = JSON.parse((0, import_node_fs13.readFileSync)(path, "utf-8"));
|
|
10279
10377
|
addSelectiveDisclosure(out, seen, parsed);
|
|
10280
10378
|
} catch {
|
|
10281
10379
|
}
|
|
@@ -10308,7 +10406,7 @@ async function recordApprovalResolution(opts) {
|
|
|
10308
10406
|
takeover_note: opts.body.takeover_note || void 0,
|
|
10309
10407
|
payload_hash: opts.body.payload_hash || void 0
|
|
10310
10408
|
};
|
|
10311
|
-
(0,
|
|
10409
|
+
(0, import_node_fs13.appendFileSync)((0, import_node_path9.join)(opts.dir, ".protect-mcp-approval-resolutions.jsonl"), JSON.stringify(record) + "\n");
|
|
10312
10410
|
let forwarded = null;
|
|
10313
10411
|
if (resolution === "approve" && opts.approvalEndpoint && opts.approvalNonce) {
|
|
10314
10412
|
const endpoint = opts.approvalEndpoint.replace(/\/$/, "") + "/approve";
|
|
@@ -10331,7 +10429,7 @@ async function recordApprovalResolution(opts) {
|
|
|
10331
10429
|
return { recorded: true, resolution: record, forwarded };
|
|
10332
10430
|
}
|
|
10333
10431
|
async function handleRecommend(argv) {
|
|
10334
|
-
const { writeFileSync:
|
|
10432
|
+
const { writeFileSync: writeFileSync5 } = await import("fs");
|
|
10335
10433
|
const { resolve } = await import("path");
|
|
10336
10434
|
const dir = resolve(commandNeedsValue(argv, "--dir") ? flagValue(argv, "--dir") || process.cwd() : process.cwd());
|
|
10337
10435
|
const outputPath = resolve(flagValue(argv, "--output") || "protect-mcp.recommended.json");
|
|
@@ -10380,7 +10478,7 @@ Dry run only. Write the policy with:
|
|
|
10380
10478
|
process.stdout.write(dim(body));
|
|
10381
10479
|
return;
|
|
10382
10480
|
}
|
|
10383
|
-
|
|
10481
|
+
writeFileSync5(outputPath, body);
|
|
10384
10482
|
process.stdout.write(`
|
|
10385
10483
|
${green("\u2713 Wrote recommended policy")}
|
|
10386
10484
|
`);
|
|
@@ -10393,7 +10491,7 @@ ${green("\u2713 Wrote recommended policy")}
|
|
|
10393
10491
|
`);
|
|
10394
10492
|
}
|
|
10395
10493
|
async function handleWrap(argv) {
|
|
10396
|
-
const { existsSync:
|
|
10494
|
+
const { existsSync: existsSync10, readFileSync: readFileSync11, writeFileSync: writeFileSync5 } = await import("fs");
|
|
10397
10495
|
const { resolve } = await import("path");
|
|
10398
10496
|
const configFlag = flagValue(argv, "--config");
|
|
10399
10497
|
const cedarFlag = flagValue(argv, "--cedar");
|
|
@@ -10430,7 +10528,7 @@ ${bold("protect-mcp wrap")}
|
|
|
10430
10528
|
return;
|
|
10431
10529
|
}
|
|
10432
10530
|
const claudePath = resolve(flagValue(argv, "--path") || claudeDesktopConfigPath());
|
|
10433
|
-
if (!claudeDesktop && !
|
|
10531
|
+
if (!claudeDesktop && !existsSync10(claudePath)) {
|
|
10434
10532
|
process.stdout.write(`
|
|
10435
10533
|
${bold("protect-mcp wrap")}
|
|
10436
10534
|
|
|
@@ -10447,7 +10545,7 @@ ${bold("protect-mcp wrap")}
|
|
|
10447
10545
|
`);
|
|
10448
10546
|
return;
|
|
10449
10547
|
}
|
|
10450
|
-
if (!
|
|
10548
|
+
if (!existsSync10(claudePath)) {
|
|
10451
10549
|
process.stderr.write(`protect-mcp wrap: Claude Desktop config not found at ${claudePath}
|
|
10452
10550
|
`);
|
|
10453
10551
|
process.exit(1);
|
|
@@ -10518,8 +10616,8 @@ Dry run only. Apply with:
|
|
|
10518
10616
|
return;
|
|
10519
10617
|
}
|
|
10520
10618
|
const backupPath = `${claudePath}.bak.${Date.now()}`;
|
|
10521
|
-
|
|
10522
|
-
|
|
10619
|
+
writeFileSync5(backupPath, readFileSync11(claudePath, "utf-8"));
|
|
10620
|
+
writeFileSync5(claudePath, JSON.stringify(next, null, 2) + "\n");
|
|
10523
10621
|
process.stdout.write(`
|
|
10524
10622
|
${green("\u2713 Claude Desktop config updated")}
|
|
10525
10623
|
`);
|
|
@@ -10545,14 +10643,14 @@ function yellow(s) {
|
|
|
10545
10643
|
return process.env.NO_COLOR ? s : `\x1B[33m${s}\x1B[0m`;
|
|
10546
10644
|
}
|
|
10547
10645
|
async function handleDigest(argv) {
|
|
10548
|
-
const { readFileSync: readFileSync11, existsSync:
|
|
10549
|
-
const { join:
|
|
10646
|
+
const { readFileSync: readFileSync11, existsSync: existsSync10 } = await import("fs");
|
|
10647
|
+
const { join: join9 } = await import("path");
|
|
10550
10648
|
let dir = process.cwd();
|
|
10551
10649
|
const dirIdx = argv.indexOf("--dir");
|
|
10552
10650
|
if (dirIdx !== -1 && argv[dirIdx + 1]) dir = argv[dirIdx + 1];
|
|
10553
10651
|
const today = argv.includes("--today");
|
|
10554
|
-
const logPath =
|
|
10555
|
-
if (!
|
|
10652
|
+
const logPath = join9(dir, ".protect-mcp-log.jsonl");
|
|
10653
|
+
if (!existsSync10(logPath)) {
|
|
10556
10654
|
process.stderr.write(`${bold("protect-mcp digest")}
|
|
10557
10655
|
|
|
10558
10656
|
No log file found. Run protect-mcp first.
|
|
@@ -10636,15 +10734,15 @@ ${bold("\u{1F6E1}\uFE0F Agent Daily Digest")}
|
|
|
10636
10734
|
`);
|
|
10637
10735
|
}
|
|
10638
10736
|
async function handleReceipts2(argv) {
|
|
10639
|
-
const { readFileSync: readFileSync11, existsSync:
|
|
10640
|
-
const { join:
|
|
10737
|
+
const { readFileSync: readFileSync11, existsSync: existsSync10 } = await import("fs");
|
|
10738
|
+
const { join: join9 } = await import("path");
|
|
10641
10739
|
let dir = process.cwd();
|
|
10642
10740
|
const dirIdx = argv.indexOf("--dir");
|
|
10643
10741
|
if (dirIdx !== -1 && argv[dirIdx + 1]) dir = argv[dirIdx + 1];
|
|
10644
10742
|
const lastIdx = argv.indexOf("--last");
|
|
10645
10743
|
const count = lastIdx !== -1 && argv[lastIdx + 1] ? parseInt(argv[lastIdx + 1], 10) : 20;
|
|
10646
|
-
const receiptsPath =
|
|
10647
|
-
if (!
|
|
10744
|
+
const receiptsPath = join9(dir, ".protect-mcp-receipts.jsonl");
|
|
10745
|
+
if (!existsSync10(receiptsPath)) {
|
|
10648
10746
|
process.stderr.write(`${bold("protect-mcp receipts")}
|
|
10649
10747
|
|
|
10650
10748
|
No signed receipt file found. Run protect-mcp with signing enabled first.
|
|
@@ -10678,19 +10776,19 @@ async function pkgVersion() {
|
|
|
10678
10776
|
if (_pkgV) return _pkgV;
|
|
10679
10777
|
let v = "0.0.0";
|
|
10680
10778
|
try {
|
|
10681
|
-
const { readFileSync: readFileSync11, existsSync:
|
|
10682
|
-
const { dirname: dirname3, join:
|
|
10779
|
+
const { readFileSync: readFileSync11, existsSync: existsSync10, realpathSync } = await import("fs");
|
|
10780
|
+
const { dirname: dirname3, join: join9, resolve } = await import("path");
|
|
10683
10781
|
let base = "";
|
|
10684
10782
|
try {
|
|
10685
10783
|
base = dirname3(realpathSync(resolve(process.argv[1] || "")));
|
|
10686
10784
|
} catch {
|
|
10687
10785
|
}
|
|
10688
10786
|
const candidates = [
|
|
10689
|
-
base ?
|
|
10690
|
-
base ?
|
|
10787
|
+
base ? join9(base, "..", "package.json") : "",
|
|
10788
|
+
base ? join9(base, "package.json") : ""
|
|
10691
10789
|
].filter(Boolean);
|
|
10692
10790
|
for (const p of candidates) {
|
|
10693
|
-
if (
|
|
10791
|
+
if (existsSync10(p)) {
|
|
10694
10792
|
const parsed = JSON.parse(readFileSync11(p, "utf-8"));
|
|
10695
10793
|
if (parsed && parsed.name === "protect-mcp" && parsed.version) {
|
|
10696
10794
|
v = parsed.version;
|
|
@@ -10727,16 +10825,16 @@ function mapRecordEntry(e) {
|
|
|
10727
10825
|
return { ts, tool, verdict, reason, hook, signed, caps, agent, dur, id: String(e.request_id || p.request_id || ""), digest, raw: e };
|
|
10728
10826
|
}
|
|
10729
10827
|
async function handleRecord(argv) {
|
|
10730
|
-
const { readFileSync: readFileSync11, existsSync:
|
|
10731
|
-
const { join:
|
|
10828
|
+
const { readFileSync: readFileSync11, existsSync: existsSync10, writeFileSync: writeFileSync5 } = await import("fs");
|
|
10829
|
+
const { join: join9 } = await import("path");
|
|
10732
10830
|
const osMod = await import("os");
|
|
10733
10831
|
const cp = await import("child_process");
|
|
10734
10832
|
let dir = process.cwd();
|
|
10735
10833
|
const di = argv.indexOf("--dir");
|
|
10736
10834
|
if (di !== -1 && argv[di + 1]) dir = argv[di + 1];
|
|
10737
|
-
const recPath =
|
|
10738
|
-
const logPath =
|
|
10739
|
-
const pick = () =>
|
|
10835
|
+
const recPath = join9(dir, ".protect-mcp-receipts.jsonl");
|
|
10836
|
+
const logPath = join9(dir, ".protect-mcp-log.jsonl");
|
|
10837
|
+
const pick = () => existsSync10(recPath) ? recPath : existsSync10(logPath) ? logPath : null;
|
|
10740
10838
|
const chosen = pick();
|
|
10741
10839
|
if (!chosen) {
|
|
10742
10840
|
process.stderr.write(`
|
|
@@ -10761,7 +10859,7 @@ Start the gate with ${bold("npx protect-mcp serve")}, use your agent, then run t
|
|
|
10761
10859
|
let pinnedKey = "";
|
|
10762
10860
|
let pinnedKid = "";
|
|
10763
10861
|
try {
|
|
10764
|
-
const kd = JSON.parse(readFileSync11(
|
|
10862
|
+
const kd = JSON.parse(readFileSync11(join9(dir, "keys", "gateway.json"), "utf-8"));
|
|
10765
10863
|
if (kd && typeof kd.publicKey === "string" && /^[0-9a-f]{64}$/i.test(kd.publicKey)) {
|
|
10766
10864
|
pinnedKey = kd.publicKey;
|
|
10767
10865
|
pinnedKid = typeof kd.kid === "string" ? kd.kid : "";
|
|
@@ -10822,8 +10920,8 @@ ${bold("\u{1F6E1}\uFE0F Your record")} ${dim("\xB7")} live at ${url}
|
|
|
10822
10920
|
const recs = readRecs(chosen);
|
|
10823
10921
|
const meta = { file: chosen, signed: chosen === recPath, count: recs.length, live: false, pinned_key: pinnedKey, pinned_kid: pinnedKid };
|
|
10824
10922
|
const html = RECORD_HTML.replace("__DATA__", () => JSON.stringify(recs)).replace("__META__", () => JSON.stringify(meta));
|
|
10825
|
-
const out =
|
|
10826
|
-
|
|
10923
|
+
const out = join9(osMod.tmpdir(), "protect-mcp-record-" + Date.now() + ".html");
|
|
10924
|
+
writeFileSync5(out, html);
|
|
10827
10925
|
openTarget(out);
|
|
10828
10926
|
process.stdout.write(`
|
|
10829
10927
|
${bold("\u{1F6E1}\uFE0F Your record")} ${dim("\xB7")} ${recs.length} decision${recs.length === 1 ? "" : "s"}, all on this machine
|
|
@@ -10996,8 +11094,8 @@ render();kickVerify();
|
|
|
10996
11094
|
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
11095
|
</script></body></html>`;
|
|
10998
11096
|
async function handleClaim(argv) {
|
|
10999
|
-
const { readFileSync: readFileSync11, existsSync:
|
|
11000
|
-
const { join:
|
|
11097
|
+
const { readFileSync: readFileSync11, existsSync: existsSync10, writeFileSync: writeFileSync5 } = await import("fs");
|
|
11098
|
+
const { join: join9 } = await import("path");
|
|
11001
11099
|
const { buildClaim: buildClaim2 } = await Promise.resolve().then(() => (init_claim(), claim_exports));
|
|
11002
11100
|
let dir = process.cwd();
|
|
11003
11101
|
const di = argv.indexOf("--dir");
|
|
@@ -11030,8 +11128,8 @@ Example: ${bold("npx protect-mcp claim --no net.egress --anchor")}
|
|
|
11030
11128
|
process.exit(0);
|
|
11031
11129
|
return;
|
|
11032
11130
|
}
|
|
11033
|
-
const keyPath =
|
|
11034
|
-
if (!
|
|
11131
|
+
const keyPath = join9(dir, "keys", "gateway.json");
|
|
11132
|
+
if (!existsSync10(keyPath)) {
|
|
11035
11133
|
process.stderr.write(`
|
|
11036
11134
|
${bold("protect-mcp claim")}
|
|
11037
11135
|
|
|
@@ -11060,8 +11158,8 @@ protect-mcp claim: ${keyPath} is missing privateKey/publicKey.
|
|
|
11060
11158
|
process.exit(1);
|
|
11061
11159
|
return;
|
|
11062
11160
|
}
|
|
11063
|
-
const recPath =
|
|
11064
|
-
if (!
|
|
11161
|
+
const recPath = join9(dir, ".protect-mcp-receipts.jsonl");
|
|
11162
|
+
if (!existsSync10(recPath)) {
|
|
11065
11163
|
process.stderr.write(`
|
|
11066
11164
|
${bold("protect-mcp claim")}
|
|
11067
11165
|
|
|
@@ -11088,8 +11186,8 @@ protect-mcp claim: no readable receipts in ${recPath}.
|
|
|
11088
11186
|
}
|
|
11089
11187
|
const pack = buildClaim2(receipts, predicate, { privateKey: key.privateKey, publicKey: key.publicKey, kid: key.kid || "gateway", issuer: "protect-mcp" }, (/* @__PURE__ */ new Date()).toISOString());
|
|
11090
11188
|
const oi = argv.indexOf("--output");
|
|
11091
|
-
const out = oi !== -1 && argv[oi + 1] ? argv[oi + 1] :
|
|
11092
|
-
|
|
11189
|
+
const out = oi !== -1 && argv[oi + 1] ? argv[oi + 1] : join9(dir, "claim-" + Date.now() + ".json");
|
|
11190
|
+
writeFileSync5(out, JSON.stringify(pack, null, 2) + "\n");
|
|
11093
11191
|
process.stdout.write(`
|
|
11094
11192
|
${bold("\u{1F6E1}\uFE0F Signed claim")}
|
|
11095
11193
|
`);
|
|
@@ -11115,7 +11213,7 @@ ${bold("\u{1F6E1}\uFE0F Signed claim")}
|
|
|
11115
11213
|
);
|
|
11116
11214
|
if (res.ok) {
|
|
11117
11215
|
const sidecar = out.replace(/\.json$/, "") + ".anchor.json";
|
|
11118
|
-
|
|
11216
|
+
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
11217
|
process.stdout.write(` ${green("Anchored")} as log entry ${bold("#" + res.seq)}${res.already_anchored ? dim(" (already present)") : ""} ${dim(res.entry_url || "")}
|
|
11120
11218
|
`);
|
|
11121
11219
|
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 +11242,16 @@ ${bold("\u{1F6E1}\uFE0F Signed claim")}
|
|
|
11144
11242
|
process.exit(0);
|
|
11145
11243
|
}
|
|
11146
11244
|
async function handleAnchorRecord(argv) {
|
|
11147
|
-
const { readFileSync: readFileSync11, existsSync:
|
|
11148
|
-
const { join:
|
|
11245
|
+
const { readFileSync: readFileSync11, existsSync: existsSync10, appendFileSync: appendFileSync3 } = await import("fs");
|
|
11246
|
+
const { join: join9 } = await import("path");
|
|
11149
11247
|
const { anchorRecordCheckpoint: anchorRecordCheckpoint2, buildRecordCheckpoint: buildRecordCheckpoint2, lookupPinnedIdentity: lookupPinnedIdentity2 } = await Promise.resolve().then(() => (init_claim(), claim_exports));
|
|
11150
11248
|
let dir = process.cwd();
|
|
11151
11249
|
const di = argv.indexOf("--dir");
|
|
11152
11250
|
if (di !== -1 && argv[di + 1]) dir = argv[di + 1];
|
|
11153
11251
|
const li = argv.indexOf("--log");
|
|
11154
11252
|
const logBase = li !== -1 && argv[li + 1] ? argv[li + 1] : void 0;
|
|
11155
|
-
const keyPath =
|
|
11156
|
-
if (!
|
|
11253
|
+
const keyPath = join9(dir, "keys", "gateway.json");
|
|
11254
|
+
if (!existsSync10(keyPath)) {
|
|
11157
11255
|
process.stderr.write(`
|
|
11158
11256
|
${bold("protect-mcp anchor-record")}
|
|
11159
11257
|
|
|
@@ -11182,8 +11280,8 @@ protect-mcp anchor-record: ${keyPath} is missing privateKey/publicKey.
|
|
|
11182
11280
|
process.exit(1);
|
|
11183
11281
|
return;
|
|
11184
11282
|
}
|
|
11185
|
-
const recPath =
|
|
11186
|
-
if (!
|
|
11283
|
+
const recPath = join9(dir, ".protect-mcp-receipts.jsonl");
|
|
11284
|
+
if (!existsSync10(recPath)) {
|
|
11187
11285
|
process.stderr.write(`
|
|
11188
11286
|
${bold("protect-mcp anchor-record")}
|
|
11189
11287
|
|
|
@@ -11209,9 +11307,9 @@ protect-mcp anchor-record: no readable receipts in ${recPath}.
|
|
|
11209
11307
|
return;
|
|
11210
11308
|
}
|
|
11211
11309
|
const claimKey = { privateKey: key.privateKey, publicKey: key.publicKey, kid: key.kid || "gateway", issuer: "protect-mcp" };
|
|
11212
|
-
const historyPath =
|
|
11310
|
+
const historyPath = join9(dir, ".protect-mcp-anchors.jsonl");
|
|
11213
11311
|
const preview = buildRecordCheckpoint2(receipts, claimKey, "preview");
|
|
11214
|
-
if (!argv.includes("--force") &&
|
|
11312
|
+
if (!argv.includes("--force") && existsSync10(historyPath)) {
|
|
11215
11313
|
const lines = readFileSync11(historyPath, "utf-8").split(/\r?\n/).filter(Boolean);
|
|
11216
11314
|
const last = lines.length ? (() => {
|
|
11217
11315
|
try {
|
|
@@ -11270,10 +11368,10 @@ ${bold("\u{1F6E1}\uFE0F Record checkpoint")}
|
|
|
11270
11368
|
process.exit(0);
|
|
11271
11369
|
}
|
|
11272
11370
|
async function handleVerifyClaim(argv) {
|
|
11273
|
-
const { readFileSync: readFileSync11, existsSync:
|
|
11371
|
+
const { readFileSync: readFileSync11, existsSync: existsSync10 } = await import("fs");
|
|
11274
11372
|
const { verifyClaim: verifyClaim2 } = await Promise.resolve().then(() => (init_claim(), claim_exports));
|
|
11275
11373
|
const file = argv.find((a) => !a.startsWith("--"));
|
|
11276
|
-
if (!file || !
|
|
11374
|
+
if (!file || !existsSync10(file)) {
|
|
11277
11375
|
process.stderr.write(`
|
|
11278
11376
|
${bold("protect-mcp verify-claim")} <claim.json> [--key <public-hex>]
|
|
11279
11377
|
|
|
@@ -11322,7 +11420,7 @@ ${bold("protect-mcp verify-claim")}
|
|
|
11322
11420
|
const sidecarPath = ai !== -1 && argv[ai + 1] ? argv[ai + 1] : file.replace(/\.json$/, "") + ".anchor.json";
|
|
11323
11421
|
const requireAnchor = argv.includes("--check-anchor");
|
|
11324
11422
|
let anchorOk = true;
|
|
11325
|
-
if (
|
|
11423
|
+
if (existsSync10(sidecarPath)) {
|
|
11326
11424
|
const { checkClaimAnchor: checkClaimAnchor2 } = await Promise.resolve().then(() => (init_claim(), claim_exports));
|
|
11327
11425
|
let sidecar = null;
|
|
11328
11426
|
try {
|
|
@@ -11393,24 +11491,24 @@ ${bold("protect-mcp verify-claim")}
|
|
|
11393
11491
|
process.exit(finalValid ? 0 : 1);
|
|
11394
11492
|
}
|
|
11395
11493
|
async function handleBundle(argv) {
|
|
11396
|
-
const { readFileSync: readFileSync11, writeFileSync:
|
|
11397
|
-
const { join:
|
|
11494
|
+
const { readFileSync: readFileSync11, writeFileSync: writeFileSync5, existsSync: existsSync10 } = await import("fs");
|
|
11495
|
+
const { join: join9 } = await import("path");
|
|
11398
11496
|
const { createAuditBundle: createAuditBundle2 } = await Promise.resolve().then(() => (init_bundle(), bundle_exports));
|
|
11399
11497
|
let dir = process.cwd();
|
|
11400
11498
|
const dirIdx = argv.indexOf("--dir");
|
|
11401
11499
|
if (dirIdx !== -1 && argv[dirIdx + 1]) dir = argv[dirIdx + 1];
|
|
11402
11500
|
const outputIdx = argv.indexOf("--output");
|
|
11403
|
-
const outputPath = outputIdx !== -1 && argv[outputIdx + 1] ? argv[outputIdx + 1] :
|
|
11404
|
-
const receiptsPath =
|
|
11405
|
-
const keyPath =
|
|
11406
|
-
if (!
|
|
11501
|
+
const outputPath = outputIdx !== -1 && argv[outputIdx + 1] ? argv[outputIdx + 1] : join9(dir, "audit-bundle.json");
|
|
11502
|
+
const receiptsPath = join9(dir, ".protect-mcp-receipts.jsonl");
|
|
11503
|
+
const keyPath = join9(dir, "keys", "gateway.json");
|
|
11504
|
+
if (!existsSync10(receiptsPath)) {
|
|
11407
11505
|
process.stderr.write(`${bold("protect-mcp bundle")}
|
|
11408
11506
|
|
|
11409
11507
|
No signed receipt file found. Run protect-mcp with signing enabled first.
|
|
11410
11508
|
`);
|
|
11411
11509
|
process.exit(0);
|
|
11412
11510
|
}
|
|
11413
|
-
if (!
|
|
11511
|
+
if (!existsSync10(keyPath)) {
|
|
11414
11512
|
process.stderr.write(`${bold("protect-mcp bundle")}
|
|
11415
11513
|
|
|
11416
11514
|
No key file found at ${keyPath}
|
|
@@ -11431,7 +11529,7 @@ No key file found at ${keyPath}
|
|
|
11431
11529
|
use: "sig"
|
|
11432
11530
|
}]
|
|
11433
11531
|
});
|
|
11434
|
-
|
|
11532
|
+
writeFileSync5(outputPath, JSON.stringify(bundle, null, 2) + "\n");
|
|
11435
11533
|
process.stdout.write(`
|
|
11436
11534
|
${bold("protect-mcp bundle")}
|
|
11437
11535
|
|
|
@@ -11447,8 +11545,8 @@ ${bold("protect-mcp bundle")}
|
|
|
11447
11545
|
`);
|
|
11448
11546
|
}
|
|
11449
11547
|
async function createSandbox() {
|
|
11450
|
-
const { mkdirSync:
|
|
11451
|
-
const { join:
|
|
11548
|
+
const { mkdirSync: mkdirSync4, writeFileSync: writeFileSync5, existsSync: existsSync10, readFileSync: readFileSync11 } = await import("fs");
|
|
11549
|
+
const { join: join9 } = await import("path");
|
|
11452
11550
|
const { homedir } = await import("os");
|
|
11453
11551
|
let response;
|
|
11454
11552
|
try {
|
|
@@ -11478,19 +11576,19 @@ async function createSandbox() {
|
|
|
11478
11576
|
return null;
|
|
11479
11577
|
}
|
|
11480
11578
|
const dashboardUrl = `https://scopeblind.com/t/${data.slug}`;
|
|
11481
|
-
const configDir =
|
|
11482
|
-
if (!
|
|
11483
|
-
|
|
11579
|
+
const configDir = join9(homedir(), ".protect-mcp");
|
|
11580
|
+
if (!existsSync10(configDir)) {
|
|
11581
|
+
mkdirSync4(configDir, { recursive: true });
|
|
11484
11582
|
}
|
|
11485
|
-
const configPath =
|
|
11583
|
+
const configPath = join9(configDir, "config.json");
|
|
11486
11584
|
let existing = {};
|
|
11487
|
-
if (
|
|
11585
|
+
if (existsSync10(configPath)) {
|
|
11488
11586
|
try {
|
|
11489
11587
|
existing = JSON.parse(readFileSync11(configPath, "utf-8"));
|
|
11490
11588
|
} catch {
|
|
11491
11589
|
}
|
|
11492
11590
|
}
|
|
11493
|
-
|
|
11591
|
+
writeFileSync5(configPath, JSON.stringify({
|
|
11494
11592
|
...existing,
|
|
11495
11593
|
sandbox_slug: data.slug,
|
|
11496
11594
|
dashboard_url: dashboardUrl
|
|
@@ -11523,10 +11621,10 @@ ${"\u2500".repeat(50)}
|
|
|
11523
11621
|
}
|
|
11524
11622
|
async function handleQuickstart(argv) {
|
|
11525
11623
|
const connectFlag = argv.includes("--connect");
|
|
11526
|
-
const { mkdtempSync, writeFileSync:
|
|
11527
|
-
const { join:
|
|
11624
|
+
const { mkdtempSync, writeFileSync: writeFileSync5, existsSync: existsSync10, mkdirSync: mkdirSync4, readFileSync: readFileSync11 } = await import("fs");
|
|
11625
|
+
const { join: join9 } = await import("path");
|
|
11528
11626
|
const { tmpdir } = await import("os");
|
|
11529
|
-
const dir = mkdtempSync(
|
|
11627
|
+
const dir = mkdtempSync(join9(tmpdir(), "protect-mcp-quickstart-"));
|
|
11530
11628
|
process.stdout.write(`
|
|
11531
11629
|
${bold("protect-mcp quickstart")}
|
|
11532
11630
|
`);
|
|
@@ -11551,8 +11649,8 @@ ${bold("protect-mcp quickstart")}
|
|
|
11551
11649
|
Working dir: ${dir}
|
|
11552
11650
|
|
|
11553
11651
|
`);
|
|
11554
|
-
const keysDir =
|
|
11555
|
-
|
|
11652
|
+
const keysDir = join9(dir, "keys");
|
|
11653
|
+
mkdirSync4(keysDir, { recursive: true });
|
|
11556
11654
|
const { randomBytes: randomBytes4 } = await import("crypto");
|
|
11557
11655
|
let keypair;
|
|
11558
11656
|
try {
|
|
@@ -11572,13 +11670,13 @@ ${bold("protect-mcp quickstart")}
|
|
|
11572
11670
|
kid: `quickstart-${Date.now()}`
|
|
11573
11671
|
};
|
|
11574
11672
|
}
|
|
11575
|
-
|
|
11673
|
+
writeFileSync5(join9(keysDir, "gateway.json"), JSON.stringify({
|
|
11576
11674
|
privateKey: keypair.privateKey,
|
|
11577
11675
|
publicKey: keypair.publicKey,
|
|
11578
11676
|
kid: keypair.kid,
|
|
11579
11677
|
generated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
11580
11678
|
}, null, 2) + "\n");
|
|
11581
|
-
const configPath =
|
|
11679
|
+
const configPath = join9(dir, "protect-mcp.json");
|
|
11582
11680
|
const config = {
|
|
11583
11681
|
tools: {
|
|
11584
11682
|
"*": { rate_limit: "100/hour" },
|
|
@@ -11586,12 +11684,12 @@ ${bold("protect-mcp quickstart")}
|
|
|
11586
11684
|
},
|
|
11587
11685
|
default_tier: "unknown",
|
|
11588
11686
|
signing: {
|
|
11589
|
-
key_path:
|
|
11687
|
+
key_path: join9(keysDir, "gateway.json"),
|
|
11590
11688
|
issuer: "protect-mcp-quickstart",
|
|
11591
11689
|
enabled: true
|
|
11592
11690
|
}
|
|
11593
11691
|
};
|
|
11594
|
-
|
|
11692
|
+
writeFileSync5(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
11595
11693
|
process.stdout.write(` \u2713 Keypair generated (kid: ${keypair.kid})
|
|
11596
11694
|
`);
|
|
11597
11695
|
process.stdout.write(` \u2713 Policy created (shadow mode, all tools logged)
|
|
@@ -11606,7 +11704,7 @@ ${bold("protect-mcp quickstart")}
|
|
|
11606
11704
|
const dashboardUrl = await createSandbox();
|
|
11607
11705
|
if (dashboardUrl) {
|
|
11608
11706
|
const updatedConfig = { ...config, dashboard_url: dashboardUrl };
|
|
11609
|
-
|
|
11707
|
+
writeFileSync5(configPath, JSON.stringify(updatedConfig, null, 2) + "\n");
|
|
11610
11708
|
process.stdout.write(green(` \u2713 Dashboard created: ${dashboardUrl}
|
|
11611
11709
|
`));
|
|
11612
11710
|
process.stdout.write(` Receipts will be uploaded automatically.
|
|
@@ -11642,7 +11740,7 @@ ${bold("protect-mcp quickstart")}
|
|
|
11642
11740
|
}
|
|
11643
11741
|
async function handleRegistry(argv) {
|
|
11644
11742
|
const subcommand = argv[0] || "status";
|
|
11645
|
-
const dir = (0,
|
|
11743
|
+
const dir = (0, import_node_path9.resolve)(flagValue(argv, "--dir") || process.cwd());
|
|
11646
11744
|
const orgName = flagValue(argv, "--org") || process.env.SCOPEBLIND_ORG;
|
|
11647
11745
|
const orgId = flagValue(argv, "--org-id") || process.env.SCOPEBLIND_ORG_ID;
|
|
11648
11746
|
const billingAccountId = flagValue(argv, "--billing-account") || process.env.SCOPEBLIND_BILLING_ACCOUNT;
|
|
@@ -11724,14 +11822,14 @@ ${bold("protect-mcp registry anchor")}
|
|
|
11724
11822
|
return;
|
|
11725
11823
|
}
|
|
11726
11824
|
if (subcommand === "status") {
|
|
11727
|
-
const registryPath = (0,
|
|
11728
|
-
const identityPath = (0,
|
|
11825
|
+
const registryPath = (0, import_node_path9.join)(dir, registryMod.REGISTRY_FILE);
|
|
11826
|
+
const identityPath = (0, import_node_path9.join)(dir, registryMod.ORG_IDENTITY_FILE);
|
|
11729
11827
|
process.stdout.write(`
|
|
11730
11828
|
${bold("protect-mcp registry status")}
|
|
11731
11829
|
|
|
11732
11830
|
`);
|
|
11733
|
-
if ((0,
|
|
11734
|
-
const identity = JSON.parse((0,
|
|
11831
|
+
if ((0, import_node_fs13.existsSync)(identityPath)) {
|
|
11832
|
+
const identity = JSON.parse((0, import_node_fs13.readFileSync)(identityPath, "utf-8"));
|
|
11735
11833
|
process.stdout.write(` Org: ${identity.org_name || "unknown"}
|
|
11736
11834
|
`);
|
|
11737
11835
|
process.stdout.write(` Org ID: ${identity.org_id || "unknown"}
|
|
@@ -11742,8 +11840,8 @@ ${bold("protect-mcp registry status")}
|
|
|
11742
11840
|
process.stdout.write(` Org identity: ${yellow("missing")} (${identityPath})
|
|
11743
11841
|
`);
|
|
11744
11842
|
}
|
|
11745
|
-
if ((0,
|
|
11746
|
-
const registry = JSON.parse((0,
|
|
11843
|
+
if ((0, import_node_fs13.existsSync)(registryPath)) {
|
|
11844
|
+
const registry = JSON.parse((0, import_node_fs13.readFileSync)(registryPath, "utf-8"));
|
|
11747
11845
|
const hosted = Array.isArray(registry.anchors) && registry.anchors.some((a) => a.timestamp_source === "scopeblind-hosted");
|
|
11748
11846
|
process.stdout.write(` Registry: ${registryPath}
|
|
11749
11847
|
`);
|
|
@@ -11753,7 +11851,7 @@ ${bold("protect-mcp registry status")}
|
|
|
11753
11851
|
`);
|
|
11754
11852
|
process.stdout.write(` Boundary: ${hosted ? green("hosted digest anchor") : yellow("local preview only")}
|
|
11755
11853
|
`);
|
|
11756
|
-
process.stdout.write(` Verifier page: ${(0,
|
|
11854
|
+
process.stdout.write(` Verifier page: ${(0, import_node_path9.join)(dir, registryMod.VERIFIER_PAGE_FILE)}
|
|
11757
11855
|
`);
|
|
11758
11856
|
} else {
|
|
11759
11857
|
process.stdout.write(` Registry: ${yellow("missing")} (${registryPath})
|
|
@@ -11781,10 +11879,10 @@ async function handleKillerDemo(argv) {
|
|
|
11781
11879
|
verifySelectiveDisclosurePackage: verifySelectiveDisclosurePackage2
|
|
11782
11880
|
} = await Promise.resolve().then(() => (init_signing_committed(), signing_committed_exports));
|
|
11783
11881
|
const registryMod = await Promise.resolve().then(() => (init_receipt_registry(), receipt_registry_exports));
|
|
11784
|
-
const dir = (0,
|
|
11785
|
-
(0,
|
|
11786
|
-
(0,
|
|
11787
|
-
(0,
|
|
11882
|
+
const dir = (0, import_node_path9.resolve)(flagValue(argv, "--dir") || mkdtempSync((0, import_node_path9.join)(tmpdir(), "scopeblind-killer-demo-")));
|
|
11883
|
+
(0, import_node_fs13.mkdirSync)(dir, { recursive: true });
|
|
11884
|
+
(0, import_node_fs13.mkdirSync)((0, import_node_path9.join)(dir, "keys"), { recursive: true });
|
|
11885
|
+
(0, import_node_fs13.mkdirSync)((0, import_node_path9.join)(dir, "receipts"), { recursive: true });
|
|
11788
11886
|
const privateKeyBytes = randomBytes4(32);
|
|
11789
11887
|
const publicKeyBytes = ed255192.getPublicKey(privateKeyBytes);
|
|
11790
11888
|
const keypair = {
|
|
@@ -11793,14 +11891,14 @@ async function handleKillerDemo(argv) {
|
|
|
11793
11891
|
kid: `killer-demo-${Date.now()}`,
|
|
11794
11892
|
issuer: "scopeblind-killer-demo"
|
|
11795
11893
|
};
|
|
11796
|
-
const keyPath = (0,
|
|
11797
|
-
(0,
|
|
11894
|
+
const keyPath = (0, import_node_path9.join)(dir, "keys", "gateway.json");
|
|
11895
|
+
(0, import_node_fs13.writeFileSync)(keyPath, JSON.stringify({
|
|
11798
11896
|
...keypair,
|
|
11799
11897
|
generated_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11800
11898
|
warning: "Demo key only. Do not use for production."
|
|
11801
11899
|
}, null, 2) + "\n");
|
|
11802
|
-
const shadowConfigPath = (0,
|
|
11803
|
-
const policyPackPath = (0,
|
|
11900
|
+
const shadowConfigPath = (0, import_node_path9.join)(dir, "protect-mcp.shadow.json");
|
|
11901
|
+
const policyPackPath = (0, import_node_path9.join)(dir, "protect-mcp.policy-pack.json");
|
|
11804
11902
|
const config = {
|
|
11805
11903
|
tools: { "*": { rate_limit: "100/hour" } },
|
|
11806
11904
|
default_tier: "signed-known",
|
|
@@ -11819,11 +11917,11 @@ async function handleKillerDemo(argv) {
|
|
|
11819
11917
|
signing: { key_path: keyPath, issuer: keypair.issuer, enabled: true },
|
|
11820
11918
|
notes: ["Demo policy pack: approvals for GitHub, email, and PMS booking; destructive tools blocked."]
|
|
11821
11919
|
};
|
|
11822
|
-
(0,
|
|
11823
|
-
(0,
|
|
11920
|
+
(0, import_node_fs13.writeFileSync)(shadowConfigPath, JSON.stringify(config, null, 2) + "\n");
|
|
11921
|
+
(0, import_node_fs13.writeFileSync)(policyPackPath, JSON.stringify(policyPack, null, 2) + "\n");
|
|
11824
11922
|
await initSigning({ enabled: true, key_path: keyPath, issuer: keypair.issuer });
|
|
11825
|
-
const logPath = (0,
|
|
11826
|
-
const receiptPath = (0,
|
|
11923
|
+
const logPath = (0, import_node_path9.join)(dir, ".protect-mcp-log.jsonl");
|
|
11924
|
+
const receiptPath = (0, import_node_path9.join)(dir, ".protect-mcp-receipts.jsonl");
|
|
11827
11925
|
const shadowCalls = [
|
|
11828
11926
|
{ tool: "read_file", input: { path: "/research/macro-notes.md" }, reason: "observe_mode" },
|
|
11829
11927
|
{ tool: "github_create_pr", input: { repo: "scopeblind/legate", branch: "agent/pms-adapter", title: "Wire mock PMS adapter" }, reason: "observe_mode" },
|
|
@@ -11832,7 +11930,7 @@ async function handleKillerDemo(argv) {
|
|
|
11832
11930
|
];
|
|
11833
11931
|
for (const [idx, call] of shadowCalls.entries()) {
|
|
11834
11932
|
const requestId2 = `demo-shadow-${idx + 1}`;
|
|
11835
|
-
(0,
|
|
11933
|
+
(0, import_node_fs13.appendFileSync)(logPath, JSON.stringify({
|
|
11836
11934
|
v: 2,
|
|
11837
11935
|
tool: call.tool,
|
|
11838
11936
|
decision: "allow",
|
|
@@ -11867,8 +11965,8 @@ async function handleKillerDemo(argv) {
|
|
|
11867
11965
|
policy_digest: (0, import_node_crypto7.createHash)("sha256").update(JSON.stringify(policyPack)).digest("hex").slice(0, 16),
|
|
11868
11966
|
action_readback: readback
|
|
11869
11967
|
};
|
|
11870
|
-
(0,
|
|
11871
|
-
(0,
|
|
11968
|
+
(0, import_node_fs13.appendFileSync)(logPath, JSON.stringify(requireApprovalEntry) + "\n");
|
|
11969
|
+
(0, import_node_fs13.appendFileSync)((0, import_node_path9.join)(dir, ".protect-mcp-approval-resolutions.jsonl"), JSON.stringify({
|
|
11872
11970
|
type: "scopeblind.approval_resolution.v1",
|
|
11873
11971
|
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11874
11972
|
request_id: requestId,
|
|
@@ -11888,11 +11986,11 @@ async function handleKillerDemo(argv) {
|
|
|
11888
11986
|
truncated: false
|
|
11889
11987
|
}
|
|
11890
11988
|
};
|
|
11891
|
-
(0,
|
|
11989
|
+
(0, import_node_fs13.appendFileSync)(logPath, JSON.stringify(executedEntry) + "\n");
|
|
11892
11990
|
const signed = signDecision(executedEntry);
|
|
11893
11991
|
if (!signed.signed) throw new Error(`demo signing failed: ${signed.warning || signed.error || "unknown"}`);
|
|
11894
|
-
(0,
|
|
11895
|
-
(0,
|
|
11992
|
+
(0, import_node_fs13.appendFileSync)(receiptPath, signed.signed + "\n");
|
|
11993
|
+
(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
11994
|
const receiptArtifact = JSON.parse(signed.signed);
|
|
11897
11995
|
const tamperedArtifact = JSON.parse(signed.signed);
|
|
11898
11996
|
if (tamperedArtifact.payload && typeof tamperedArtifact.payload === "object") {
|
|
@@ -11903,7 +12001,7 @@ async function handleKillerDemo(argv) {
|
|
|
11903
12001
|
}
|
|
11904
12002
|
const validOriginal = artifacts.verifyArtifact(receiptArtifact, keypair.publicKey);
|
|
11905
12003
|
const validTampered = artifacts.verifyArtifact(tamperedArtifact, keypair.publicKey);
|
|
11906
|
-
(0,
|
|
12004
|
+
(0, import_node_fs13.writeFileSync)((0, import_node_path9.join)(dir, "receipts", "tampered.receipt.json"), JSON.stringify(tamperedArtifact, null, 2) + "\n");
|
|
11907
12005
|
const committed = signCommittedDecision2(
|
|
11908
12006
|
executedEntry,
|
|
11909
12007
|
["tool", "payload_digest", "swarm"],
|
|
@@ -11915,11 +12013,11 @@ async function handleKillerDemo(argv) {
|
|
|
11915
12013
|
const committedReceipt = JSON.parse(committed.signed);
|
|
11916
12014
|
const disclosurePackage = createSelectiveDisclosurePackage2(committedReceipt, ["tool"], committed.openings);
|
|
11917
12015
|
const disclosureVerification = verifySelectiveDisclosurePackage2(committedReceipt, disclosurePackage);
|
|
11918
|
-
(0,
|
|
11919
|
-
(0,
|
|
11920
|
-
(0,
|
|
11921
|
-
(0,
|
|
11922
|
-
(0,
|
|
12016
|
+
(0, import_node_fs13.appendFileSync)(receiptPath, committed.signed + "\n");
|
|
12017
|
+
(0, import_node_fs13.writeFileSync)((0, import_node_path9.join)(dir, "receipts", "selective-disclosure.receipt.json"), JSON.stringify(committedReceipt, null, 2) + "\n");
|
|
12018
|
+
(0, import_node_fs13.writeFileSync)((0, import_node_path9.join)(dir, "receipts", "selective-disclosure.package.json"), JSON.stringify(disclosurePackage, null, 2) + "\n");
|
|
12019
|
+
(0, import_node_fs13.writeFileSync)((0, import_node_path9.join)(dir, "receipts", "selective-disclosure.tool-only.json"), JSON.stringify(disclosurePackage, null, 2) + "\n");
|
|
12020
|
+
(0, import_node_fs13.writeFileSync)((0, import_node_path9.join)(dir, "verification-results.json"), JSON.stringify({
|
|
11923
12021
|
original_receipt_valid: validOriginal,
|
|
11924
12022
|
tampered_receipt_valid: validTampered,
|
|
11925
12023
|
selective_disclosure_valid: disclosureVerification.valid,
|
|
@@ -12002,19 +12100,19 @@ async function handleKillerDemo(argv) {
|
|
|
12002
12100
|
"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
12101
|
""
|
|
12004
12102
|
].join("\n");
|
|
12005
|
-
(0,
|
|
12006
|
-
(0,
|
|
12103
|
+
(0, import_node_fs13.writeFileSync)((0, import_node_path9.join)(dir, "DEMO-RUNBOOK.md"), runbook);
|
|
12104
|
+
(0, import_node_fs13.writeFileSync)((0, import_node_path9.join)(dir, "demo-summary.json"), JSON.stringify({
|
|
12007
12105
|
dir,
|
|
12008
12106
|
dashboard_command: `npx protect-mcp dashboard --dir ${dir} --policy ${policyPackPath} --open`,
|
|
12009
12107
|
policy_pack: policyPackPath,
|
|
12010
|
-
receipt: (0,
|
|
12011
|
-
tampered_receipt: (0,
|
|
12012
|
-
selective_disclosure_receipt: (0,
|
|
12013
|
-
selective_disclosure_package: (0,
|
|
12014
|
-
verification_results: (0,
|
|
12108
|
+
receipt: (0, import_node_path9.join)(dir, "receipts", "approved-pms-booking.receipt.json"),
|
|
12109
|
+
tampered_receipt: (0, import_node_path9.join)(dir, "receipts", "tampered.receipt.json"),
|
|
12110
|
+
selective_disclosure_receipt: (0, import_node_path9.join)(dir, "receipts", "selective-disclosure.receipt.json"),
|
|
12111
|
+
selective_disclosure_package: (0, import_node_path9.join)(dir, "receipts", "selective-disclosure.tool-only.json"),
|
|
12112
|
+
verification_results: (0, import_node_path9.join)(dir, "verification-results.json"),
|
|
12015
12113
|
registry: registry.registryPath,
|
|
12016
12114
|
verifier_page: registry.verifierPath,
|
|
12017
|
-
runbook: (0,
|
|
12115
|
+
runbook: (0, import_node_path9.join)(dir, "DEMO-RUNBOOK.md"),
|
|
12018
12116
|
original_valid: validOriginal.valid,
|
|
12019
12117
|
tampered_valid: validTampered.valid,
|
|
12020
12118
|
selective_disclosure_valid: disclosureVerification.valid
|
|
@@ -12027,9 +12125,9 @@ ${bold("protect-mcp killer-demo")}
|
|
|
12027
12125
|
`);
|
|
12028
12126
|
process.stdout.write(` Dashboard: ${dim(`npx protect-mcp dashboard --dir ${dir} --policy ${policyPackPath} --open`)}
|
|
12029
12127
|
`);
|
|
12030
|
-
process.stdout.write(` Runbook: ${(0,
|
|
12128
|
+
process.stdout.write(` Runbook: ${(0, import_node_path9.join)(dir, "DEMO-RUNBOOK.md")}
|
|
12031
12129
|
`);
|
|
12032
|
-
process.stdout.write(` Signed receipt: ${(0,
|
|
12130
|
+
process.stdout.write(` Signed receipt: ${(0, import_node_path9.join)(dir, "receipts", "approved-pms-booking.receipt.json")}
|
|
12033
12131
|
`);
|
|
12034
12132
|
process.stdout.write(` Tamper check: original=${validOriginal.valid ? green("valid") : red("invalid")} tampered=${validTampered.valid ? red("valid") : green("invalid")}
|
|
12035
12133
|
`);
|
|
@@ -12049,8 +12147,8 @@ async function handleVerifyDisclosure(argv) {
|
|
|
12049
12147
|
process.exit(1);
|
|
12050
12148
|
}
|
|
12051
12149
|
const { verifySelectiveDisclosurePackage: verifySelectiveDisclosurePackage2 } = await Promise.resolve().then(() => (init_signing_committed(), signing_committed_exports));
|
|
12052
|
-
const receipt = JSON.parse((0,
|
|
12053
|
-
const disclosure = JSON.parse((0,
|
|
12150
|
+
const receipt = JSON.parse((0, import_node_fs13.readFileSync)((0, import_node_path9.resolve)(receiptPath), "utf-8"));
|
|
12151
|
+
const disclosure = JSON.parse((0, import_node_fs13.readFileSync)((0, import_node_path9.resolve)(disclosurePath), "utf-8"));
|
|
12054
12152
|
const result = verifySelectiveDisclosurePackage2(receipt, disclosure);
|
|
12055
12153
|
process.stdout.write(`
|
|
12056
12154
|
${bold("protect-mcp verify-disclosure")}
|
|
@@ -12086,7 +12184,7 @@ ${red("Errors:")}
|
|
|
12086
12184
|
async function handlePolicyPacks(argv) {
|
|
12087
12185
|
const subcommand = argv[0] || "list";
|
|
12088
12186
|
const packArg = argv[1];
|
|
12089
|
-
const dir = (0,
|
|
12187
|
+
const dir = (0, import_node_path9.resolve)(flagValue(argv, "--dir") || "./cedar");
|
|
12090
12188
|
const force = argv.includes("--force");
|
|
12091
12189
|
if (subcommand === "list") {
|
|
12092
12190
|
process.stdout.write(`
|
|
@@ -12141,17 +12239,17 @@ ${bold(pack.name)} (${pack.id})
|
|
|
12141
12239
|
`);
|
|
12142
12240
|
process.exit(1);
|
|
12143
12241
|
}
|
|
12144
|
-
(0,
|
|
12242
|
+
(0, import_node_fs13.mkdirSync)(dir, { recursive: true });
|
|
12145
12243
|
const written = [];
|
|
12146
12244
|
for (const pack of packs) {
|
|
12147
12245
|
for (const file of pack.files) {
|
|
12148
|
-
const outPath = (0,
|
|
12149
|
-
if ((0,
|
|
12246
|
+
const outPath = (0, import_node_path9.join)(dir, file.path);
|
|
12247
|
+
if ((0, import_node_fs13.existsSync)(outPath) && !force) {
|
|
12150
12248
|
process.stderr.write(`Refusing to overwrite ${outPath}. Re-run with --force if intentional.
|
|
12151
12249
|
`);
|
|
12152
12250
|
process.exit(1);
|
|
12153
12251
|
}
|
|
12154
|
-
(0,
|
|
12252
|
+
(0, import_node_fs13.writeFileSync)(outPath, file.contents.endsWith("\n") ? file.contents : `${file.contents}
|
|
12155
12253
|
`);
|
|
12156
12254
|
written.push(outPath);
|
|
12157
12255
|
}
|
|
@@ -12176,7 +12274,7 @@ Next: ${dim(`protect-mcp serve --cedar ${dir}`)} for shadow mode, then add ${dim
|
|
|
12176
12274
|
async function handleConnectors(argv) {
|
|
12177
12275
|
const subcommand = argv[0] || "list";
|
|
12178
12276
|
const pilotArg = argv[1];
|
|
12179
|
-
const dir = (0,
|
|
12277
|
+
const dir = (0, import_node_path9.resolve)(flagValue(argv, "--dir") || process.cwd());
|
|
12180
12278
|
const force = argv.includes("--force");
|
|
12181
12279
|
if (subcommand === "list") {
|
|
12182
12280
|
process.stdout.write(`
|
|
@@ -12418,11 +12516,11 @@ ${"\u2500".repeat(60)}
|
|
|
12418
12516
|
`);
|
|
12419
12517
|
}
|
|
12420
12518
|
async function traceLocal(receiptId) {
|
|
12421
|
-
const { readFileSync: readFileSync11, existsSync:
|
|
12422
|
-
const { join:
|
|
12519
|
+
const { readFileSync: readFileSync11, existsSync: existsSync10 } = await import("fs");
|
|
12520
|
+
const { join: join9 } = await import("path");
|
|
12423
12521
|
const dir = process.cwd();
|
|
12424
|
-
const receiptsDir =
|
|
12425
|
-
if (!
|
|
12522
|
+
const receiptsDir = join9(dir, ".protect-mcp", "receipts");
|
|
12523
|
+
if (!existsSync10(receiptsDir)) {
|
|
12426
12524
|
process.stdout.write(` No local receipts found in ${receiptsDir}
|
|
12427
12525
|
|
|
12428
12526
|
`);
|
|
@@ -12436,7 +12534,7 @@ async function traceLocal(receiptId) {
|
|
|
12436
12534
|
const receipts = [];
|
|
12437
12535
|
for (const file of files) {
|
|
12438
12536
|
try {
|
|
12439
|
-
const content = readFileSync11(
|
|
12537
|
+
const content = readFileSync11(join9(receiptsDir, file), "utf-8");
|
|
12440
12538
|
const receipt = JSON.parse(content);
|
|
12441
12539
|
receipts.push(receipt);
|
|
12442
12540
|
} catch {
|
|
@@ -12493,8 +12591,8 @@ function getTypeEmoji(type) {
|
|
|
12493
12591
|
}
|
|
12494
12592
|
}
|
|
12495
12593
|
async function handleInitHooks(argv) {
|
|
12496
|
-
const { writeFileSync:
|
|
12497
|
-
const { join:
|
|
12594
|
+
const { writeFileSync: writeFileSync5, existsSync: existsSync10, mkdirSync: mkdirSync4, readFileSync: readFileSync11 } = await import("fs");
|
|
12595
|
+
const { join: join9 } = await import("path");
|
|
12498
12596
|
const { generateHookSettings: generateHookSettings2, generateSampleCedarPolicy: generateSampleCedarPolicy2, generateVerifyReceiptSkill: generateVerifyReceiptSkill2 } = await Promise.resolve().then(() => (init_hook_patterns(), hook_patterns_exports));
|
|
12499
12597
|
let dir = process.cwd();
|
|
12500
12598
|
const dirIdx = argv.indexOf("--dir");
|
|
@@ -12508,13 +12606,13 @@ ${bold("protect-mcp init-hooks")}
|
|
|
12508
12606
|
process.stdout.write(`${"\u2500".repeat(55)}
|
|
12509
12607
|
|
|
12510
12608
|
`);
|
|
12511
|
-
const claudeDir =
|
|
12512
|
-
const settingsPath =
|
|
12609
|
+
const claudeDir = join9(dir, ".claude");
|
|
12610
|
+
const settingsPath = join9(claudeDir, "settings.json");
|
|
12513
12611
|
let existingSettings = {};
|
|
12514
|
-
if (!
|
|
12515
|
-
|
|
12612
|
+
if (!existsSync10(claudeDir)) {
|
|
12613
|
+
mkdirSync4(claudeDir, { recursive: true });
|
|
12516
12614
|
}
|
|
12517
|
-
if (
|
|
12615
|
+
if (existsSync10(settingsPath)) {
|
|
12518
12616
|
try {
|
|
12519
12617
|
existingSettings = JSON.parse(readFileSync11(settingsPath, "utf-8"));
|
|
12520
12618
|
} catch {
|
|
@@ -12530,7 +12628,7 @@ ${bold("protect-mcp init-hooks")}
|
|
|
12530
12628
|
...hookSettings.hooks
|
|
12531
12629
|
}
|
|
12532
12630
|
};
|
|
12533
|
-
|
|
12631
|
+
writeFileSync5(settingsPath, JSON.stringify(mergedSettings, null, 2) + "\n");
|
|
12534
12632
|
process.stdout.write(` ${green("\u2713")} ${settingsPath}
|
|
12535
12633
|
`);
|
|
12536
12634
|
process.stdout.write(` Hook URL: ${dim(hookUrl)}
|
|
@@ -12538,26 +12636,26 @@ ${bold("protect-mcp init-hooks")}
|
|
|
12538
12636
|
process.stdout.write(` Events: PreToolUse, PostToolUse, SubagentStart/Stop, Task, Session, Config, Stop
|
|
12539
12637
|
|
|
12540
12638
|
`);
|
|
12541
|
-
const keysDir =
|
|
12542
|
-
const keyPath =
|
|
12543
|
-
if (!
|
|
12544
|
-
if (!
|
|
12639
|
+
const keysDir = join9(dir, "keys");
|
|
12640
|
+
const keyPath = join9(keysDir, "gateway.json");
|
|
12641
|
+
if (!existsSync10(keyPath)) {
|
|
12642
|
+
if (!existsSync10(keysDir)) mkdirSync4(keysDir, { recursive: true });
|
|
12545
12643
|
const { randomBytes: rb } = await import("crypto");
|
|
12546
12644
|
try {
|
|
12547
12645
|
const { ed25519: ed255192 } = await Promise.resolve().then(() => (init_ed25519(), ed25519_exports));
|
|
12548
12646
|
const { bytesToHex: bytesToHex2 } = await Promise.resolve().then(() => (init_utils(), utils_exports));
|
|
12549
12647
|
const privateKey = rb(32);
|
|
12550
12648
|
const publicKey = ed255192.getPublicKey(privateKey);
|
|
12551
|
-
|
|
12649
|
+
writeFileSync5(keyPath, JSON.stringify({
|
|
12552
12650
|
privateKey: bytesToHex2(privateKey),
|
|
12553
12651
|
publicKey: bytesToHex2(publicKey),
|
|
12554
12652
|
kid: `hook-${Date.now()}`,
|
|
12555
12653
|
generated_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12556
12654
|
warning: "KEEP THIS FILE SECRET. Never commit to version control."
|
|
12557
12655
|
}, null, 2) + "\n");
|
|
12558
|
-
const gitignorePath =
|
|
12559
|
-
if (!
|
|
12560
|
-
|
|
12656
|
+
const gitignorePath = join9(keysDir, ".gitignore");
|
|
12657
|
+
if (!existsSync10(gitignorePath)) {
|
|
12658
|
+
writeFileSync5(gitignorePath, "# Never commit signing keys\n*.json\n");
|
|
12561
12659
|
}
|
|
12562
12660
|
process.stdout.write(` ${green("\u2713")} ${keyPath} (Ed25519 keypair)
|
|
12563
12661
|
|
|
@@ -12572,11 +12670,11 @@ ${bold("protect-mcp init-hooks")}
|
|
|
12572
12670
|
|
|
12573
12671
|
`);
|
|
12574
12672
|
}
|
|
12575
|
-
const policiesDir =
|
|
12576
|
-
const cedarPath =
|
|
12577
|
-
if (!
|
|
12578
|
-
if (!
|
|
12579
|
-
|
|
12673
|
+
const policiesDir = join9(dir, "policies");
|
|
12674
|
+
const cedarPath = join9(policiesDir, "agent.cedar");
|
|
12675
|
+
if (!existsSync10(cedarPath)) {
|
|
12676
|
+
if (!existsSync10(policiesDir)) mkdirSync4(policiesDir, { recursive: true });
|
|
12677
|
+
writeFileSync5(cedarPath, generateSampleCedarPolicy2());
|
|
12580
12678
|
process.stdout.write(` ${green("\u2713")} ${cedarPath}
|
|
12581
12679
|
`);
|
|
12582
12680
|
process.stdout.write(` Edit to customize tool permissions. Cedar deny is AUTHORITATIVE.
|
|
@@ -12587,8 +12685,8 @@ ${bold("protect-mcp init-hooks")}
|
|
|
12587
12685
|
|
|
12588
12686
|
`);
|
|
12589
12687
|
}
|
|
12590
|
-
const configPath =
|
|
12591
|
-
if (!
|
|
12688
|
+
const configPath = join9(dir, "protect-mcp.json");
|
|
12689
|
+
if (!existsSync10(configPath)) {
|
|
12592
12690
|
const config = {
|
|
12593
12691
|
tools: { "*": { rate_limit: "100/hour" } },
|
|
12594
12692
|
default_tier: "unknown",
|
|
@@ -12598,16 +12696,16 @@ ${bold("protect-mcp init-hooks")}
|
|
|
12598
12696
|
enabled: true
|
|
12599
12697
|
}
|
|
12600
12698
|
};
|
|
12601
|
-
|
|
12699
|
+
writeFileSync5(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
12602
12700
|
process.stdout.write(` ${green("\u2713")} ${configPath}
|
|
12603
12701
|
|
|
12604
12702
|
`);
|
|
12605
12703
|
}
|
|
12606
|
-
const skillsDir =
|
|
12607
|
-
const skillPath =
|
|
12608
|
-
if (!
|
|
12609
|
-
|
|
12610
|
-
|
|
12704
|
+
const skillsDir = join9(dir, ".claude", "skills", "verify-receipt");
|
|
12705
|
+
const skillPath = join9(skillsDir, "SKILL.md");
|
|
12706
|
+
if (!existsSync10(skillPath)) {
|
|
12707
|
+
mkdirSync4(skillsDir, { recursive: true });
|
|
12708
|
+
writeFileSync5(skillPath, generateVerifyReceiptSkill2());
|
|
12611
12709
|
process.stdout.write(` ${green("\u2713")} ${skillPath}
|
|
12612
12710
|
`);
|
|
12613
12711
|
process.stdout.write(` Use ${dim("/verify-receipt")} in Claude Code to check audit trails.
|
|
@@ -12663,13 +12761,13 @@ ${bold("protect-mcp init-hooks")}
|
|
|
12663
12761
|
}
|
|
12664
12762
|
async function sendInstallTelemetry() {
|
|
12665
12763
|
try {
|
|
12666
|
-
const { existsSync:
|
|
12667
|
-
const { join:
|
|
12764
|
+
const { existsSync: existsSync10, mkdirSync: mkdirSync4, writeFileSync: writeFileSync5, readFileSync: readFileSync11 } = await import("fs");
|
|
12765
|
+
const { join: join9, dirname: dirname3 } = await import("path");
|
|
12668
12766
|
const { homedir } = await import("os");
|
|
12669
12767
|
const { fileURLToPath } = await import("url");
|
|
12670
|
-
const markerDir =
|
|
12671
|
-
const markerFile =
|
|
12672
|
-
if (
|
|
12768
|
+
const markerDir = join9(homedir(), ".protect-mcp");
|
|
12769
|
+
const markerFile = join9(markerDir, ".telemetry-sent");
|
|
12770
|
+
if (existsSync10(markerFile) || process.env.PROTECT_MCP_TELEMETRY === "off") {
|
|
12673
12771
|
return;
|
|
12674
12772
|
}
|
|
12675
12773
|
const version = await pkgVersion();
|
|
@@ -12689,10 +12787,10 @@ async function sendInstallTelemetry() {
|
|
|
12689
12787
|
signal: controller.signal
|
|
12690
12788
|
}).catch(() => {
|
|
12691
12789
|
}).finally(() => clearTimeout(timeout));
|
|
12692
|
-
if (!
|
|
12693
|
-
|
|
12790
|
+
if (!existsSync10(markerDir)) {
|
|
12791
|
+
mkdirSync4(markerDir, { recursive: true });
|
|
12694
12792
|
}
|
|
12695
|
-
|
|
12793
|
+
writeFileSync5(markerFile, String(Date.now()), "utf-8");
|
|
12696
12794
|
process.stderr.write(
|
|
12697
12795
|
"[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
12796
|
);
|
|
@@ -12708,8 +12806,8 @@ function loadPolicyArg(argv) {
|
|
|
12708
12806
|
const policyFile = flagValue(argv, "--policy");
|
|
12709
12807
|
try {
|
|
12710
12808
|
if (cedarDir) return loadCedarPolicies(cedarDir);
|
|
12711
|
-
if (policyFile && (0,
|
|
12712
|
-
return policySetFromSource((0,
|
|
12809
|
+
if (policyFile && (0, import_node_fs13.existsSync)(policyFile)) {
|
|
12810
|
+
return policySetFromSource((0, import_node_fs13.readFileSync)(policyFile, "utf-8"), (0, import_node_path9.basename)(policyFile));
|
|
12713
12811
|
}
|
|
12714
12812
|
} catch {
|
|
12715
12813
|
}
|
|
@@ -12810,7 +12908,7 @@ async function handleSign(argv) {
|
|
|
12810
12908
|
if (m.tool) tool = m.tool;
|
|
12811
12909
|
}
|
|
12812
12910
|
}
|
|
12813
|
-
if (keyPath && (0,
|
|
12911
|
+
if (keyPath && (0, import_node_fs13.existsSync)(keyPath)) {
|
|
12814
12912
|
try {
|
|
12815
12913
|
await initSigning({ enabled: true, key_path: keyPath });
|
|
12816
12914
|
} catch {
|
|
@@ -12827,12 +12925,12 @@ async function handleSign(argv) {
|
|
|
12827
12925
|
timestamp: Date.now()
|
|
12828
12926
|
});
|
|
12829
12927
|
try {
|
|
12830
|
-
(0,
|
|
12928
|
+
(0, import_node_fs13.mkdirSync)(receiptsDir, { recursive: true });
|
|
12831
12929
|
} catch {
|
|
12832
12930
|
}
|
|
12833
12931
|
const line = signed.signed ?? JSON.stringify({ tool, request_id: requestId, signed: false, note: signed.warning || "no signer configured" });
|
|
12834
12932
|
try {
|
|
12835
|
-
(0,
|
|
12933
|
+
(0, import_node_fs13.appendFileSync)((0, import_node_path9.join)(receiptsDir, "receipts.jsonl"), line + "\n");
|
|
12836
12934
|
} catch {
|
|
12837
12935
|
}
|
|
12838
12936
|
if (format === "hermes") {
|
|
@@ -12842,12 +12940,55 @@ async function handleSign(argv) {
|
|
|
12842
12940
|
process.stdout.write(JSON.stringify({ signed: Boolean(signed.signed), artifact_type: signed.artifact_type, request_id: requestId }) + "\n");
|
|
12843
12941
|
process.exit(0);
|
|
12844
12942
|
}
|
|
12943
|
+
async function handleSample(argv) {
|
|
12944
|
+
const dir = flagValue(argv, "--dir") || process.cwd();
|
|
12945
|
+
const { buildSampleKit: buildSampleKit2 } = await Promise.resolve().then(() => (init_sample(), sample_exports));
|
|
12946
|
+
let kit;
|
|
12947
|
+
try {
|
|
12948
|
+
kit = buildSampleKit2(dir, { force: argv.includes("--force") });
|
|
12949
|
+
} catch (err) {
|
|
12950
|
+
if (err?.code === "SAMPLE_EXISTS") {
|
|
12951
|
+
process.stderr.write(
|
|
12952
|
+
"\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"
|
|
12953
|
+
);
|
|
12954
|
+
process.exit(1);
|
|
12955
|
+
}
|
|
12956
|
+
throw err;
|
|
12957
|
+
}
|
|
12958
|
+
process.stdout.write(`
|
|
12959
|
+
${bold("\u{1F6E1} Sample record seeded")} \xB7 8 decisions (1 blocked, 2 payments), signed with a fresh key ${dim(`(kid ${kit.kid})`)}
|
|
12960
|
+
|
|
12961
|
+
`);
|
|
12962
|
+
process.stdout.write(" .protect-mcp-receipts.jsonl the signed sample record\n");
|
|
12963
|
+
process.stdout.write(" demo-tampered.jsonl the same record with ONE decision edited after signing\n");
|
|
12964
|
+
process.stdout.write(` keys/gateway.json sample keypair ${dim("(never commit)")}
|
|
12965
|
+
|
|
12966
|
+
`);
|
|
12967
|
+
process.stdout.write(`${bold("Replay the demo")} ${dim("(the film: legate.scopeblind.com/record)")}
|
|
12968
|
+
`);
|
|
12969
|
+
process.stdout.write(" npx protect-mcp record\n");
|
|
12970
|
+
process.stdout.write(" npx protect-mcp claim --payment-under 100 --anchor --output payments-under-100.json\n");
|
|
12971
|
+
process.stdout.write(" npx protect-mcp verify-claim payments-under-100.json\n");
|
|
12972
|
+
process.stdout.write(" npx protect-mcp anchor-record\n\n");
|
|
12973
|
+
process.stdout.write(`${dim("Drop demo-tampered.jsonl into the record page to watch tampering get caught.")}
|
|
12974
|
+
`);
|
|
12975
|
+
process.stdout.write(`${dim("Everything runs locally; --anchor publishes only a digest to the public log.")}
|
|
12976
|
+
|
|
12977
|
+
`);
|
|
12978
|
+
process.exit(0);
|
|
12979
|
+
}
|
|
12845
12980
|
async function main() {
|
|
12846
12981
|
sendInstallTelemetry().catch(() => {
|
|
12847
12982
|
});
|
|
12848
12983
|
const args = process.argv.slice(2);
|
|
12849
12984
|
process.env.PROTECT_MCP_VERSION = process.env.PROTECT_MCP_VERSION || await pkgVersion();
|
|
12850
|
-
|
|
12985
|
+
const preSep = args.includes("--") ? args.slice(0, args.indexOf("--")) : args;
|
|
12986
|
+
if (args[0] === "version" || preSep.includes("--version") || preSep.includes("-V")) {
|
|
12987
|
+
process.stdout.write(`${process.env.PROTECT_MCP_VERSION || "unknown"}
|
|
12988
|
+
`);
|
|
12989
|
+
process.exit(0);
|
|
12990
|
+
}
|
|
12991
|
+
if (args.length === 0 || args[0] === "help" || preSep.includes("--help") || preSep.includes("-h")) {
|
|
12851
12992
|
printHelp();
|
|
12852
12993
|
process.exit(0);
|
|
12853
12994
|
}
|
|
@@ -12901,6 +13042,10 @@ async function main() {
|
|
|
12901
13042
|
await handleAnchorRecord(args.slice(1));
|
|
12902
13043
|
return;
|
|
12903
13044
|
}
|
|
13045
|
+
if (args[0] === "sample") {
|
|
13046
|
+
await handleSample(args.slice(1));
|
|
13047
|
+
return;
|
|
13048
|
+
}
|
|
12904
13049
|
if (args[0] === "init-hooks") {
|
|
12905
13050
|
await handleInitHooks(args.slice(1));
|
|
12906
13051
|
process.exit(0);
|
|
@@ -13005,10 +13150,10 @@ async function main() {
|
|
|
13005
13150
|
let cedarPolicySet = null;
|
|
13006
13151
|
let effectiveCedarDir = cedarDir;
|
|
13007
13152
|
if (!effectiveCedarDir && !policyPath) {
|
|
13008
|
-
const { existsSync:
|
|
13153
|
+
const { existsSync: existsSync10, readdirSync: readdirSync4 } = await import("fs");
|
|
13009
13154
|
for (const candidate of ["cedar", "policies", "."]) {
|
|
13010
13155
|
try {
|
|
13011
|
-
if (
|
|
13156
|
+
if (existsSync10(candidate) && readdirSync4(candidate).some((f) => f.endsWith(".cedar"))) {
|
|
13012
13157
|
effectiveCedarDir = candidate;
|
|
13013
13158
|
process.stderr.write(`[PROTECT_MCP] Auto-detected Cedar policies in ./${candidate}/
|
|
13014
13159
|
`);
|
|
@@ -13119,8 +13264,8 @@ async function handleSimulate(args) {
|
|
|
13119
13264
|
process.stderr.write("Usage: protect-mcp simulate --policy <path> [--log <path>] [--tier <tier>] [--json]\n");
|
|
13120
13265
|
process.exit(1);
|
|
13121
13266
|
}
|
|
13122
|
-
const { existsSync:
|
|
13123
|
-
if (!
|
|
13267
|
+
const { existsSync: existsSync10 } = await import("fs");
|
|
13268
|
+
if (!existsSync10(logPath)) {
|
|
13124
13269
|
process.stderr.write(`Log file not found: ${logPath}
|
|
13125
13270
|
`);
|
|
13126
13271
|
process.stderr.write("Run protect-mcp in shadow mode first to generate a log file.\n");
|
|
@@ -13142,8 +13287,8 @@ async function handleSimulate(args) {
|
|
|
13142
13287
|
}
|
|
13143
13288
|
}
|
|
13144
13289
|
async function handleDoctor() {
|
|
13145
|
-
const { existsSync:
|
|
13146
|
-
const { join:
|
|
13290
|
+
const { existsSync: existsSync10, readFileSync: readFileSync11, readdirSync: readdirSync4 } = await import("fs");
|
|
13291
|
+
const { join: join9 } = await import("path");
|
|
13147
13292
|
const { execSync } = await import("child_process");
|
|
13148
13293
|
const green2 = (s) => `\x1B[32m\u2713\x1B[0m ${s}`;
|
|
13149
13294
|
const red2 = (s) => `\x1B[31m\u2717\x1B[0m ${s}`;
|
|
@@ -13162,8 +13307,8 @@ async function handleDoctor() {
|
|
|
13162
13307
|
`));
|
|
13163
13308
|
issues++;
|
|
13164
13309
|
}
|
|
13165
|
-
const configPath =
|
|
13166
|
-
if (
|
|
13310
|
+
const configPath = join9(process.cwd(), "scopeblind.config.json");
|
|
13311
|
+
if (existsSync10(configPath)) {
|
|
13167
13312
|
try {
|
|
13168
13313
|
const config = JSON.parse(readFileSync11(configPath, "utf-8"));
|
|
13169
13314
|
if (config.signing?.private_key || config.signing?.key_file) {
|
|
@@ -13182,7 +13327,7 @@ async function handleDoctor() {
|
|
|
13182
13327
|
let policyFound = false;
|
|
13183
13328
|
for (const dir of ["cedar", "policies", "."]) {
|
|
13184
13329
|
try {
|
|
13185
|
-
if (
|
|
13330
|
+
if (existsSync10(dir) && readdirSync4(dir).some((f) => f.endsWith(".cedar"))) {
|
|
13186
13331
|
process.stdout.write(green2(`Cedar policies found in ./${dir}/
|
|
13187
13332
|
`));
|
|
13188
13333
|
policyFound = true;
|
|
@@ -13193,7 +13338,7 @@ async function handleDoctor() {
|
|
|
13193
13338
|
}
|
|
13194
13339
|
if (!policyFound) {
|
|
13195
13340
|
for (const name of ["policy.json", "protect-mcp.policy.json", "scopeblind-policy.json"]) {
|
|
13196
|
-
if (
|
|
13341
|
+
if (existsSync10(name)) {
|
|
13197
13342
|
process.stdout.write(green2(`JSON policy found: ${name}
|
|
13198
13343
|
`));
|
|
13199
13344
|
policyFound = true;
|
|
@@ -13214,9 +13359,9 @@ async function handleDoctor() {
|
|
|
13214
13359
|
} catch {
|
|
13215
13360
|
process.stdout.write(dim2(" Cedar WASM not installed\n"));
|
|
13216
13361
|
}
|
|
13217
|
-
const logFile =
|
|
13218
|
-
const receiptFile =
|
|
13219
|
-
if (
|
|
13362
|
+
const logFile = join9(process.cwd(), "protect-mcp-decisions.jsonl");
|
|
13363
|
+
const receiptFile = join9(process.cwd(), "protect-mcp-receipts.jsonl");
|
|
13364
|
+
if (existsSync10(logFile)) {
|
|
13220
13365
|
try {
|
|
13221
13366
|
const lines = readFileSync11(logFile, "utf-8").trim().split("\n").length;
|
|
13222
13367
|
process.stdout.write(green2(`Decision log: ${lines} entries
|
|
@@ -13227,7 +13372,7 @@ async function handleDoctor() {
|
|
|
13227
13372
|
} else {
|
|
13228
13373
|
process.stdout.write(dim2(" No decision log yet, will be created on first tool call\n"));
|
|
13229
13374
|
}
|
|
13230
|
-
if (
|
|
13375
|
+
if (existsSync10(receiptFile)) {
|
|
13231
13376
|
try {
|
|
13232
13377
|
const lines = readFileSync11(receiptFile, "utf-8").trim().split("\n").length;
|
|
13233
13378
|
process.stdout.write(green2(`Receipt file: ${lines} signed receipts
|
|
@@ -13298,9 +13443,9 @@ async function handleReport(args) {
|
|
|
13298
13443
|
}
|
|
13299
13444
|
}
|
|
13300
13445
|
const { generateReport: generateReport2, formatReportMarkdown: formatReportMarkdown2 } = await Promise.resolve().then(() => (init_report(), report_exports));
|
|
13301
|
-
const { join:
|
|
13302
|
-
const logPath =
|
|
13303
|
-
const receiptPath =
|
|
13446
|
+
const { join: join9 } = await import("path");
|
|
13447
|
+
const logPath = join9(dir, ".protect-mcp-log.jsonl");
|
|
13448
|
+
const receiptPath = join9(dir, ".protect-mcp-receipts.jsonl");
|
|
13304
13449
|
const report = generateReport2(logPath, receiptPath, period);
|
|
13305
13450
|
let output;
|
|
13306
13451
|
if (format === "md") {
|
|
@@ -13309,8 +13454,8 @@ async function handleReport(args) {
|
|
|
13309
13454
|
output = JSON.stringify(report, null, 2);
|
|
13310
13455
|
}
|
|
13311
13456
|
if (outputPath) {
|
|
13312
|
-
const { writeFileSync:
|
|
13313
|
-
|
|
13457
|
+
const { writeFileSync: writeFileSync5 } = await import("fs");
|
|
13458
|
+
writeFileSync5(outputPath, output, "utf-8");
|
|
13314
13459
|
process.stderr.write(`Report written to ${outputPath}
|
|
13315
13460
|
`);
|
|
13316
13461
|
} else {
|