@node9/proxy 2.43.0 → 2.44.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +1228 -1100
- package/dist/cli.mjs +1192 -1064
- package/dist/dashboard.mjs +656 -156
- package/dist/index.js +185 -105
- package/dist/index.mjs +185 -105
- package/dist/scan-ink.mjs +5 -1
- package/package.json +1 -1
package/dist/dashboard.mjs
CHANGED
|
@@ -881,12 +881,12 @@ function judgeInlineCode(language, code, isProtected) {
|
|
|
881
881
|
category
|
|
882
882
|
};
|
|
883
883
|
}
|
|
884
|
-
const
|
|
885
|
-
if (
|
|
884
|
+
const path45 = protectedLiteral(text, isProtected);
|
|
885
|
+
if (path45) {
|
|
886
886
|
return {
|
|
887
887
|
outcome: "dangerous",
|
|
888
888
|
what: WHAT["reads-protected"],
|
|
889
|
-
evidence:
|
|
889
|
+
evidence: path45.slice(0, 60),
|
|
890
890
|
category: "reads-protected"
|
|
891
891
|
};
|
|
892
892
|
}
|
|
@@ -3049,9 +3049,9 @@ function ruleServerMatches(rule, serverLabel) {
|
|
|
3049
3049
|
if (rule.notServer && label2 && matchesPattern(label2, rule.notServer)) return false;
|
|
3050
3050
|
return true;
|
|
3051
3051
|
}
|
|
3052
|
-
function getNestedValue(obj,
|
|
3052
|
+
function getNestedValue(obj, path45) {
|
|
3053
3053
|
if (!obj || typeof obj !== "object") return null;
|
|
3054
|
-
const segments2 =
|
|
3054
|
+
const segments2 = path45.split(".");
|
|
3055
3055
|
for (const seg of segments2) {
|
|
3056
3056
|
if (FORBIDDEN_PATH_SEGMENTS.has(seg)) return null;
|
|
3057
3057
|
}
|
|
@@ -4031,8 +4031,8 @@ function collectFileAccesses(tool, args, context = {}, inspection = {}) {
|
|
|
4031
4031
|
}
|
|
4032
4032
|
return out;
|
|
4033
4033
|
}
|
|
4034
|
-
function within(
|
|
4035
|
-
return
|
|
4034
|
+
function within(path45, root) {
|
|
4035
|
+
return path45 === root || path45.startsWith(root === "/" ? "/" : root + "/");
|
|
4036
4036
|
}
|
|
4037
4037
|
function hits(access, paths, directories, context) {
|
|
4038
4038
|
const home = context.homeDirs?.[0] ?? "~";
|
|
@@ -5420,7 +5420,10 @@ function classify(inv) {
|
|
|
5420
5420
|
out.push({ ...f, shown: inv.shown, reason: `${f.reason}: ${inv.shown}` });
|
|
5421
5421
|
};
|
|
5422
5422
|
if (hooksBypass(inv)) found(HOOKS_BYPASS);
|
|
5423
|
-
|
|
5423
|
+
const staged = stagesEverything(inv);
|
|
5424
|
+
if (staged) {
|
|
5425
|
+
found(staged.ifWorkTreeTop === void 0 ? STAGE_ALL : { ...STAGE_ALL, ...staged });
|
|
5426
|
+
}
|
|
5424
5427
|
if (inv.sub === null) {
|
|
5425
5428
|
found(UNREADABLE);
|
|
5426
5429
|
return out;
|
|
@@ -5481,12 +5484,35 @@ function appliedValue(env, name) {
|
|
|
5481
5484
|
const word = f?.Stmts?.[0]?.Cmd?.Args?.[1];
|
|
5482
5485
|
return word ? resolveWordLiteral(word) : null;
|
|
5483
5486
|
}
|
|
5487
|
+
function lexicalPath(p) {
|
|
5488
|
+
if (/^[\\/~$]/.test(p) || /^[A-Za-z]:/.test(p)) return null;
|
|
5489
|
+
const parts = [];
|
|
5490
|
+
let climbed = false;
|
|
5491
|
+
for (const part of p.split(/[\\/]+/)) {
|
|
5492
|
+
if (part === "" || part === ".") continue;
|
|
5493
|
+
if (part === "..") {
|
|
5494
|
+
if (parts.length === 0) climbed = true;
|
|
5495
|
+
else parts.pop();
|
|
5496
|
+
} else parts.push(part);
|
|
5497
|
+
}
|
|
5498
|
+
return { parts, climbed };
|
|
5499
|
+
}
|
|
5500
|
+
function hooksPathBypasses(value) {
|
|
5501
|
+
if (value === null || value === void 0) return true;
|
|
5502
|
+
const v = value.trim();
|
|
5503
|
+
if (v === "" || v === "true") return true;
|
|
5504
|
+
const lp = lexicalPath(v);
|
|
5505
|
+
if (lp === null || lp.climbed || lp.parts.length === 0) return true;
|
|
5506
|
+
return lp.parts[0] === ".git" && lp.parts[1] !== "hooks";
|
|
5507
|
+
}
|
|
5484
5508
|
function hooksBypass(inv) {
|
|
5485
|
-
if (inv.config.has("core.hookspath")
|
|
5509
|
+
if (inv.config.has("core.hookspath") && hooksPathBypasses(inv.config.get("core.hookspath")))
|
|
5510
|
+
return true;
|
|
5486
5511
|
for (const [name, value] of inv.env) {
|
|
5487
5512
|
if (name === "GIT_CONFIG_PARAMETERS" && value !== null && /core\.hookspath/i.test(value))
|
|
5488
5513
|
return true;
|
|
5489
|
-
|
|
5514
|
+
const key = /^GIT_CONFIG_KEY_(\d+)$/.exec(name);
|
|
5515
|
+
if (key && value !== null && value.toLowerCase() === "core.hookspath" && hooksPathBypasses(inv.env.get(`GIT_CONFIG_VALUE_${key[1]}`)))
|
|
5490
5516
|
return true;
|
|
5491
5517
|
}
|
|
5492
5518
|
for (const name of HOOK_KILL_SWITCHES) {
|
|
@@ -5528,7 +5554,10 @@ function configWritesHooksPath(args) {
|
|
|
5528
5554
|
}
|
|
5529
5555
|
const key = args[i];
|
|
5530
5556
|
if (key === null || key === void 0 || key.toLowerCase() !== "core.hookspath") return false;
|
|
5531
|
-
|
|
5557
|
+
const unset = args.slice(0, i).some((w) => w === "--unset" || w === "--unset-all") || args[0] === "unset";
|
|
5558
|
+
if (unset) return true;
|
|
5559
|
+
if (i + 1 < args.length) return hooksPathBypasses(args[i + 1]);
|
|
5560
|
+
return write;
|
|
5532
5561
|
}
|
|
5533
5562
|
function isHookPath(word) {
|
|
5534
5563
|
return /(^|[\\/])(\.git[\\/]hooks|\.husky)([\\/]|$)/.test(word);
|
|
@@ -5563,31 +5592,49 @@ function chmodDisarmsHook(args, shown) {
|
|
|
5563
5592
|
function wholeTree(operand) {
|
|
5564
5593
|
if (/^:\(top\)\.?$/.test(operand) || operand === ":/" || operand === "*") return true;
|
|
5565
5594
|
if (operand.startsWith(":")) return false;
|
|
5595
|
+
const lp = lexicalPath(operand);
|
|
5596
|
+
return lp !== null && lp.parts.length === 0;
|
|
5597
|
+
}
|
|
5598
|
+
function chdirPrefix(inv) {
|
|
5599
|
+
const dirs = inv.chdir ?? [];
|
|
5600
|
+
if (dirs.length === 0) return null;
|
|
5566
5601
|
const parts = [];
|
|
5567
|
-
for (const
|
|
5568
|
-
if (
|
|
5569
|
-
|
|
5570
|
-
|
|
5602
|
+
for (const d of dirs) {
|
|
5603
|
+
if (d === null || d === "") return null;
|
|
5604
|
+
const lp = lexicalPath(d);
|
|
5605
|
+
if (lp === null || lp.climbed) return null;
|
|
5606
|
+
parts.push(...lp.parts);
|
|
5571
5607
|
}
|
|
5572
|
-
return parts.length === 0
|
|
5608
|
+
return parts.length === 0 ? null : parts.join("/");
|
|
5573
5609
|
}
|
|
5574
5610
|
function stagesEverything(inv) {
|
|
5575
5611
|
const sub = inv.sub?.toLowerCase();
|
|
5612
|
+
const prefix = chdirPrefix(inv);
|
|
5613
|
+
const judge = (operands2) => {
|
|
5614
|
+
let conditional = false;
|
|
5615
|
+
for (const operand of operands2) {
|
|
5616
|
+
if (!wholeTree(operand)) continue;
|
|
5617
|
+
if (prefix === null || operand.startsWith(":") || operand.startsWith("/")) return {};
|
|
5618
|
+
if (wholeTree(`${prefix}/${operand === "*" ? "." : operand}`)) return {};
|
|
5619
|
+
conditional = true;
|
|
5620
|
+
}
|
|
5621
|
+
return conditional && prefix !== null ? { ifWorkTreeTop: prefix } : null;
|
|
5622
|
+
};
|
|
5576
5623
|
if (sub === "add") {
|
|
5577
5624
|
const o = parseOptions(inv.args, "", ADD_LONG_VALUE);
|
|
5578
5625
|
if (o.flags.has("-n") || o.flags.has("--dry-run") || o.flags.has("-i") || o.flags.has("--interactive") || o.flags.has("-p") || o.flags.has("--patch") || o.flags.has("-e") || o.flags.has("--edit"))
|
|
5579
|
-
return
|
|
5626
|
+
return null;
|
|
5580
5627
|
const all = o.flags.has("-A") || o.flags.has("--all") || o.flags.has("--no-ignore-removal") || o.flags.has("-u") || o.flags.has("--update");
|
|
5581
|
-
if (all && o.operands.length === 0) return
|
|
5582
|
-
return o.operands
|
|
5628
|
+
if (all && o.operands.length === 0) return {};
|
|
5629
|
+
return judge(o.operands);
|
|
5583
5630
|
}
|
|
5584
5631
|
if (sub === "commit") {
|
|
5585
5632
|
const o = parseOptions(inv.args, "mFCcut", COMMIT_LONG_VALUE);
|
|
5586
|
-
if (o.flags.has("--dry-run")) return
|
|
5587
|
-
if (o.flags.has("-a") || o.flags.has("--all")) return
|
|
5588
|
-
return o.operands
|
|
5633
|
+
if (o.flags.has("--dry-run")) return null;
|
|
5634
|
+
if (o.flags.has("-a") || o.flags.has("--all")) return {};
|
|
5635
|
+
return judge(o.operands);
|
|
5589
5636
|
}
|
|
5590
|
-
return
|
|
5637
|
+
return null;
|
|
5591
5638
|
}
|
|
5592
5639
|
function hookTextRunsNothing(text) {
|
|
5593
5640
|
return text.split(/\r?\n/).every((line) => INERT_LINE_RE.test(line.trim()));
|
|
@@ -7243,9 +7290,9 @@ function bareToolName(toolName) {
|
|
|
7243
7290
|
const parts = toolName.split("__");
|
|
7244
7291
|
return (parts.length >= 3 ? parts.slice(2).join("__") : toolName).toLowerCase();
|
|
7245
7292
|
}
|
|
7246
|
-
function valuesAt(args,
|
|
7293
|
+
function valuesAt(args, path45) {
|
|
7247
7294
|
let cursors = [args];
|
|
7248
|
-
for (const rawSegment of
|
|
7295
|
+
for (const rawSegment of path45.split(".")) {
|
|
7249
7296
|
const isArray = rawSegment.endsWith("[]");
|
|
7250
7297
|
const key = isArray ? rawSegment.slice(0, -2) : rawSegment;
|
|
7251
7298
|
const next = [];
|
|
@@ -7276,8 +7323,8 @@ function extractToolDestinations(toolName, args) {
|
|
|
7276
7323
|
try {
|
|
7277
7324
|
const out = [];
|
|
7278
7325
|
const paths = DESTINATION_ARGS.get(bareToolName(toolName)) ?? [];
|
|
7279
|
-
for (const
|
|
7280
|
-
for (const value of valuesAt(args,
|
|
7326
|
+
for (const path45 of paths) {
|
|
7327
|
+
for (const value of valuesAt(args, path45)) {
|
|
7281
7328
|
const host2 = hostOf(value);
|
|
7282
7329
|
if (host2) out.push({ host: host2, binary: toolName, raw: value });
|
|
7283
7330
|
}
|
|
@@ -7303,8 +7350,8 @@ function ssrfDestinationFloor(toolName, args, opts = {}) {
|
|
|
7303
7350
|
try {
|
|
7304
7351
|
const paths = DESTINATION_ARGS.get(bareToolName(toolName));
|
|
7305
7352
|
if (!paths) return null;
|
|
7306
|
-
for (const
|
|
7307
|
-
for (const value of valuesAt(args,
|
|
7353
|
+
for (const path45 of paths) {
|
|
7354
|
+
for (const value of valuesAt(args, path45)) {
|
|
7308
7355
|
const host2 = hostOf(value);
|
|
7309
7356
|
if (!host2) continue;
|
|
7310
7357
|
const m = classifySsrf(host2);
|
|
@@ -7313,7 +7360,7 @@ function ssrfDestinationFloor(toolName, args, opts = {}) {
|
|
|
7313
7360
|
const decision = ssrfFloorDecision(m, written, opts);
|
|
7314
7361
|
if (decision === null) continue;
|
|
7315
7362
|
const reason = decision === "review" ? ssrfDisguisedReason(m, written) : ssrfReason(m, host2);
|
|
7316
|
-
return { ...m, argPath:
|
|
7363
|
+
return { ...m, argPath: path45, host: host2, reason, decision };
|
|
7317
7364
|
}
|
|
7318
7365
|
}
|
|
7319
7366
|
return null;
|
|
@@ -7595,7 +7642,7 @@ async function evaluatePolicy(config, toolName, args, context = {}, hostHooks =
|
|
|
7595
7642
|
}
|
|
7596
7643
|
async function evaluatePolicyCore(config, toolName, args, context = {}, hooks = {}) {
|
|
7597
7644
|
const { agent, cwd, activeEnvironment } = context;
|
|
7598
|
-
const { checkProvenance: checkProvenance2, isTrustedHost: isTrustedHost2, isConfiguredRegistry } = hooks;
|
|
7645
|
+
const { checkProvenance: checkProvenance2, isTrustedHost: isTrustedHost2, isConfiguredRegistry, isGitWorkTreeTop: isGitWorkTreeTop2 } = hooks;
|
|
7599
7646
|
const settingsView = catalogSettings(config);
|
|
7600
7647
|
const layers = inspectionLayers(config.policy);
|
|
7601
7648
|
const cv = (id) => checkValue(settingsView, id);
|
|
@@ -7749,6 +7796,8 @@ async function evaluatePolicyCore(config, toolName, args, context = {}, hooks =
|
|
|
7749
7796
|
const action = cv(id);
|
|
7750
7797
|
const finding = action === "off" ? void 0 : git.findings.find((f) => f.checkId === id);
|
|
7751
7798
|
if (!finding) continue;
|
|
7799
|
+
if (finding.ifWorkTreeTop !== void 0 && isGitWorkTreeTop2 && cwd && !isGitWorkTreeTop2(finding.ifWorkTreeTop, cwd))
|
|
7800
|
+
continue;
|
|
7752
7801
|
const v = applyCheckValue(action, {
|
|
7753
7802
|
decision: "review",
|
|
7754
7803
|
blockedByLabel: `Node9 (AST): ${finding.ruleName}`,
|
|
@@ -16360,6 +16409,7 @@ zoo`.split("\n");
|
|
|
16360
16409
|
/** The words after `git`: global options, then the subcommand. */
|
|
16361
16410
|
visitGit(rest, env, depth, viaXargs, fromAlias, shown) {
|
|
16362
16411
|
const config = /* @__PURE__ */ new Map();
|
|
16412
|
+
const chdir = [];
|
|
16363
16413
|
let i = 0;
|
|
16364
16414
|
while (i < rest.length) {
|
|
16365
16415
|
const w = rest[i];
|
|
@@ -16374,6 +16424,7 @@ zoo`.split("\n");
|
|
|
16374
16424
|
i++;
|
|
16375
16425
|
value = i < rest.length ? rest[i] : null;
|
|
16376
16426
|
}
|
|
16427
|
+
if (name === "-C") chdir.push(viaXargs || (value ?? "").includes("{}") ? null : value);
|
|
16377
16428
|
if (name === "-c" && value !== null) {
|
|
16378
16429
|
const ceq = value.indexOf("=");
|
|
16379
16430
|
const key = (ceq > 0 ? value.slice(0, ceq) : value).toLowerCase();
|
|
@@ -16390,7 +16441,7 @@ zoo`.split("\n");
|
|
|
16390
16441
|
}
|
|
16391
16442
|
const subWord = i < rest.length ? rest[i] : void 0;
|
|
16392
16443
|
const args = rest.slice(i + 1);
|
|
16393
|
-
const inv = { sub: null, args, config, env, shown };
|
|
16444
|
+
const inv = { sub: null, args, config, env, shown, chdir };
|
|
16394
16445
|
if (subWord === void 0) {
|
|
16395
16446
|
if (!viaXargs) return;
|
|
16396
16447
|
this.emit(inv);
|
|
@@ -16404,16 +16455,23 @@ zoo`.split("\n");
|
|
|
16404
16455
|
const fromConfig = config.get(`alias.${subWord.toLowerCase()}`);
|
|
16405
16456
|
const alias = fromConfig !== void 0 ? fromConfig : this.aliases.get(subWord);
|
|
16406
16457
|
if (alias !== void 0 && !fromAlias) {
|
|
16407
|
-
this.expandAlias(subWord, alias, args, env, depth, 0, shown);
|
|
16458
|
+
this.expandAlias(subWord, alias, args, env, depth, 0, shown, chdir);
|
|
16408
16459
|
return;
|
|
16409
16460
|
}
|
|
16410
16461
|
inv.sub = subWord;
|
|
16411
16462
|
this.emit(inv);
|
|
16412
16463
|
this.recordAliasDefinition(inv, env, depth);
|
|
16413
16464
|
}
|
|
16414
|
-
expandAlias(name, value, args, env, depth, level, shown) {
|
|
16465
|
+
expandAlias(name, value, args, env, depth, level, shown, chdir) {
|
|
16415
16466
|
if (value === null) {
|
|
16416
|
-
this.emit({
|
|
16467
|
+
this.emit({
|
|
16468
|
+
sub: null,
|
|
16469
|
+
args,
|
|
16470
|
+
config: /* @__PURE__ */ new Map(),
|
|
16471
|
+
env,
|
|
16472
|
+
shown: `${shown} (alias ${name})`,
|
|
16473
|
+
chdir
|
|
16474
|
+
});
|
|
16417
16475
|
return;
|
|
16418
16476
|
}
|
|
16419
16477
|
if (value.startsWith("!")) {
|
|
@@ -16427,7 +16485,16 @@ zoo`.split("\n");
|
|
|
16427
16485
|
const nested = this.aliases.get(sub);
|
|
16428
16486
|
const expandedShown = `${shown} (alias ${name} = ${value})`;
|
|
16429
16487
|
if (nested !== void 0 && level < MAX_ALIAS_DEPTH) {
|
|
16430
|
-
this.expandAlias(
|
|
16488
|
+
this.expandAlias(
|
|
16489
|
+
sub,
|
|
16490
|
+
nested,
|
|
16491
|
+
[...words.slice(1), ...args],
|
|
16492
|
+
env,
|
|
16493
|
+
depth,
|
|
16494
|
+
level + 1,
|
|
16495
|
+
shown,
|
|
16496
|
+
chdir
|
|
16497
|
+
);
|
|
16431
16498
|
return;
|
|
16432
16499
|
}
|
|
16433
16500
|
this.emit({
|
|
@@ -16435,7 +16502,8 @@ zoo`.split("\n");
|
|
|
16435
16502
|
args: [...words.slice(1), ...args],
|
|
16436
16503
|
config: /* @__PURE__ */ new Map(),
|
|
16437
16504
|
env,
|
|
16438
|
-
shown: expandedShown
|
|
16505
|
+
shown: expandedShown,
|
|
16506
|
+
chdir
|
|
16439
16507
|
});
|
|
16440
16508
|
}
|
|
16441
16509
|
/**
|
|
@@ -16469,7 +16537,7 @@ zoo`.split("\n");
|
|
|
16469
16537
|
if (i + 1 >= args.length) return;
|
|
16470
16538
|
const value = args[i + 1];
|
|
16471
16539
|
this.aliases.set(m[1], value);
|
|
16472
|
-
this.expandAlias(m[1], value, [], env, depth, 0, `${inv.shown}
|
|
16540
|
+
this.expandAlias(m[1], value, [], env, depth, 0, `${inv.shown}`, []);
|
|
16473
16541
|
}
|
|
16474
16542
|
};
|
|
16475
16543
|
POSITIONAL2 = "\0positional";
|
|
@@ -17971,7 +18039,7 @@ zoo`.split("\n");
|
|
|
17971
18039
|
{ view: "separators-stripped", decoder: "separators", stripped: true }
|
|
17972
18040
|
];
|
|
17973
18041
|
LONG_OUTPUT_THRESHOLD_BYTES = 100 * 1024;
|
|
17974
|
-
CANONICAL_EXTRACTOR_VERSION = "canonical-
|
|
18042
|
+
CANONICAL_EXTRACTOR_VERSION = "canonical-v35";
|
|
17975
18043
|
}
|
|
17976
18044
|
});
|
|
17977
18045
|
|
|
@@ -18666,8 +18734,7 @@ function homeCredentialFiles(home) {
|
|
|
18666
18734
|
),
|
|
18667
18735
|
f([".docker", "config.json"], "Docker registry auth tokens", 10),
|
|
18668
18736
|
f([".netrc"], "FTP/HTTP credentials in plain text", 15),
|
|
18669
|
-
f([".npmrc"], "npm auth token: can publish packages as you", 10)
|
|
18670
|
-
f([".node9", "credentials.json"], "Node9 cloud API key", 10)
|
|
18737
|
+
f([".npmrc"], "npm auth token: can publish packages as you", 10)
|
|
18671
18738
|
];
|
|
18672
18739
|
}
|
|
18673
18740
|
var init_credential_files = __esm({
|
|
@@ -19316,8 +19383,8 @@ var init_daemon = __esm({
|
|
|
19316
19383
|
import { z } from "zod";
|
|
19317
19384
|
function formatIssues(issues) {
|
|
19318
19385
|
const lines = issues.map((issue) => {
|
|
19319
|
-
const
|
|
19320
|
-
return ` \u2022 ${
|
|
19386
|
+
const path45 = issue.path.length > 0 ? issue.path.map(String).join(".") : "root";
|
|
19387
|
+
return ` \u2022 ${path45}: ${issue.message}`;
|
|
19321
19388
|
});
|
|
19322
19389
|
return `Invalid config:
|
|
19323
19390
|
${lines.join("\n")}`;
|
|
@@ -19330,9 +19397,9 @@ function prunePaths(root, paths) {
|
|
|
19330
19397
|
const yi = y[y.length - 1];
|
|
19331
19398
|
return typeof xi === "number" && typeof yi === "number" ? yi - xi : 0;
|
|
19332
19399
|
});
|
|
19333
|
-
for (const
|
|
19400
|
+
for (const path45 of ordered) {
|
|
19334
19401
|
let cur = root;
|
|
19335
|
-
for (const key of
|
|
19402
|
+
for (const key of path45.slice(0, -1)) {
|
|
19336
19403
|
if (cur === null || typeof cur !== "object") {
|
|
19337
19404
|
cur = void 0;
|
|
19338
19405
|
break;
|
|
@@ -19340,7 +19407,7 @@ function prunePaths(root, paths) {
|
|
|
19340
19407
|
cur = cur[key];
|
|
19341
19408
|
}
|
|
19342
19409
|
if (cur === null || typeof cur !== "object") continue;
|
|
19343
|
-
const last =
|
|
19410
|
+
const last = path45[path45.length - 1];
|
|
19344
19411
|
if (Array.isArray(cur)) {
|
|
19345
19412
|
const i = Number(last);
|
|
19346
19413
|
if (Number.isInteger(i) && i >= 0 && i < cur.length) {
|
|
@@ -19376,7 +19443,7 @@ function sanitizeConfig(raw) {
|
|
|
19376
19443
|
return issue.keys.map((k) => [...at, k]);
|
|
19377
19444
|
}
|
|
19378
19445
|
return [at.slice(0, -level || void 0)];
|
|
19379
|
-
}).filter((
|
|
19446
|
+
}).filter((path45) => path45.length > 0);
|
|
19380
19447
|
if (paths.length === 0 || !prunePaths(working, paths)) break;
|
|
19381
19448
|
}
|
|
19382
19449
|
if (ConfigFileSchema.safeParse(working).success) break;
|
|
@@ -20700,13 +20767,13 @@ function getConfig(cwd) {
|
|
|
20700
20767
|
}
|
|
20701
20768
|
if (Array.isArray(mc.jailPaths)) {
|
|
20702
20769
|
for (const jp of mc.jailPaths) {
|
|
20703
|
-
const
|
|
20704
|
-
if (!
|
|
20770
|
+
const path45 = typeof jp?.path === "string" ? jp.path.trim() : "";
|
|
20771
|
+
if (!path45) continue;
|
|
20705
20772
|
const verdict = jp?.verdict === "review" ? "review" : "block";
|
|
20706
|
-
for (const r of pathRules(
|
|
20773
|
+
for (const r of pathRules(path45, verdict, "org-managed jail")) {
|
|
20707
20774
|
mergedPolicy.smartRules.push({ ...r, name: `org:${r.name}` });
|
|
20708
20775
|
}
|
|
20709
|
-
mergedPolicy.managedJailPaths.push({ path:
|
|
20776
|
+
mergedPolicy.managedJailPaths.push({ path: path45, verdict });
|
|
20710
20777
|
}
|
|
20711
20778
|
}
|
|
20712
20779
|
if (Array.isArray(mc.trustedHosts)) {
|
|
@@ -21417,6 +21484,68 @@ var init_cost_gemini = __esm({
|
|
|
21417
21484
|
});
|
|
21418
21485
|
|
|
21419
21486
|
// src/cost-copilot.ts
|
|
21487
|
+
function priceTokens(model, u) {
|
|
21488
|
+
const tuple = pricingFor(model);
|
|
21489
|
+
if (!tuple || !u) return 0;
|
|
21490
|
+
const [pin, pout, pcw, pcr] = tuple;
|
|
21491
|
+
return (u.inputTokens ?? 0) * pin + (u.outputTokens ?? 0) * pout + (u.cacheWriteTokens ?? 0) * pcw + (u.cacheReadTokens ?? 0) * pcr;
|
|
21492
|
+
}
|
|
21493
|
+
function parseCopilotSession(lines) {
|
|
21494
|
+
let sessionId = "";
|
|
21495
|
+
let cwd = "";
|
|
21496
|
+
let startDate = "";
|
|
21497
|
+
let shutdownDate = "";
|
|
21498
|
+
let modelMetrics = null;
|
|
21499
|
+
for (const raw of lines) {
|
|
21500
|
+
if (!raw.trim()) continue;
|
|
21501
|
+
let o;
|
|
21502
|
+
try {
|
|
21503
|
+
o = JSON.parse(raw);
|
|
21504
|
+
} catch {
|
|
21505
|
+
continue;
|
|
21506
|
+
}
|
|
21507
|
+
const d = o.data ?? {};
|
|
21508
|
+
if (o.type === "session.start") {
|
|
21509
|
+
if (typeof d["sessionId"] === "string") sessionId = d["sessionId"];
|
|
21510
|
+
if (typeof d["startTime"] === "string") startDate = d["startTime"];
|
|
21511
|
+
const ctx = d["context"] ?? {};
|
|
21512
|
+
if (typeof ctx["cwd"] === "string") cwd = ctx["cwd"];
|
|
21513
|
+
} else if (o.type === "session.shutdown") {
|
|
21514
|
+
if (d["modelMetrics"] && typeof d["modelMetrics"] === "object") {
|
|
21515
|
+
modelMetrics = d["modelMetrics"];
|
|
21516
|
+
}
|
|
21517
|
+
if (typeof o.timestamp === "string") shutdownDate = o.timestamp;
|
|
21518
|
+
}
|
|
21519
|
+
}
|
|
21520
|
+
if (!modelMetrics) return [];
|
|
21521
|
+
const date = (startDate || shutdownDate).slice(0, 10);
|
|
21522
|
+
if (!date) return [];
|
|
21523
|
+
const rows = [];
|
|
21524
|
+
for (const [rawModel, m] of Object.entries(modelMetrics)) {
|
|
21525
|
+
const u = m.usage ?? {};
|
|
21526
|
+
const inputTokens = u.inputTokens ?? 0;
|
|
21527
|
+
const outputTokens = u.outputTokens ?? 0;
|
|
21528
|
+
const cacheReadTokens = u.cacheReadTokens ?? 0;
|
|
21529
|
+
const cacheWriteTokens = u.cacheWriteTokens ?? 0;
|
|
21530
|
+
if (inputTokens === 0 && outputTokens === 0 && cacheReadTokens === 0 && cacheWriteTokens === 0) {
|
|
21531
|
+
continue;
|
|
21532
|
+
}
|
|
21533
|
+
const model = normalizeModel(rawModel);
|
|
21534
|
+
const costUSD = typeof m.requests?.cost === "number" && m.requests.cost > 0 ? m.requests.cost : priceTokens(rawModel, u);
|
|
21535
|
+
rows.push({
|
|
21536
|
+
date,
|
|
21537
|
+
model,
|
|
21538
|
+
workingDir: cwd,
|
|
21539
|
+
runId: sessionId,
|
|
21540
|
+
costUSD,
|
|
21541
|
+
inputTokens,
|
|
21542
|
+
outputTokens,
|
|
21543
|
+
cacheReadTokens,
|
|
21544
|
+
cacheWriteTokens
|
|
21545
|
+
});
|
|
21546
|
+
}
|
|
21547
|
+
return rows;
|
|
21548
|
+
}
|
|
21420
21549
|
var init_cost_copilot = __esm({
|
|
21421
21550
|
"src/cost-copilot.ts"() {
|
|
21422
21551
|
"use strict";
|
|
@@ -24920,6 +25049,7 @@ var init_provenance2 = __esm({
|
|
|
24920
25049
|
|
|
24921
25050
|
// src/policy/index.ts
|
|
24922
25051
|
import fs30 from "fs";
|
|
25052
|
+
import path32 from "path";
|
|
24923
25053
|
async function evaluatePolicy2(toolName, args, agent, cwd, opts) {
|
|
24924
25054
|
const config = opts?.config ?? getConfig(cwd);
|
|
24925
25055
|
const prepared = opts?.fileAccess ?? prepareFileAccesses(toolName, args, cwd, inspectionLayers(config.policy));
|
|
@@ -24945,10 +25075,19 @@ async function evaluatePolicy2(toolName, args, agent, cwd, opts) {
|
|
|
24945
25075
|
// reaches a long-lived in-process authorizer (the gateway) within seconds.
|
|
24946
25076
|
isTrustedHost: config.policy.trustedHostsManaged ? (host2) => matchesTrustedHost(host2, config.policy.trustedHosts) : isTrustedHost,
|
|
24947
25077
|
readAgentSettings,
|
|
24948
|
-
isConfiguredRegistry: (host2, at) => configuredRegistryHosts(at).has(host2)
|
|
25078
|
+
isConfiguredRegistry: (host2, at) => configuredRegistryHosts(at).has(host2),
|
|
25079
|
+
isGitWorkTreeTop
|
|
24949
25080
|
}
|
|
24950
25081
|
);
|
|
24951
25082
|
}
|
|
25083
|
+
function isGitWorkTreeTop(dir2, cwd) {
|
|
25084
|
+
if (!path32.isAbsolute(cwd)) return true;
|
|
25085
|
+
try {
|
|
25086
|
+
return fs30.existsSync(path32.join(path32.resolve(cwd, dir2), ".git"));
|
|
25087
|
+
} catch {
|
|
25088
|
+
return true;
|
|
25089
|
+
}
|
|
25090
|
+
}
|
|
24952
25091
|
function readAgentSettings(absPath) {
|
|
24953
25092
|
const r = readCappedText(
|
|
24954
25093
|
absPath,
|
|
@@ -24983,7 +25122,7 @@ var init_policy = __esm({
|
|
|
24983
25122
|
|
|
24984
25123
|
// src/auth/state.ts
|
|
24985
25124
|
import fs31 from "fs";
|
|
24986
|
-
import
|
|
25125
|
+
import path33 from "path";
|
|
24987
25126
|
import os22 from "os";
|
|
24988
25127
|
function extractCommandPattern(toolName, args) {
|
|
24989
25128
|
const lower2 = toolName.toLowerCase();
|
|
@@ -25061,7 +25200,7 @@ function writeTrustSession(toolName, durationMs, args) {
|
|
|
25061
25200
|
}
|
|
25062
25201
|
function getPersistentDecision(toolName) {
|
|
25063
25202
|
try {
|
|
25064
|
-
const file2 =
|
|
25203
|
+
const file2 = path33.join(os22.homedir(), ".node9", "decisions.json");
|
|
25065
25204
|
if (!fs31.existsSync(file2)) return null;
|
|
25066
25205
|
const decisions = JSON.parse(fs31.readFileSync(file2, "utf-8"));
|
|
25067
25206
|
const d = decisions[toolName];
|
|
@@ -25077,15 +25216,15 @@ var init_state2 = __esm({
|
|
|
25077
25216
|
init_policy();
|
|
25078
25217
|
init_atomic_write();
|
|
25079
25218
|
init_forecast_context();
|
|
25080
|
-
PAUSED_FILE =
|
|
25081
|
-
TRUST_FILE =
|
|
25219
|
+
PAUSED_FILE = path33.join(os22.homedir(), ".node9", "PAUSED");
|
|
25220
|
+
TRUST_FILE = path33.join(os22.homedir(), ".node9", "trust.json");
|
|
25082
25221
|
}
|
|
25083
25222
|
});
|
|
25084
25223
|
|
|
25085
25224
|
// src/auth/cloud.ts
|
|
25086
25225
|
import fs32 from "fs";
|
|
25087
25226
|
import os23 from "os";
|
|
25088
|
-
import
|
|
25227
|
+
import path34 from "path";
|
|
25089
25228
|
function maskArgsForCloud(args) {
|
|
25090
25229
|
if (args === void 0 || args === null) return args;
|
|
25091
25230
|
try {
|
|
@@ -25118,7 +25257,7 @@ async function initNode9SaaS(toolName, args, creds, meta, riskMetadata, agentPol
|
|
|
25118
25257
|
let ciContext;
|
|
25119
25258
|
if (process.env.CI) {
|
|
25120
25259
|
try {
|
|
25121
|
-
const ciContextPath =
|
|
25260
|
+
const ciContextPath = path34.join(os23.homedir(), ".node9", "ci-context.json");
|
|
25122
25261
|
const ci = readCappedText(ciContextPath, 1e4);
|
|
25123
25262
|
if (!ci) throw new Error("ci-context.json unreadable");
|
|
25124
25263
|
if (ci.truncated) throw new Error("ci-context.json exceeds 10 KB");
|
|
@@ -25265,10 +25404,10 @@ var init_cloud = __esm({
|
|
|
25265
25404
|
|
|
25266
25405
|
// src/loop-detector.ts
|
|
25267
25406
|
import fs33 from "fs";
|
|
25268
|
-
import
|
|
25407
|
+
import path35 from "path";
|
|
25269
25408
|
import os24 from "os";
|
|
25270
25409
|
function loopStateFile() {
|
|
25271
|
-
return
|
|
25410
|
+
return path35.join(os24.homedir(), ".node9", "loop-state.json");
|
|
25272
25411
|
}
|
|
25273
25412
|
function readState() {
|
|
25274
25413
|
try {
|
|
@@ -25282,7 +25421,7 @@ function readState() {
|
|
|
25282
25421
|
}
|
|
25283
25422
|
}
|
|
25284
25423
|
function writeState(records) {
|
|
25285
|
-
const dir2 =
|
|
25424
|
+
const dir2 = path35.dirname(loopStateFile());
|
|
25286
25425
|
if (!fs33.existsSync(dir2)) fs33.mkdirSync(dir2, { recursive: true });
|
|
25287
25426
|
const tmpPath = `${loopStateFile()}.${os24.hostname()}.${process.pid}.tmp`;
|
|
25288
25427
|
fs33.writeFileSync(tmpPath, JSON.stringify(records));
|
|
@@ -25445,7 +25584,7 @@ var init_card_result = __esm({
|
|
|
25445
25584
|
|
|
25446
25585
|
// src/auth/orchestrator.ts
|
|
25447
25586
|
import os25 from "os";
|
|
25448
|
-
import
|
|
25587
|
+
import path36 from "path";
|
|
25449
25588
|
function isProtectedPathRule(ruleName) {
|
|
25450
25589
|
if (!ruleName) return false;
|
|
25451
25590
|
return /^(?:org:)?block-path-/.test(ruleName) || /^shield:project-jail:block-read-/.test(ruleName);
|
|
@@ -25492,9 +25631,9 @@ function isMcpToolName(toolName) {
|
|
|
25492
25631
|
return /^mcp__[^_](?:[^_]|_(?!_))*?__/i.test(toolName);
|
|
25493
25632
|
}
|
|
25494
25633
|
function absoluteUploadPath(spelled, cwd) {
|
|
25495
|
-
const expanded = spelled === "~" || spelled.startsWith("~/") ?
|
|
25496
|
-
if (
|
|
25497
|
-
return cwd &&
|
|
25634
|
+
const expanded = spelled === "~" || spelled.startsWith("~/") ? path36.join(os25.homedir(), spelled.slice(1)) : spelled;
|
|
25635
|
+
if (path36.isAbsolute(expanded)) return expanded;
|
|
25636
|
+
return cwd && path36.isAbsolute(cwd) ? path36.resolve(cwd, expanded) : expanded;
|
|
25498
25637
|
}
|
|
25499
25638
|
function extractFilePaths(toolName, args, shellCommand) {
|
|
25500
25639
|
const paths = [];
|
|
@@ -27111,17 +27250,17 @@ var init_forecast = __esm({
|
|
|
27111
27250
|
|
|
27112
27251
|
// src/version.ts
|
|
27113
27252
|
import fs34 from "fs";
|
|
27114
|
-
import
|
|
27253
|
+
import path37 from "path";
|
|
27115
27254
|
function readVersion() {
|
|
27116
27255
|
let dir2 = __dirname;
|
|
27117
27256
|
for (let i = 0; i < 5; i++) {
|
|
27118
|
-
const p =
|
|
27257
|
+
const p = path37.join(dir2, "package.json");
|
|
27119
27258
|
try {
|
|
27120
27259
|
const pkg = JSON.parse(fs34.readFileSync(p, "utf-8"));
|
|
27121
27260
|
if (pkg.name === "@node9/proxy" && typeof pkg.version === "string") return pkg.version;
|
|
27122
27261
|
} catch {
|
|
27123
27262
|
}
|
|
27124
|
-
const parent =
|
|
27263
|
+
const parent = path37.dirname(dir2);
|
|
27125
27264
|
if (parent === dir2) break;
|
|
27126
27265
|
dir2 = parent;
|
|
27127
27266
|
}
|
|
@@ -27138,10 +27277,10 @@ var init_version = __esm({
|
|
|
27138
27277
|
// src/cli/commands/scan-cache.ts
|
|
27139
27278
|
import fs35 from "fs";
|
|
27140
27279
|
import os26 from "os";
|
|
27141
|
-
import
|
|
27280
|
+
import path38 from "path";
|
|
27142
27281
|
import { createHash as createHash5 } from "crypto";
|
|
27143
27282
|
function scanCacheRoot() {
|
|
27144
|
-
return
|
|
27283
|
+
return path38.join(os26.homedir(), ".node9", "scan-cache");
|
|
27145
27284
|
}
|
|
27146
27285
|
function configHashOf(config) {
|
|
27147
27286
|
const h = createHash5("sha256");
|
|
@@ -27149,7 +27288,7 @@ function configHashOf(config) {
|
|
|
27149
27288
|
h.update(JSON.stringify(config.policySource));
|
|
27150
27289
|
h.update(JSON.stringify(config.policy));
|
|
27151
27290
|
for (const name of SIDECAR_FILES) {
|
|
27152
|
-
const st = fileStamp(
|
|
27291
|
+
const st = fileStamp(path38.join(os26.homedir(), ".node9", name));
|
|
27153
27292
|
h.update(`|${name}:${st ? `${st.size}:${st.mtimeMs}` : "absent"}`);
|
|
27154
27293
|
}
|
|
27155
27294
|
return h.digest("hex").slice(0, 16);
|
|
@@ -27180,8 +27319,8 @@ var init_scan_cache = __esm({
|
|
|
27180
27319
|
"trusted-hosts.json",
|
|
27181
27320
|
"trusted-recipients.json",
|
|
27182
27321
|
"rules-cache.json",
|
|
27183
|
-
|
|
27184
|
-
|
|
27322
|
+
path38.join("osv", "npm", "meta.json"),
|
|
27323
|
+
path38.join("osv", "PyPI", "meta.json")
|
|
27185
27324
|
];
|
|
27186
27325
|
ScanCache = class {
|
|
27187
27326
|
root;
|
|
@@ -27210,7 +27349,7 @@ var init_scan_cache = __esm({
|
|
|
27210
27349
|
if (!this.enabled) return fresh;
|
|
27211
27350
|
try {
|
|
27212
27351
|
const raw = JSON.parse(
|
|
27213
|
-
fs35.readFileSync(
|
|
27352
|
+
fs35.readFileSync(path38.join(this.root, "index.json"), "utf8")
|
|
27214
27353
|
);
|
|
27215
27354
|
if (raw.cacheVersion !== SCAN_CACHE_VERSION || raw.engineVersion !== CANONICAL_EXTRACTOR_VERSION || raw.proxyVersion !== PROXY_VERSION || raw.configHash !== configHash || typeof raw.files !== "object") {
|
|
27216
27355
|
this.dropAll();
|
|
@@ -27247,7 +27386,7 @@ var init_scan_cache = __esm({
|
|
|
27247
27386
|
}
|
|
27248
27387
|
}
|
|
27249
27388
|
try {
|
|
27250
|
-
const stored = JSON.parse(fs35.readFileSync(
|
|
27389
|
+
const stored = JSON.parse(fs35.readFileSync(path38.join(this.root, e.result), "utf8"));
|
|
27251
27390
|
if ((stored.judgedFrom ?? null) !== (e.judgedFrom ?? null) || !stored.result) {
|
|
27252
27391
|
this.misses++;
|
|
27253
27392
|
return null;
|
|
@@ -27262,13 +27401,13 @@ var init_scan_cache = __esm({
|
|
|
27262
27401
|
put(filePath, stamp, agent, result, hasher, judgeFrom = null) {
|
|
27263
27402
|
if (!this.enabled) return;
|
|
27264
27403
|
const name = createHash5("sha256").update(`${agent}|${filePath}`).digest("hex").slice(0, 40);
|
|
27265
|
-
const rel =
|
|
27404
|
+
const rel = path38.join("files", name.slice(0, 2), `${name}.json`);
|
|
27266
27405
|
const configHashes = {};
|
|
27267
27406
|
if (hasher)
|
|
27268
27407
|
for (const cwd of result.configCwds ?? []) configHashes[cwd] = hasher.configHashFor(cwd);
|
|
27269
27408
|
try {
|
|
27270
|
-
const abs =
|
|
27271
|
-
fs35.mkdirSync(
|
|
27409
|
+
const abs = path38.join(this.root, rel);
|
|
27410
|
+
fs35.mkdirSync(path38.dirname(abs), { recursive: true, mode: 448 });
|
|
27272
27411
|
fs35.writeFileSync(
|
|
27273
27412
|
abs,
|
|
27274
27413
|
JSON.stringify({ judgedFrom: judgeFrom ? judgeFrom.toISOString() : null, result }),
|
|
@@ -27294,7 +27433,7 @@ var init_scan_cache = __esm({
|
|
|
27294
27433
|
for (const [key, e] of Object.entries(this.index.files)) {
|
|
27295
27434
|
if (fs35.existsSync(key)) continue;
|
|
27296
27435
|
try {
|
|
27297
|
-
fs35.rmSync(
|
|
27436
|
+
fs35.rmSync(path38.join(this.root, e.result), { force: true });
|
|
27298
27437
|
} catch {
|
|
27299
27438
|
}
|
|
27300
27439
|
delete this.index.files[key];
|
|
@@ -27316,9 +27455,9 @@ var init_scan_cache = __esm({
|
|
|
27316
27455
|
if (!this.dirty) return;
|
|
27317
27456
|
try {
|
|
27318
27457
|
fs35.mkdirSync(this.root, { recursive: true, mode: 448 });
|
|
27319
|
-
const tmp =
|
|
27458
|
+
const tmp = path38.join(this.root, `index.${process.pid}.tmp`);
|
|
27320
27459
|
fs35.writeFileSync(tmp, JSON.stringify(this.index), { mode: 384 });
|
|
27321
|
-
fs35.renameSync(tmp,
|
|
27460
|
+
fs35.renameSync(tmp, path38.join(this.root, "index.json"));
|
|
27322
27461
|
this.dirty = false;
|
|
27323
27462
|
} catch {
|
|
27324
27463
|
}
|
|
@@ -27330,7 +27469,7 @@ var init_scan_cache = __esm({
|
|
|
27330
27469
|
// src/cli/commands/scan-judge.ts
|
|
27331
27470
|
import fs36 from "fs";
|
|
27332
27471
|
import os27 from "os";
|
|
27333
|
-
import
|
|
27472
|
+
import path39 from "path";
|
|
27334
27473
|
function findingDedupKey(f) {
|
|
27335
27474
|
return `${f.source.rule.name ?? "<unnamed>"}|${preview(f.input)}|${f.project}|${f.repo ?? ""}`;
|
|
27336
27475
|
}
|
|
@@ -27429,20 +27568,20 @@ var init_scan_judge = __esm({
|
|
|
27429
27568
|
* file, so worktrees count), home-relative; undefined when none. Cached per
|
|
27430
27569
|
* directory: history repeats the same few cwds thousands of times. */
|
|
27431
27570
|
repoOf(cwd) {
|
|
27432
|
-
if (!cwd || !
|
|
27571
|
+
if (!cwd || !path39.isAbsolute(cwd)) return void 0;
|
|
27433
27572
|
if (this.repos.has(cwd)) return this.repos.get(cwd);
|
|
27434
27573
|
let dir2 = cwd;
|
|
27435
27574
|
let found;
|
|
27436
27575
|
for (; ; ) {
|
|
27437
27576
|
try {
|
|
27438
|
-
if (fs36.existsSync(
|
|
27577
|
+
if (fs36.existsSync(path39.join(dir2, ".git"))) {
|
|
27439
27578
|
found = dir2;
|
|
27440
27579
|
break;
|
|
27441
27580
|
}
|
|
27442
27581
|
} catch {
|
|
27443
27582
|
break;
|
|
27444
27583
|
}
|
|
27445
|
-
const parent =
|
|
27584
|
+
const parent = path39.dirname(dir2);
|
|
27446
27585
|
if (parent === dir2) break;
|
|
27447
27586
|
dir2 = parent;
|
|
27448
27587
|
}
|
|
@@ -27591,6 +27730,16 @@ var init_scan_judge = __esm({
|
|
|
27591
27730
|
});
|
|
27592
27731
|
|
|
27593
27732
|
// src/utils/hook-payload.ts
|
|
27733
|
+
function canonicalToolInput(rawToolName, input) {
|
|
27734
|
+
if (rawToolName !== "run_command") return input;
|
|
27735
|
+
if (typeof input !== "object" || input === null || Array.isArray(input)) return input;
|
|
27736
|
+
const args = input;
|
|
27737
|
+
if (typeof args.CommandLine !== "string") return input;
|
|
27738
|
+
const { CommandLine, Cwd, ...rest } = args;
|
|
27739
|
+
const canonical = { ...rest, command: CommandLine };
|
|
27740
|
+
if (typeof Cwd === "string" && Cwd.length > 0) canonical.cwd = Cwd;
|
|
27741
|
+
return canonical;
|
|
27742
|
+
}
|
|
27594
27743
|
var init_hook_payload = __esm({
|
|
27595
27744
|
"src/utils/hook-payload.ts"() {
|
|
27596
27745
|
"use strict";
|
|
@@ -27609,7 +27758,7 @@ var init_scan_summary = __esm({
|
|
|
27609
27758
|
|
|
27610
27759
|
// src/agent-wiring.ts
|
|
27611
27760
|
import fs37 from "fs";
|
|
27612
|
-
import
|
|
27761
|
+
import path40 from "path";
|
|
27613
27762
|
import * as yaml from "yaml";
|
|
27614
27763
|
import { parse as parseToml } from "smol-toml";
|
|
27615
27764
|
var exists, ck, lg, AGENT_SPECS;
|
|
@@ -27631,55 +27780,55 @@ var init_agent_wiring = __esm({
|
|
|
27631
27780
|
id: "claude",
|
|
27632
27781
|
label: "Claude Code",
|
|
27633
27782
|
setupCommand: "node9 agents add claude",
|
|
27634
|
-
hookFile: (h) =>
|
|
27783
|
+
hookFile: (h) => path40.join(h, ".claude", "settings.json"),
|
|
27635
27784
|
hookFormat: "matcher",
|
|
27636
27785
|
// UserPromptSubmit is prompt DLP. setup.ts has written it for Claude since
|
|
27637
27786
|
// that shipped; the spec must name it too, or status/doctor never show the
|
|
27638
27787
|
// row and heal (which repairs via setupAgent) has no signal it is missing.
|
|
27639
27788
|
hookEvents: [ck("PreToolUse"), lg("PostToolUse"), ck("UserPromptSubmit")],
|
|
27640
|
-
mcpFile: (h) =>
|
|
27641
|
-
present: (h) => exists(
|
|
27789
|
+
mcpFile: (h) => path40.join(h, ".claude.json"),
|
|
27790
|
+
present: (h) => exists(path40.join(h, ".claude", "settings.json")) || exists(path40.join(h, ".claude.json"))
|
|
27642
27791
|
},
|
|
27643
27792
|
{
|
|
27644
27793
|
id: "gemini",
|
|
27645
27794
|
label: "Gemini CLI",
|
|
27646
27795
|
setupCommand: "node9 agents add gemini",
|
|
27647
|
-
hookFile: (h) =>
|
|
27796
|
+
hookFile: (h) => path40.join(h, ".gemini", "settings.json"),
|
|
27648
27797
|
hookFormat: "matcher",
|
|
27649
27798
|
hookEvents: [ck("BeforeTool"), lg("AfterTool")],
|
|
27650
|
-
mcpFile: (h) =>
|
|
27651
|
-
present: (h) => exists(
|
|
27799
|
+
mcpFile: (h) => path40.join(h, ".gemini", "settings.json"),
|
|
27800
|
+
present: (h) => exists(path40.join(h, ".gemini", "settings.json"))
|
|
27652
27801
|
},
|
|
27653
27802
|
{
|
|
27654
27803
|
id: "codex",
|
|
27655
27804
|
label: "Codex",
|
|
27656
27805
|
setupCommand: "node9 agents add codex",
|
|
27657
|
-
hookFile: (h) =>
|
|
27806
|
+
hookFile: (h) => path40.join(h, ".codex", "hooks.json"),
|
|
27658
27807
|
hookFormat: "matcher",
|
|
27659
27808
|
hookEvents: [ck("PreToolUse"), ck("UserPromptSubmit")],
|
|
27660
|
-
mcpFile: (h) =>
|
|
27809
|
+
mcpFile: (h) => path40.join(h, ".codex", "config.toml"),
|
|
27661
27810
|
mcpFormat: "toml",
|
|
27662
|
-
present: (h) => exists(
|
|
27811
|
+
present: (h) => exists(path40.join(h, ".codex"))
|
|
27663
27812
|
},
|
|
27664
27813
|
{
|
|
27665
27814
|
id: "antigravity",
|
|
27666
27815
|
label: "Antigravity",
|
|
27667
27816
|
setupCommand: "node9 agents add antigravity",
|
|
27668
|
-
hookFile: (h) =>
|
|
27817
|
+
hookFile: (h) => path40.join(h, ".gemini", "config", "hooks.json"),
|
|
27669
27818
|
hookFormat: "matcher",
|
|
27670
27819
|
hookEvents: [ck("PreToolUse"), lg("PostToolUse")],
|
|
27671
|
-
mcpFile: (h) =>
|
|
27672
|
-
present: (h) => exists(
|
|
27820
|
+
mcpFile: (h) => path40.join(h, ".gemini", "config", "mcp_config.json"),
|
|
27821
|
+
present: (h) => exists(path40.join(h, ".gemini", "config", "hooks.json")) || exists(path40.join(h, ".gemini", "antigravity-cli")) || exists(path40.join(h, ".gemini", "antigravity-ide"))
|
|
27673
27822
|
},
|
|
27674
27823
|
{
|
|
27675
27824
|
id: "copilot",
|
|
27676
27825
|
label: "GitHub Copilot",
|
|
27677
27826
|
setupCommand: "node9 agents add copilot",
|
|
27678
|
-
hookFile: (h) =>
|
|
27827
|
+
hookFile: (h) => path40.join(h, ".copilot", "hooks", "node9.json"),
|
|
27679
27828
|
hookFormat: "flat",
|
|
27680
27829
|
hookEvents: [ck("PreToolUse"), lg("PostToolUse"), ck("UserPromptSubmit")],
|
|
27681
|
-
mcpFile: (h) =>
|
|
27682
|
-
present: (h) => exists(
|
|
27830
|
+
mcpFile: (h) => path40.join(h, ".copilot", "mcp-config.json"),
|
|
27831
|
+
present: (h) => exists(path40.join(h, ".copilot"))
|
|
27683
27832
|
},
|
|
27684
27833
|
{
|
|
27685
27834
|
id: "cursor",
|
|
@@ -27688,8 +27837,8 @@ var init_agent_wiring = __esm({
|
|
|
27688
27837
|
// MCP-only — no hook file (see note above).
|
|
27689
27838
|
hookFormat: "flat",
|
|
27690
27839
|
hookEvents: [],
|
|
27691
|
-
mcpFile: (h) =>
|
|
27692
|
-
present: (h) => exists(
|
|
27840
|
+
mcpFile: (h) => path40.join(h, ".cursor", "mcp.json"),
|
|
27841
|
+
present: (h) => exists(path40.join(h, ".cursor", "mcp.json"))
|
|
27693
27842
|
},
|
|
27694
27843
|
{
|
|
27695
27844
|
id: "hermes",
|
|
@@ -27710,8 +27859,8 @@ var init_agent_wiring = __esm({
|
|
|
27710
27859
|
setupCommand: "node9 agents add opencode",
|
|
27711
27860
|
hookFormat: "flat",
|
|
27712
27861
|
hookEvents: [],
|
|
27713
|
-
shimFile: (h) =>
|
|
27714
|
-
present: (h) => exists(opencodeConfigDir(h)) || exists(
|
|
27862
|
+
shimFile: (h) => path40.join(opencodeConfigDir(h), "plugins", "node9.js"),
|
|
27863
|
+
present: (h) => exists(opencodeConfigDir(h)) || exists(path40.join(opencodeConfigDir(h), "plugins", "node9.js"))
|
|
27715
27864
|
},
|
|
27716
27865
|
{
|
|
27717
27866
|
id: "pi",
|
|
@@ -27719,8 +27868,8 @@ var init_agent_wiring = __esm({
|
|
|
27719
27868
|
setupCommand: "node9 agents add pi",
|
|
27720
27869
|
hookFormat: "flat",
|
|
27721
27870
|
hookEvents: [],
|
|
27722
|
-
shimFile: (h) =>
|
|
27723
|
-
present: (h) => exists(
|
|
27871
|
+
shimFile: (h) => path40.join(h, ".pi", "agent", "extensions", "node9.js"),
|
|
27872
|
+
present: (h) => exists(path40.join(h, ".pi", "agent")) || exists(path40.join(h, ".pi", "agent", "extensions", "node9.js"))
|
|
27724
27873
|
}
|
|
27725
27874
|
];
|
|
27726
27875
|
}
|
|
@@ -27771,14 +27920,14 @@ var init_codex_trust = __esm({
|
|
|
27771
27920
|
});
|
|
27772
27921
|
|
|
27773
27922
|
// src/daemon/hook-baseline.ts
|
|
27774
|
-
import
|
|
27923
|
+
import path41 from "path";
|
|
27775
27924
|
import os28 from "os";
|
|
27776
27925
|
var BASELINE_FILE, NOTIFIED_FILE;
|
|
27777
27926
|
var init_hook_baseline = __esm({
|
|
27778
27927
|
"src/daemon/hook-baseline.ts"() {
|
|
27779
27928
|
"use strict";
|
|
27780
|
-
BASELINE_FILE =
|
|
27781
|
-
NOTIFIED_FILE =
|
|
27929
|
+
BASELINE_FILE = path41.join(os28.homedir(), ".node9", "hooks-baseline.json");
|
|
27930
|
+
NOTIFIED_FILE = path41.join(os28.homedir(), ".node9", "hook-heal-notified.json");
|
|
27782
27931
|
}
|
|
27783
27932
|
});
|
|
27784
27933
|
|
|
@@ -27797,7 +27946,7 @@ var init_setup_pi_shim = __esm({
|
|
|
27797
27946
|
});
|
|
27798
27947
|
|
|
27799
27948
|
// src/setup.ts
|
|
27800
|
-
import
|
|
27949
|
+
import path42 from "path";
|
|
27801
27950
|
import os29 from "os";
|
|
27802
27951
|
import chalk3 from "chalk";
|
|
27803
27952
|
import { confirm as rawConfirm } from "@inquirer/prompts";
|
|
@@ -27805,16 +27954,16 @@ import { parse as parseToml5, stringify as stringifyToml2 } from "smol-toml";
|
|
|
27805
27954
|
import * as yaml2 from "yaml";
|
|
27806
27955
|
function opencodeConfigDir(home = os29.homedir()) {
|
|
27807
27956
|
const xdg = process.env.XDG_CONFIG_HOME;
|
|
27808
|
-
const base2 = xdg &&
|
|
27809
|
-
return
|
|
27957
|
+
const base2 = xdg && path42.isAbsolute(xdg) ? xdg : path42.join(home, ".config");
|
|
27958
|
+
return path42.join(base2, "opencode");
|
|
27810
27959
|
}
|
|
27811
27960
|
function hermesHomeDir(homeDir = os29.homedir()) {
|
|
27812
27961
|
const env = process.env.HERMES_HOME?.trim();
|
|
27813
|
-
if (env &&
|
|
27814
|
-
return
|
|
27962
|
+
if (env && path42.isAbsolute(env)) return env;
|
|
27963
|
+
return path42.join(homeDir, ".hermes");
|
|
27815
27964
|
}
|
|
27816
27965
|
function hermesConfigPath(homeDir = os29.homedir()) {
|
|
27817
|
-
return
|
|
27966
|
+
return path42.join(hermesHomeDir(homeDir), HERMES_CONFIG_FILENAME);
|
|
27818
27967
|
}
|
|
27819
27968
|
var HERMES_CONFIG_FILENAME;
|
|
27820
27969
|
var init_setup = __esm({
|
|
@@ -27860,7 +28009,7 @@ var init_scan_history = __esm({
|
|
|
27860
28009
|
// src/cli/commands/scan.ts
|
|
27861
28010
|
import chalk5 from "chalk";
|
|
27862
28011
|
import fs38 from "fs";
|
|
27863
|
-
import
|
|
28012
|
+
import path43 from "path";
|
|
27864
28013
|
import os30 from "os";
|
|
27865
28014
|
import stringWidth2 from "string-width";
|
|
27866
28015
|
function dayKey(ts) {
|
|
@@ -28074,7 +28223,7 @@ async function processClaudeFile(file2, projPath, projLabel, judge, judgeFrom, r
|
|
|
28074
28223
|
const session = { sessionId, costUSD: 0, toolCalls: 0 };
|
|
28075
28224
|
let raw;
|
|
28076
28225
|
try {
|
|
28077
|
-
raw = fs38.readFileSync(
|
|
28226
|
+
raw = fs38.readFileSync(path43.join(projPath, file2), "utf-8");
|
|
28078
28227
|
} catch {
|
|
28079
28228
|
return false;
|
|
28080
28229
|
}
|
|
@@ -28143,7 +28292,7 @@ async function processClaudeFile(file2, projPath, projLabel, judge, judgeFrom, r
|
|
|
28143
28292
|
if (block.type !== "tool_result") continue;
|
|
28144
28293
|
const filePath = block.tool_use_id ? toolUseFilePaths.get(block.tool_use_id) : void 0;
|
|
28145
28294
|
if (filePath) {
|
|
28146
|
-
const ext =
|
|
28295
|
+
const ext = path43.extname(filePath).toLowerCase();
|
|
28147
28296
|
if (CODE_EXTENSIONS.has(ext)) continue;
|
|
28148
28297
|
}
|
|
28149
28298
|
const resultText = typeof block.content === "string" ? block.content : Array.isArray(block.content) ? block.content.map((c) => c.text ?? "").join("\n") : null;
|
|
@@ -28263,7 +28412,7 @@ async function processClaudeFile(file2, projPath, projLabel, judge, judgeFrom, r
|
|
|
28263
28412
|
return true;
|
|
28264
28413
|
}
|
|
28265
28414
|
async function processClaudeProjectAsync(proj, projectsDir, judge, cache7, startDate, result, dedup, canaryVals, onProgress, onLine) {
|
|
28266
|
-
const projPath =
|
|
28415
|
+
const projPath = path43.join(projectsDir, proj);
|
|
28267
28416
|
try {
|
|
28268
28417
|
if (!fs38.statSync(projPath).isDirectory()) return;
|
|
28269
28418
|
} catch {
|
|
@@ -28277,7 +28426,7 @@ async function processClaudeProjectAsync(proj, projectsDir, judge, cache7, start
|
|
|
28277
28426
|
return;
|
|
28278
28427
|
}
|
|
28279
28428
|
for (const file2 of files) {
|
|
28280
|
-
const filePath =
|
|
28429
|
+
const filePath = path43.join(projPath, file2);
|
|
28281
28430
|
const stamp = fileStamp(filePath);
|
|
28282
28431
|
if (!fileMayHoldWindow(stamp, startDate)) continue;
|
|
28283
28432
|
result.filesScanned++;
|
|
@@ -28420,7 +28569,7 @@ function mergeFileScan(result, file2, dedup, startDate = null) {
|
|
|
28420
28569
|
}
|
|
28421
28570
|
}
|
|
28422
28571
|
async function scanClaudeHistoryAsync(startDate, onProgress, onLine, opts = {}) {
|
|
28423
|
-
const projectsDir =
|
|
28572
|
+
const projectsDir = path43.join(os30.homedir(), ".claude", "projects");
|
|
28424
28573
|
const result = emptyClaudeScan();
|
|
28425
28574
|
if (!fs38.existsSync(projectsDir)) return result;
|
|
28426
28575
|
let projDirs;
|
|
@@ -28453,7 +28602,7 @@ async function scanClaudeHistoryAsync(startDate, onProgress, onLine, opts = {})
|
|
|
28453
28602
|
}
|
|
28454
28603
|
async function scanGeminiHistory(startDate, onProgress, onLine, opts = {}) {
|
|
28455
28604
|
const canaryVals = safeCanaryScanValues();
|
|
28456
|
-
const tmpDir =
|
|
28605
|
+
const tmpDir = path43.join(os30.homedir(), ".gemini", "tmp");
|
|
28457
28606
|
const result = {
|
|
28458
28607
|
filesScanned: 0,
|
|
28459
28608
|
sessions: 0,
|
|
@@ -28480,7 +28629,7 @@ async function scanGeminiHistory(startDate, onProgress, onLine, opts = {}) {
|
|
|
28480
28629
|
const judge = new HistoryJudge();
|
|
28481
28630
|
const cache7 = openScanCache(judge, opts);
|
|
28482
28631
|
for (const slug2 of slugDirs) {
|
|
28483
|
-
const slugPath =
|
|
28632
|
+
const slugPath = path43.join(tmpDir, slug2);
|
|
28484
28633
|
try {
|
|
28485
28634
|
if (!fs38.statSync(slugPath).isDirectory()) continue;
|
|
28486
28635
|
} catch {
|
|
@@ -28489,11 +28638,11 @@ async function scanGeminiHistory(startDate, onProgress, onLine, opts = {}) {
|
|
|
28489
28638
|
let projLabel = stripTerminalEscapes(slug2).slice(0, 40);
|
|
28490
28639
|
try {
|
|
28491
28640
|
projLabel = stripTerminalEscapes(
|
|
28492
|
-
fs38.readFileSync(
|
|
28641
|
+
fs38.readFileSync(path43.join(slugPath, ".project_root"), "utf-8").trim()
|
|
28493
28642
|
).replace(os30.homedir(), "~").slice(0, 40);
|
|
28494
28643
|
} catch {
|
|
28495
28644
|
}
|
|
28496
|
-
const chatsDir =
|
|
28645
|
+
const chatsDir = path43.join(slugPath, "chats");
|
|
28497
28646
|
if (!fs38.existsSync(chatsDir)) continue;
|
|
28498
28647
|
let chatFiles;
|
|
28499
28648
|
try {
|
|
@@ -28503,7 +28652,7 @@ async function scanGeminiHistory(startDate, onProgress, onLine, opts = {}) {
|
|
|
28503
28652
|
}
|
|
28504
28653
|
for (const chatFile of chatFiles) {
|
|
28505
28654
|
const sessionId = chatFile.replace(/\.jsonl?$/, "");
|
|
28506
|
-
const filePath =
|
|
28655
|
+
const filePath = path43.join(chatsDir, chatFile);
|
|
28507
28656
|
const stamp = fileStamp(filePath);
|
|
28508
28657
|
if (!fileMayHoldWindow(stamp, startDate)) continue;
|
|
28509
28658
|
result.filesScanned++;
|
|
@@ -28517,7 +28666,7 @@ async function scanGeminiHistory(startDate, onProgress, onLine, opts = {}) {
|
|
|
28517
28666
|
const fileDedup = emptyScanDedup();
|
|
28518
28667
|
let raw;
|
|
28519
28668
|
try {
|
|
28520
|
-
raw = fs38.readFileSync(
|
|
28669
|
+
raw = fs38.readFileSync(path43.join(chatsDir, chatFile), "utf-8");
|
|
28521
28670
|
} catch {
|
|
28522
28671
|
continue;
|
|
28523
28672
|
}
|
|
@@ -28653,6 +28802,360 @@ async function scanGeminiHistory(startDate, onProgress, onLine, opts = {}) {
|
|
|
28653
28802
|
result.policy = judge.policy(result.unknownInputs ?? {});
|
|
28654
28803
|
return result;
|
|
28655
28804
|
}
|
|
28805
|
+
function antigravityBrainDirs() {
|
|
28806
|
+
return ["antigravity-cli", "antigravity-ide"].map((surface) => path43.join(os30.homedir(), ".gemini", surface, "brain")).filter((p) => fs38.existsSync(p));
|
|
28807
|
+
}
|
|
28808
|
+
function antigravityTranscriptPath(convPath) {
|
|
28809
|
+
const logsDir = path43.join(convPath, ".system_generated", "logs");
|
|
28810
|
+
for (const name of ["transcript_full.jsonl", "transcript.jsonl"]) {
|
|
28811
|
+
const p = path43.join(logsDir, name);
|
|
28812
|
+
if (fs38.existsSync(p)) return p;
|
|
28813
|
+
}
|
|
28814
|
+
return null;
|
|
28815
|
+
}
|
|
28816
|
+
async function scanAntigravityHistory(startDate, onProgress, onLine, opts = {}) {
|
|
28817
|
+
const canaryVals = safeCanaryScanValues();
|
|
28818
|
+
const result = {
|
|
28819
|
+
filesScanned: 0,
|
|
28820
|
+
sessions: 0,
|
|
28821
|
+
totalToolCalls: 0,
|
|
28822
|
+
bashCalls: 0,
|
|
28823
|
+
findings: [],
|
|
28824
|
+
dlpFindings: [],
|
|
28825
|
+
canaryFindings: [],
|
|
28826
|
+
loopFindings: [],
|
|
28827
|
+
totalCostUSD: 0,
|
|
28828
|
+
// transcripts carry no token/model data
|
|
28829
|
+
firstDate: null,
|
|
28830
|
+
lastDate: null,
|
|
28831
|
+
sessionsWithEarlySecrets: 0,
|
|
28832
|
+
perSession: []
|
|
28833
|
+
};
|
|
28834
|
+
const dedup = emptyScanDedup();
|
|
28835
|
+
const brainDirs = antigravityBrainDirs();
|
|
28836
|
+
if (brainDirs.length === 0) return result;
|
|
28837
|
+
const judge = new HistoryJudge();
|
|
28838
|
+
const cache7 = openScanCache(judge, opts);
|
|
28839
|
+
for (const brainDir of brainDirs) {
|
|
28840
|
+
let convDirs;
|
|
28841
|
+
try {
|
|
28842
|
+
convDirs = fs38.readdirSync(brainDir);
|
|
28843
|
+
} catch {
|
|
28844
|
+
continue;
|
|
28845
|
+
}
|
|
28846
|
+
for (const conv of convDirs) {
|
|
28847
|
+
const convPath = path43.join(brainDir, conv);
|
|
28848
|
+
try {
|
|
28849
|
+
if (!fs38.statSync(convPath).isDirectory()) continue;
|
|
28850
|
+
} catch {
|
|
28851
|
+
continue;
|
|
28852
|
+
}
|
|
28853
|
+
const transcriptFile = antigravityTranscriptPath(convPath);
|
|
28854
|
+
if (!transcriptFile) continue;
|
|
28855
|
+
const stamp = fileStamp(transcriptFile);
|
|
28856
|
+
if (!fileMayHoldWindow(stamp, startDate)) continue;
|
|
28857
|
+
result.filesScanned++;
|
|
28858
|
+
onProgress?.(result.filesScanned);
|
|
28859
|
+
const cached = stamp ? cache7.get(transcriptFile, stamp, judge, judgeFromFor(startDate)) : null;
|
|
28860
|
+
if (cached) {
|
|
28861
|
+
mergeFileScan(result, cached, dedup, startDate);
|
|
28862
|
+
continue;
|
|
28863
|
+
}
|
|
28864
|
+
const fileResult = emptyScan();
|
|
28865
|
+
const fileDedup = emptyScanDedup();
|
|
28866
|
+
let raw;
|
|
28867
|
+
try {
|
|
28868
|
+
raw = fs38.readFileSync(transcriptFile, "utf-8");
|
|
28869
|
+
} catch {
|
|
28870
|
+
continue;
|
|
28871
|
+
}
|
|
28872
|
+
const sessionId = conv;
|
|
28873
|
+
let projLabel = conv.slice(0, 8);
|
|
28874
|
+
const sessionCalls = [];
|
|
28875
|
+
for (const line of raw.split("\n")) {
|
|
28876
|
+
if (!line.trim()) continue;
|
|
28877
|
+
onLine?.();
|
|
28878
|
+
let step;
|
|
28879
|
+
try {
|
|
28880
|
+
step = JSON.parse(line);
|
|
28881
|
+
} catch {
|
|
28882
|
+
continue;
|
|
28883
|
+
}
|
|
28884
|
+
const timestamp2 = step.created_at ?? "";
|
|
28885
|
+
if (beforeJudged(judgeFromFor(startDate), timestamp2 || void 0)) continue;
|
|
28886
|
+
const day = dayOf(fileResult, timestamp2 || void 0);
|
|
28887
|
+
if (fileResult.sessions === 0) fileResult.sessions = 1;
|
|
28888
|
+
if (step.type === "USER_INPUT") {
|
|
28889
|
+
const text = typeof step.content === "string" ? step.content : "";
|
|
28890
|
+
if (text) {
|
|
28891
|
+
const decoysHere5 = recordCanaries(
|
|
28892
|
+
{ text },
|
|
28893
|
+
"user-prompt",
|
|
28894
|
+
timestamp2,
|
|
28895
|
+
projLabel,
|
|
28896
|
+
sessionId,
|
|
28897
|
+
"antigravity",
|
|
28898
|
+
fileResult,
|
|
28899
|
+
fileDedup,
|
|
28900
|
+
canaryVals
|
|
28901
|
+
);
|
|
28902
|
+
const dlpMatch = scanArgs(scrubDecoys({ text }, decoysHere5));
|
|
28903
|
+
if (dlpMatch) {
|
|
28904
|
+
const k = dlpKey(dlpMatch.patternName, dlpMatch.redactedSample, projLabel, timestamp2);
|
|
28905
|
+
if (!fileDedup.dlpKeys.has(k)) {
|
|
28906
|
+
fileDedup.dlpKeys.add(k);
|
|
28907
|
+
fileResult.dlpFindings.push({
|
|
28908
|
+
patternName: dlpMatch.patternName,
|
|
28909
|
+
redactedSample: dlpMatch.redactedSample,
|
|
28910
|
+
toolName: "user-prompt",
|
|
28911
|
+
timestamp: timestamp2,
|
|
28912
|
+
project: projLabel,
|
|
28913
|
+
sessionId,
|
|
28914
|
+
agent: "antigravity"
|
|
28915
|
+
});
|
|
28916
|
+
}
|
|
28917
|
+
}
|
|
28918
|
+
}
|
|
28919
|
+
continue;
|
|
28920
|
+
}
|
|
28921
|
+
if (!Array.isArray(step.tool_calls) || step.tool_calls.length === 0) continue;
|
|
28922
|
+
if (timestamp2) {
|
|
28923
|
+
if (!fileResult.firstDate || timestamp2 < fileResult.firstDate)
|
|
28924
|
+
fileResult.firstDate = timestamp2;
|
|
28925
|
+
if (!fileResult.lastDate || timestamp2 > fileResult.lastDate)
|
|
28926
|
+
fileResult.lastDate = timestamp2;
|
|
28927
|
+
}
|
|
28928
|
+
for (const tc of step.tool_calls) {
|
|
28929
|
+
fileResult.totalToolCalls++;
|
|
28930
|
+
day.toolCalls++;
|
|
28931
|
+
const toolName = tc.name ?? "";
|
|
28932
|
+
countMcpCall(fileResult, toolName, day);
|
|
28933
|
+
const toolNameLower = toolName.toLowerCase();
|
|
28934
|
+
const input = canonicalToolInput(toolName, tc.args ?? {});
|
|
28935
|
+
sessionCalls.push({ toolName, input, timestamp: timestamp2 });
|
|
28936
|
+
const shellInput = resolveShellInput(toolNameLower, input, toolInspectionMap);
|
|
28937
|
+
const isShellTool = shellInput.kind === "shell";
|
|
28938
|
+
if (isShellTool) {
|
|
28939
|
+
fileResult.bashCalls++;
|
|
28940
|
+
day.bashCalls++;
|
|
28941
|
+
const cwd = String(input.cwd ?? "");
|
|
28942
|
+
if (cwd && projLabel === conv.slice(0, 8)) {
|
|
28943
|
+
projLabel = stripTerminalEscapes(cwd).replace(os30.homedir(), "~").slice(0, 40);
|
|
28944
|
+
}
|
|
28945
|
+
}
|
|
28946
|
+
const rawCmd = String(input.command ?? "").trimStart();
|
|
28947
|
+
if (/^node9\s+(scan|explain|report|tail|dlp|status|sessions|audit)\b/.test(rawCmd))
|
|
28948
|
+
continue;
|
|
28949
|
+
const decoysHere6 = recordCanaries(
|
|
28950
|
+
input,
|
|
28951
|
+
toolName,
|
|
28952
|
+
timestamp2,
|
|
28953
|
+
projLabel,
|
|
28954
|
+
sessionId,
|
|
28955
|
+
"antigravity",
|
|
28956
|
+
fileResult,
|
|
28957
|
+
fileDedup,
|
|
28958
|
+
canaryVals
|
|
28959
|
+
);
|
|
28960
|
+
const judged = await judge.judge(
|
|
28961
|
+
{
|
|
28962
|
+
agent: "antigravity",
|
|
28963
|
+
toolName,
|
|
28964
|
+
input,
|
|
28965
|
+
scrubbed: decoysHere6.length ? scrubDecoys(input, decoysHere6) : void 0,
|
|
28966
|
+
timestamp: timestamp2,
|
|
28967
|
+
sessionId,
|
|
28968
|
+
project: projLabel,
|
|
28969
|
+
cwd: typeof input.cwd === "string" && path43.isAbsolute(input.cwd) ? input.cwd : void 0
|
|
28970
|
+
},
|
|
28971
|
+
fileResult,
|
|
28972
|
+
fileDedup
|
|
28973
|
+
);
|
|
28974
|
+
addUnknown(day, judged.verdict.unknown);
|
|
28975
|
+
}
|
|
28976
|
+
}
|
|
28977
|
+
fileResult.loopFindings.push(
|
|
28978
|
+
...detectLoops(sessionCalls, projLabel, sessionId, "antigravity")
|
|
28979
|
+
);
|
|
28980
|
+
if (stamp)
|
|
28981
|
+
cache7.put(transcriptFile, stamp, "antigravity", fileResult, judge, judgeFromFor(startDate));
|
|
28982
|
+
mergeFileScan(result, fileResult, dedup, startDate);
|
|
28983
|
+
await yieldTick();
|
|
28984
|
+
}
|
|
28985
|
+
}
|
|
28986
|
+
cache7.close();
|
|
28987
|
+
result.policy = judge.policy(result.unknownInputs ?? {});
|
|
28988
|
+
return result;
|
|
28989
|
+
}
|
|
28990
|
+
async function scanCopilotHistory(startDate, onProgress, onLine, opts = {}) {
|
|
28991
|
+
const canaryVals = safeCanaryScanValues();
|
|
28992
|
+
const sessionDir = path43.join(os30.homedir(), ".copilot", "session-state");
|
|
28993
|
+
const result = {
|
|
28994
|
+
filesScanned: 0,
|
|
28995
|
+
sessions: 0,
|
|
28996
|
+
totalToolCalls: 0,
|
|
28997
|
+
bashCalls: 0,
|
|
28998
|
+
findings: [],
|
|
28999
|
+
dlpFindings: [],
|
|
29000
|
+
canaryFindings: [],
|
|
29001
|
+
loopFindings: [],
|
|
29002
|
+
totalCostUSD: 0,
|
|
29003
|
+
// event logs carry no token/cost rollup
|
|
29004
|
+
firstDate: null,
|
|
29005
|
+
lastDate: null,
|
|
29006
|
+
sessionsWithEarlySecrets: 0,
|
|
29007
|
+
perSession: []
|
|
29008
|
+
};
|
|
29009
|
+
const dedup = emptyScanDedup();
|
|
29010
|
+
if (!fs38.existsSync(sessionDir)) return result;
|
|
29011
|
+
let sessionIds;
|
|
29012
|
+
try {
|
|
29013
|
+
sessionIds = fs38.readdirSync(sessionDir);
|
|
29014
|
+
} catch {
|
|
29015
|
+
return result;
|
|
29016
|
+
}
|
|
29017
|
+
const judge = new HistoryJudge();
|
|
29018
|
+
const cache7 = openScanCache(judge, opts);
|
|
29019
|
+
for (const sessionId of sessionIds) {
|
|
29020
|
+
const eventsPath = path43.join(sessionDir, sessionId, "events.jsonl");
|
|
29021
|
+
if (!fs38.existsSync(eventsPath)) continue;
|
|
29022
|
+
const stamp = fileStamp(eventsPath);
|
|
29023
|
+
if (!fileMayHoldWindow(stamp, startDate)) continue;
|
|
29024
|
+
result.filesScanned++;
|
|
29025
|
+
onProgress?.(result.filesScanned);
|
|
29026
|
+
const cached = stamp ? cache7.get(eventsPath, stamp, judge, judgeFromFor(startDate)) : null;
|
|
29027
|
+
if (cached) {
|
|
29028
|
+
mergeFileScan(result, cached, dedup, startDate);
|
|
29029
|
+
continue;
|
|
29030
|
+
}
|
|
29031
|
+
const fileResult = emptyScan();
|
|
29032
|
+
const fileDedup = emptyScanDedup();
|
|
29033
|
+
let raw;
|
|
29034
|
+
try {
|
|
29035
|
+
raw = fs38.readFileSync(eventsPath, "utf-8");
|
|
29036
|
+
} catch {
|
|
29037
|
+
continue;
|
|
29038
|
+
}
|
|
29039
|
+
let projLabel = sessionId.slice(0, 8);
|
|
29040
|
+
let sessionCwd;
|
|
29041
|
+
const sessionCalls = [];
|
|
29042
|
+
for (const row of parseCopilotSession(raw.split("\n"))) {
|
|
29043
|
+
if (beforeJudged(judgeFromFor(startDate), row.date || void 0)) continue;
|
|
29044
|
+
fileResult.totalCostUSD += row.costUSD;
|
|
29045
|
+
dayOf(fileResult, row.date || void 0, false).costUSD += row.costUSD;
|
|
29046
|
+
}
|
|
29047
|
+
for (const line of raw.split("\n")) {
|
|
29048
|
+
if (!line.trim()) continue;
|
|
29049
|
+
onLine?.();
|
|
29050
|
+
let ev;
|
|
29051
|
+
try {
|
|
29052
|
+
ev = JSON.parse(line);
|
|
29053
|
+
} catch {
|
|
29054
|
+
continue;
|
|
29055
|
+
}
|
|
29056
|
+
const timestamp2 = ev.timestamp ?? "";
|
|
29057
|
+
if (ev.type === "session.start") {
|
|
29058
|
+
const cwd = ev.data?.context?.cwd;
|
|
29059
|
+
if (typeof cwd === "string" && cwd) {
|
|
29060
|
+
projLabel = stripTerminalEscapes(cwd).replace(os30.homedir(), "~").slice(0, 40);
|
|
29061
|
+
if (path43.isAbsolute(cwd)) sessionCwd = cwd;
|
|
29062
|
+
}
|
|
29063
|
+
continue;
|
|
29064
|
+
}
|
|
29065
|
+
if (beforeJudged(judgeFromFor(startDate), timestamp2 || void 0)) continue;
|
|
29066
|
+
const day = dayOf(fileResult, timestamp2 || void 0);
|
|
29067
|
+
if (fileResult.sessions === 0) fileResult.sessions = 1;
|
|
29068
|
+
if (ev.type === "user.message") {
|
|
29069
|
+
const text = ev.data?.content ?? ev.data?.text ?? "";
|
|
29070
|
+
if (typeof text === "string" && text) {
|
|
29071
|
+
const decoysHere7 = recordCanaries(
|
|
29072
|
+
{ text },
|
|
29073
|
+
"user-prompt",
|
|
29074
|
+
timestamp2,
|
|
29075
|
+
projLabel,
|
|
29076
|
+
sessionId,
|
|
29077
|
+
"copilot",
|
|
29078
|
+
fileResult,
|
|
29079
|
+
fileDedup,
|
|
29080
|
+
canaryVals
|
|
29081
|
+
);
|
|
29082
|
+
const dlpMatch = scanArgs(scrubDecoys({ text }, decoysHere7));
|
|
29083
|
+
if (dlpMatch) {
|
|
29084
|
+
const k = dlpKey(dlpMatch.patternName, dlpMatch.redactedSample, projLabel, timestamp2);
|
|
29085
|
+
if (!fileDedup.dlpKeys.has(k)) {
|
|
29086
|
+
fileDedup.dlpKeys.add(k);
|
|
29087
|
+
fileResult.dlpFindings.push({
|
|
29088
|
+
patternName: dlpMatch.patternName,
|
|
29089
|
+
redactedSample: dlpMatch.redactedSample,
|
|
29090
|
+
toolName: "user-prompt",
|
|
29091
|
+
timestamp: timestamp2,
|
|
29092
|
+
project: projLabel,
|
|
29093
|
+
sessionId,
|
|
29094
|
+
agent: "copilot"
|
|
29095
|
+
});
|
|
29096
|
+
}
|
|
29097
|
+
}
|
|
29098
|
+
}
|
|
29099
|
+
continue;
|
|
29100
|
+
}
|
|
29101
|
+
if (ev.type !== "tool.execution_start") continue;
|
|
29102
|
+
const toolName = ev.data?.toolName ?? "";
|
|
29103
|
+
const toolNameLower = toolName.toLowerCase();
|
|
29104
|
+
const input = ev.data?.arguments ?? {};
|
|
29105
|
+
fileResult.totalToolCalls++;
|
|
29106
|
+
day.toolCalls++;
|
|
29107
|
+
countMcpCall(fileResult, toolName, day);
|
|
29108
|
+
sessionCalls.push({ toolName, input, timestamp: timestamp2 });
|
|
29109
|
+
const shellInput = resolveShellInput(toolNameLower, input, toolInspectionMap);
|
|
29110
|
+
const isShellTool = shellInput.kind === "shell";
|
|
29111
|
+
if (isShellTool) {
|
|
29112
|
+
fileResult.bashCalls++;
|
|
29113
|
+
day.bashCalls++;
|
|
29114
|
+
}
|
|
29115
|
+
if (timestamp2) {
|
|
29116
|
+
if (!fileResult.firstDate || timestamp2 < fileResult.firstDate)
|
|
29117
|
+
fileResult.firstDate = timestamp2;
|
|
29118
|
+
if (!fileResult.lastDate || timestamp2 > fileResult.lastDate)
|
|
29119
|
+
fileResult.lastDate = timestamp2;
|
|
29120
|
+
}
|
|
29121
|
+
const rawCmd = String(input.command ?? "").trimStart();
|
|
29122
|
+
if (/^node9\s+(scan|explain|report|tail|dlp|status|sessions|audit)\b/.test(rawCmd)) continue;
|
|
29123
|
+
const decoysHere8 = recordCanaries(
|
|
29124
|
+
input,
|
|
29125
|
+
toolName,
|
|
29126
|
+
timestamp2,
|
|
29127
|
+
projLabel,
|
|
29128
|
+
sessionId,
|
|
29129
|
+
"copilot",
|
|
29130
|
+
fileResult,
|
|
29131
|
+
fileDedup,
|
|
29132
|
+
canaryVals
|
|
29133
|
+
);
|
|
29134
|
+
const judged = await judge.judge(
|
|
29135
|
+
{
|
|
29136
|
+
agent: "copilot",
|
|
29137
|
+
toolName,
|
|
29138
|
+
input,
|
|
29139
|
+
scrubbed: decoysHere8.length ? scrubDecoys(input, decoysHere8) : void 0,
|
|
29140
|
+
timestamp: timestamp2,
|
|
29141
|
+
sessionId,
|
|
29142
|
+
project: projLabel,
|
|
29143
|
+
cwd: sessionCwd
|
|
29144
|
+
},
|
|
29145
|
+
fileResult,
|
|
29146
|
+
fileDedup
|
|
29147
|
+
);
|
|
29148
|
+
addUnknown(day, judged.verdict.unknown);
|
|
29149
|
+
}
|
|
29150
|
+
fileResult.loopFindings.push(...detectLoops(sessionCalls, projLabel, sessionId, "copilot"));
|
|
29151
|
+
if (stamp) cache7.put(eventsPath, stamp, "copilot", fileResult, judge, judgeFromFor(startDate));
|
|
29152
|
+
mergeFileScan(result, fileResult, dedup, startDate);
|
|
29153
|
+
await yieldTick();
|
|
29154
|
+
}
|
|
29155
|
+
cache7.close();
|
|
29156
|
+
result.policy = judge.policy(result.unknownInputs ?? {});
|
|
29157
|
+
return result;
|
|
29158
|
+
}
|
|
28656
29159
|
async function scanCodexHistory(startDate, onProgress, onLine, opts = {}) {
|
|
28657
29160
|
const canaryVals = safeCanaryScanValues();
|
|
28658
29161
|
const result = {
|
|
@@ -28712,7 +29215,7 @@ async function scanCodexHistory(startDate, onProgress, onLine, opts = {}) {
|
|
|
28712
29215
|
startTime = String(payload["timestamp"] ?? "");
|
|
28713
29216
|
const cwd = String(payload["cwd"] ?? "");
|
|
28714
29217
|
projLabel = stripTerminalEscapes(cwd.replace(os30.homedir(), "~")).slice(0, 40);
|
|
28715
|
-
if (
|
|
29218
|
+
if (path43.isAbsolute(cwd)) sessionCwd = cwd;
|
|
28716
29219
|
continue;
|
|
28717
29220
|
}
|
|
28718
29221
|
if (entry.type === "event_msg" && payload["type"] === "user_message") {
|
|
@@ -28912,10 +29415,10 @@ var init_scan = __esm({
|
|
|
28912
29415
|
// src/tui/dashboard/data.ts
|
|
28913
29416
|
import fs39 from "fs";
|
|
28914
29417
|
import os31 from "os";
|
|
28915
|
-
import
|
|
29418
|
+
import path44 from "path";
|
|
28916
29419
|
import http2 from "http";
|
|
28917
29420
|
function auditLogPath() {
|
|
28918
|
-
return
|
|
29421
|
+
return path44.join(os31.homedir(), ".node9", "audit.log");
|
|
28919
29422
|
}
|
|
28920
29423
|
function readAuditEntriesAsync(chunkSize = 1e3, customPath) {
|
|
28921
29424
|
return new Promise((resolve) => {
|
|
@@ -29062,9 +29565,9 @@ function shortenPath(p) {
|
|
|
29062
29565
|
return p.startsWith(home) ? p.replace(home, "~") : p;
|
|
29063
29566
|
}
|
|
29064
29567
|
async function loadReportAuditAsync(period) {
|
|
29065
|
-
const claudeProjectsDir =
|
|
29568
|
+
const claudeProjectsDir = path44.join(os31.homedir(), ".claude", "projects");
|
|
29066
29569
|
const codexSessionsDir2 = codexSessionsDir();
|
|
29067
|
-
const geminiTmpDir =
|
|
29570
|
+
const geminiTmpDir = path44.join(os31.homedir(), ".gemini", "tmp");
|
|
29068
29571
|
const { start, end } = getDateRange(period, /* @__PURE__ */ new Date());
|
|
29069
29572
|
const entries = await readAuditEntriesAsync();
|
|
29070
29573
|
void ensurePricingLoaded();
|
|
@@ -29103,9 +29606,17 @@ function startScanWalk(onUpdate) {
|
|
|
29103
29606
|
if (cancelled) return;
|
|
29104
29607
|
const codex = await scanCodexHistory(lookbackStart);
|
|
29105
29608
|
if (cancelled) return;
|
|
29609
|
+
await yieldToEventLoop();
|
|
29610
|
+
if (cancelled) return;
|
|
29611
|
+
const copilot = await scanCopilotHistory(lookbackStart);
|
|
29612
|
+
if (cancelled) return;
|
|
29613
|
+
await yieldToEventLoop();
|
|
29614
|
+
if (cancelled) return;
|
|
29615
|
+
const antigravity = await scanAntigravityHistory(lookbackStart);
|
|
29616
|
+
if (cancelled) return;
|
|
29106
29617
|
onUpdate({
|
|
29107
29618
|
status: "ready",
|
|
29108
|
-
results: { claude, gemini, codex },
|
|
29619
|
+
results: { claude, gemini, codex, copilot, antigravity },
|
|
29109
29620
|
readyAt: Date.now()
|
|
29110
29621
|
});
|
|
29111
29622
|
} catch (err) {
|
|
@@ -30034,7 +30545,7 @@ var init_util = __esm({
|
|
|
30034
30545
|
BLOCK_REASON_LABELS = {
|
|
30035
30546
|
timeout: "Approval timeout",
|
|
30036
30547
|
"smart-rule-block": "Smart rule",
|
|
30037
|
-
"observe-mode-dlp-would-block": "DLP (
|
|
30548
|
+
"observe-mode-dlp-would-block": "DLP (audit)",
|
|
30038
30549
|
"persistent-deny": "Persistent deny",
|
|
30039
30550
|
"local-decision": "User denied",
|
|
30040
30551
|
"dlp-block": "DLP block",
|
|
@@ -30176,8 +30687,8 @@ import { Fragment as Fragment3, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-run
|
|
|
30176
30687
|
function TopToolsProjects({ audit }) {
|
|
30177
30688
|
const data = audit?.data;
|
|
30178
30689
|
const tools = data ? [...data.toolMap.entries()].sort(([, a], [, b]) => b.calls - a.calls).slice(0, ROW_LIMIT) : [];
|
|
30179
|
-
const projects = data ? [...data.cost.byProject.entries()].map(([
|
|
30180
|
-
name: basenameOf(
|
|
30690
|
+
const projects = data ? [...data.cost.byProject.entries()].map(([path45, r]) => ({
|
|
30691
|
+
name: basenameOf(path45),
|
|
30181
30692
|
cost: r.cost,
|
|
30182
30693
|
tokens: r.inputTokens + r.outputTokens
|
|
30183
30694
|
})).sort((a, b) => b.cost - a.cost).slice(0, ROW_LIMIT) : [];
|
|
@@ -30546,23 +31057,12 @@ function filterScanByPeriod(ready, period, now = /* @__PURE__ */ new Date()) {
|
|
|
30546
31057
|
if (Number.isNaN(t)) return false;
|
|
30547
31058
|
return t >= startMs && t <= endMs;
|
|
30548
31059
|
};
|
|
30549
|
-
const
|
|
30550
|
-
|
|
30551
|
-
|
|
30552
|
-
|
|
30553
|
-
|
|
30554
|
-
const
|
|
30555
|
-
...ready.results.claude.loopFindings,
|
|
30556
|
-
...ready.results.gemini.loopFindings,
|
|
30557
|
-
...ready.results.codex.loopFindings
|
|
30558
|
-
].filter((f) => inPeriod(f.timestamp));
|
|
30559
|
-
const allFindings = [
|
|
30560
|
-
...ready.results.claude.findings,
|
|
30561
|
-
...ready.results.gemini.findings,
|
|
30562
|
-
...ready.results.codex.findings
|
|
30563
|
-
].filter((f) => inPeriod(f.timestamp));
|
|
30564
|
-
const totalToolCalls = ready.results.claude.totalToolCalls + ready.results.gemini.totalToolCalls + ready.results.codex.totalToolCalls;
|
|
30565
|
-
const sessionsWithEarlySecrets = ready.results.claude.sessionsWithEarlySecrets + ready.results.gemini.sessionsWithEarlySecrets + ready.results.codex.sessionsWithEarlySecrets;
|
|
31060
|
+
const slots = Object.values(ready.results);
|
|
31061
|
+
const allLeaks = slots.flatMap((r) => r.dlpFindings).filter((f) => inPeriod(f.timestamp));
|
|
31062
|
+
const allLoops = slots.flatMap((r) => r.loopFindings).filter((f) => inPeriod(f.timestamp));
|
|
31063
|
+
const allFindings = slots.flatMap((r) => r.findings).filter((f) => inPeriod(f.timestamp));
|
|
31064
|
+
const totalToolCalls = slots.reduce((n, r) => n + r.totalToolCalls, 0);
|
|
31065
|
+
const sessionsWithEarlySecrets = slots.reduce((n, r) => n + r.sessionsWithEarlySecrets, 0);
|
|
30566
31066
|
return {
|
|
30567
31067
|
leaks: allLeaks,
|
|
30568
31068
|
loops: allLoops,
|
|
@@ -30614,8 +31114,8 @@ function pickTopLoopFile(loops) {
|
|
|
30614
31114
|
map.set(k, (map.get(k) ?? 0) + (l.count ?? 0));
|
|
30615
31115
|
}
|
|
30616
31116
|
if (map.size === 0) return void 0;
|
|
30617
|
-
const [
|
|
30618
|
-
return { path:
|
|
31117
|
+
const [path45, count] = [...map.entries()].sort((a, b) => b[1] - a[1])[0];
|
|
31118
|
+
return { path: path45, count };
|
|
30619
31119
|
}
|
|
30620
31120
|
var EMPTY_FILTERED_SCAN;
|
|
30621
31121
|
var init_derive = __esm({
|