@node9/proxy 2.12.0 → 2.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/cli.js +543 -514
- package/dist/cli.mjs +543 -514
- package/dist/dashboard.mjs +2580 -2327
- package/dist/index.js +169 -84
- package/dist/index.mjs +169 -84
- package/dist/scan-ink.mjs +43 -12
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -1157,9 +1157,7 @@ function unwrapCommandHead(words) {
|
|
|
1157
1157
|
while (i < words.length) {
|
|
1158
1158
|
const head = (words[i] ?? "").toLowerCase().split("/").pop() ?? "";
|
|
1159
1159
|
if (head === "find") {
|
|
1160
|
-
const x = words.findIndex(
|
|
1161
|
-
(w, k) => k > i && (w === "-exec" || w === "-execdir" || w === "-ok")
|
|
1162
|
-
);
|
|
1160
|
+
const x = words.findIndex((w, k) => k > i && w !== null && FIND_EXEC_FLAGS.has(w));
|
|
1163
1161
|
if (x < 0) break;
|
|
1164
1162
|
i = x + 1;
|
|
1165
1163
|
continue;
|
|
@@ -1181,7 +1179,11 @@ function unwrapCommandHead(words) {
|
|
|
1181
1179
|
if (t.startsWith("-")) {
|
|
1182
1180
|
i++;
|
|
1183
1181
|
const nxt = words[i];
|
|
1184
|
-
if (nxt != null && !nxt.startsWith("-") && !INLINE_INTERPRETER.test(nxt.split("/").pop() ?? "") && !COMMAND_WRAPPERS.has(nxt.toLowerCase()) && !RUNNER_WRAPPERS.has(nxt.toLowerCase())
|
|
1182
|
+
if (nxt != null && !nxt.startsWith("-") && !INLINE_INTERPRETER.test(nxt.split("/").pop() ?? "") && !COMMAND_WRAPPERS.has(nxt.toLowerCase()) && !RUNNER_WRAPPERS.has(nxt.toLowerCase()) && // A reader is a command, never a flag's operand: `env - cat X`,
|
|
1183
|
+
// `stdbuf -o0 cat X`, `ionice -c3 cat X`. Without this the head was
|
|
1184
|
+
// swallowed and the jail needed a looser fallback whose cost was a
|
|
1185
|
+
// false positive on `sudo echo cat X`.
|
|
1186
|
+
!FS_READ_TOOLS.has(nxt.split("/").pop()?.toLowerCase() ?? ""))
|
|
1185
1187
|
i++;
|
|
1186
1188
|
continue;
|
|
1187
1189
|
}
|
|
@@ -1302,34 +1304,18 @@ function isProtectedHomePath(rawPath) {
|
|
|
1302
1304
|
}
|
|
1303
1305
|
function extractLiteralArgs(callExpr) {
|
|
1304
1306
|
const args = callExpr.Args || [];
|
|
1305
|
-
if (args.length === 0) return { name: "", flags: [], paths: [] };
|
|
1306
|
-
const
|
|
1307
|
-
|
|
1308
|
-
let s = "";
|
|
1309
|
-
for (const p of parts) {
|
|
1310
|
-
const t = syntax.NodeType(p);
|
|
1311
|
-
if (t === "Lit") s += (p.Value ?? "").replace(/\\(.)/g, "$1");
|
|
1312
|
-
else if (t === "SglQuoted") s += p.Value ?? "";
|
|
1313
|
-
else if (t === "DblQuoted") {
|
|
1314
|
-
const inner = p.Parts || [];
|
|
1315
|
-
if (!inner.every((ip) => syntax.NodeType(ip) === "Lit")) return null;
|
|
1316
|
-
s += inner.map((ip) => ip.Value ?? "").join("");
|
|
1317
|
-
} else {
|
|
1318
|
-
return null;
|
|
1319
|
-
}
|
|
1320
|
-
}
|
|
1321
|
-
return s;
|
|
1322
|
-
};
|
|
1323
|
-
const name = (litFromWord(args[0]) || "").toLowerCase();
|
|
1307
|
+
if (args.length === 0) return { name: "", flags: [], paths: [], words: [] };
|
|
1308
|
+
const words = args.map((a) => resolveWordLiteral(a));
|
|
1309
|
+
const name = (words[0] ?? "").toLowerCase();
|
|
1324
1310
|
const flags = [];
|
|
1325
1311
|
const paths = [];
|
|
1326
|
-
for (let i = 1; i <
|
|
1327
|
-
const v =
|
|
1312
|
+
for (let i = 1; i < words.length; i++) {
|
|
1313
|
+
const v = words[i];
|
|
1328
1314
|
if (v === null) continue;
|
|
1329
1315
|
if (v.startsWith("-")) flags.push(v);
|
|
1330
1316
|
else paths.push(v);
|
|
1331
1317
|
}
|
|
1332
|
-
return { name, flags, paths };
|
|
1318
|
+
return { name, flags, paths, words };
|
|
1333
1319
|
}
|
|
1334
1320
|
function resolveWordLiteral(w) {
|
|
1335
1321
|
const parts = w?.Parts || [];
|
|
@@ -1587,16 +1573,21 @@ function isRmCreatedInCommandCleanup(command) {
|
|
|
1587
1573
|
}
|
|
1588
1574
|
return sawRm && ok2;
|
|
1589
1575
|
}
|
|
1590
|
-
function analyzeFsOperationImpl(command) {
|
|
1576
|
+
function analyzeFsOperationImpl(command, depth = 0) {
|
|
1591
1577
|
const f = parseShared(command);
|
|
1592
1578
|
if (f === PARSE_FAIL) return null;
|
|
1593
1579
|
let result = null;
|
|
1594
1580
|
try {
|
|
1595
1581
|
syntax.Walk(f, (node) => {
|
|
1596
|
-
if (!node || result) return false;
|
|
1582
|
+
if (!node || result?.verdict === "block") return false;
|
|
1597
1583
|
const n = node;
|
|
1598
|
-
|
|
1599
|
-
|
|
1584
|
+
const nodeType = syntax.NodeType(n);
|
|
1585
|
+
if (nodeType === "Stmt") {
|
|
1586
|
+
result = stricter(result, jailedRedirectRead(n));
|
|
1587
|
+
return result?.verdict !== "block";
|
|
1588
|
+
}
|
|
1589
|
+
if (nodeType !== "CallExpr") return true;
|
|
1590
|
+
const { name, flags, paths, words } = extractLiteralArgs(n);
|
|
1600
1591
|
if (!name) return true;
|
|
1601
1592
|
if (name === "rm") {
|
|
1602
1593
|
const flagStr = flags.join("").toLowerCase();
|
|
@@ -1625,19 +1616,22 @@ function analyzeFsOperationImpl(command) {
|
|
|
1625
1616
|
}
|
|
1626
1617
|
}
|
|
1627
1618
|
}
|
|
1628
|
-
if (
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
reason: sp.reason,
|
|
1636
|
-
path: p
|
|
1637
|
-
};
|
|
1638
|
-
return false;
|
|
1639
|
-
}
|
|
1619
|
+
if (depth < 1) {
|
|
1620
|
+
const payload = literalShellPayload(words, name);
|
|
1621
|
+
if (payload !== null) {
|
|
1622
|
+
const inner = analyzeFsOperationImpl(payload, depth + 1);
|
|
1623
|
+
if (inner) {
|
|
1624
|
+
result = inner;
|
|
1625
|
+
return false;
|
|
1640
1626
|
}
|
|
1627
|
+
return true;
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
const readPaths = FS_READ_TOOLS.has(name) ? paths : wrappedReadPaths(words, name);
|
|
1631
|
+
if (readPaths) {
|
|
1632
|
+
for (const p of readPaths) {
|
|
1633
|
+
result = stricter(result, matchSensitivePath2(p));
|
|
1634
|
+
if (result?.verdict === "block") return false;
|
|
1641
1635
|
}
|
|
1642
1636
|
}
|
|
1643
1637
|
return true;
|
|
@@ -1647,6 +1641,55 @@ function analyzeFsOperationImpl(command) {
|
|
|
1647
1641
|
return null;
|
|
1648
1642
|
}
|
|
1649
1643
|
}
|
|
1644
|
+
function stricter(a, b) {
|
|
1645
|
+
if (!a) return b;
|
|
1646
|
+
if (!b) return a;
|
|
1647
|
+
return b.verdict === "block" && a.verdict !== "block" ? b : a;
|
|
1648
|
+
}
|
|
1649
|
+
function matchSensitivePath2(p) {
|
|
1650
|
+
for (const sp of SENSITIVE_PATH_RULES) {
|
|
1651
|
+
if (sp.match(p))
|
|
1652
|
+
return { ruleName: sp.rule, verdict: sp.verdict ?? "block", reason: sp.reason, path: p };
|
|
1653
|
+
}
|
|
1654
|
+
return null;
|
|
1655
|
+
}
|
|
1656
|
+
function wrappedReadPaths(words, name) {
|
|
1657
|
+
if (name === "find") {
|
|
1658
|
+
const k = words.findIndex((w) => w !== null && FIND_EXEC_FLAGS.has(w));
|
|
1659
|
+
if (k < 1 || !isReaderWord(words[k + 1] ?? null)) return null;
|
|
1660
|
+
const firstPredicate = words.findIndex((w, i) => i > 0 && w !== null && w.startsWith("-"));
|
|
1661
|
+
return positionalAfter(words, 1, firstPredicate > 0 ? firstPredicate : k);
|
|
1662
|
+
}
|
|
1663
|
+
if (!COMMAND_WRAPPERS.has(name) && !RUNNER_WRAPPERS.has(name)) return null;
|
|
1664
|
+
const h = unwrapCommandHead(words);
|
|
1665
|
+
return h > 0 && isReaderWord(words[h] ?? null) ? positionalAfter(words, h + 1) : null;
|
|
1666
|
+
}
|
|
1667
|
+
function literalShellPayload(words, name) {
|
|
1668
|
+
const h = COMMAND_WRAPPERS.has(name) || RUNNER_WRAPPERS.has(name) ? unwrapCommandHead(words) : 0;
|
|
1669
|
+
const head = (words[h] ?? "").split("/").pop()?.toLowerCase() ?? "";
|
|
1670
|
+
if (head === "eval") {
|
|
1671
|
+
const rest = words.slice(h + 1);
|
|
1672
|
+
if (rest.length === 0 || rest.some((w) => w === null)) return null;
|
|
1673
|
+
return rest.join(" ");
|
|
1674
|
+
}
|
|
1675
|
+
if (SHELL_INTERPRETERS.has(head)) {
|
|
1676
|
+
const c = words.findIndex((w, i) => i > h && w !== null && isInlineCodeFlag(head, w));
|
|
1677
|
+
if (c < 0) return null;
|
|
1678
|
+
return words[c + 1] ?? null;
|
|
1679
|
+
}
|
|
1680
|
+
return null;
|
|
1681
|
+
}
|
|
1682
|
+
function jailedRedirectRead(stmt) {
|
|
1683
|
+
const redirs = stmt.Redirs || [];
|
|
1684
|
+
for (const r of redirs) {
|
|
1685
|
+
if (!r || !REDIR_FILE_IN_OPS.has(r.Op)) continue;
|
|
1686
|
+
const p = resolveWordLiteral(r.Word);
|
|
1687
|
+
if (p === null || p === "") continue;
|
|
1688
|
+
const hit = matchSensitivePath2(p);
|
|
1689
|
+
if (hit) return hit;
|
|
1690
|
+
}
|
|
1691
|
+
return null;
|
|
1692
|
+
}
|
|
1650
1693
|
function analyzeShellCommand(command) {
|
|
1651
1694
|
const actions = [];
|
|
1652
1695
|
const paths = [];
|
|
@@ -1782,8 +1825,8 @@ function splitOnPipe(cmd) {
|
|
|
1782
1825
|
if (current.trim()) segments2.push(current.trim());
|
|
1783
1826
|
return segments2.filter(Boolean);
|
|
1784
1827
|
}
|
|
1785
|
-
function positionalTokens(
|
|
1786
|
-
return
|
|
1828
|
+
function positionalTokens(tokens) {
|
|
1829
|
+
return tokens.slice(1).filter((t) => !t.startsWith("-") && !t.startsWith("@") && t.length > 0);
|
|
1787
1830
|
}
|
|
1788
1831
|
function analyzePipeChain(command) {
|
|
1789
1832
|
const segments2 = splitOnPipe(command);
|
|
@@ -1806,8 +1849,10 @@ function analyzePipeChain(command) {
|
|
|
1806
1849
|
for (const segment of segments2) {
|
|
1807
1850
|
const tokens = segment.split(/\s+/).filter(Boolean);
|
|
1808
1851
|
if (tokens.length === 0) continue;
|
|
1809
|
-
const
|
|
1810
|
-
const
|
|
1852
|
+
const h = unwrapCommandHead(tokens);
|
|
1853
|
+
const head = h < tokens.length ? h : 0;
|
|
1854
|
+
const binary = tokens[head].toLowerCase();
|
|
1855
|
+
const args = positionalTokens(tokens.slice(head));
|
|
1811
1856
|
if (SOURCE_COMMANDS.has(binary)) {
|
|
1812
1857
|
sourceFiles.push(...args);
|
|
1813
1858
|
if (args.some(isSensitivePath)) hasSensitiveSource = true;
|
|
@@ -3287,7 +3332,7 @@ function* stringValues(obj, depth = 0) {
|
|
|
3287
3332
|
}
|
|
3288
3333
|
for (const v of Object.values(obj)) yield* stringValues(v, depth + 1);
|
|
3289
3334
|
}
|
|
3290
|
-
var IBAN_LENGTH, B58, B58_INDEX, XPRV_VERSIONS, MAX, UNTRUSTED_TOOLS, SIGNALS, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, MAX_DEPTH, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, DLP_SCAN_LIMITS, MAX_REGEX_LENGTH, REGEX_CACHE_MAX, regexCache, FORBIDDEN_PATH_SEGMENTS, syntax, sharedParser, MESSAGE_FLAGS, SHELL_INTERPRETERS, DOWNLOAD_CMDS, NORMALIZE_CACHE_MAX, normalizeCache, AST_CACHE_MAX, astCache, PARSE_FAIL, FS_READ_TOOLS, FS_OP_PRESCREEN_RE, HOME_CACHE_ALLOWLIST, SENSITIVE_PATH_RULES, BASH_TOOL_NAMES, AST_FS_REGEX_RULES, SQL_DB_CLIS, SQL_DDL_RE, CHMOD_OPEN_PERM_TOKENS, COMMAND_WRAPPERS, INLINE_INTERPRETER, RUNNER_WRAPPERS, _redirStdinOps, _listOps, WRAPPER_TAKES_TARGET, INTERP_LEADING_TARGET, NET_BINARIES, VALUE_FLAGS, FS_OP_CACHE_MAX, fsOpCache, stripDotSlash, REDIR_TRUNCATE_OPS, REDIR_HEREDOC_OPS, DEFAULT_EGRESS_ALLOWLIST, SOURCE_COMMANDS, SINK_COMMANDS, OBFUSCATORS, SENSITIVE_PATTERNS, FLAGS_WITH_VALUES, SSRF_MAX_HOST, METADATA_ADDRESSES, STRICT_TIERS, METADATA_HOSTNAMES, v4Octets, TIER_REASON, DESTINATION_ARGS, VERDICT_RANK, SQL_DML_KEYWORDS, aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, BUILTIN_SHIELDS, LOOP_MAX_RECORDS, FINDING_TO_SIGNAL, SCAN_SIGNAL_WEIGHTS, LOOP_THRESHOLD_FOR_WASTE, DESTRUCTIVE_OP_RE, SENSITIVE_PATH_RE, FILE_TOOLS, PII_EMAIL_RE, PII_SSN_RE, PII_PHONE_RE, PII_CC16_RE, PII_CC15_RE, PII_IBAN_RE, REALTIME_PII_PATTERNS, MAX_PII_SCAN_BYTES, CANARY_MIN_LENGTH, MAX_TEXT, MAX_DEPTH2, MAX_JSON_PARSE, URL_DEPTH, B64_DEPTH, MIN_SEGMENT, SEPARATORS, stripSeparators, looksText, CANARY_DECODERS, VIEWS, LONG_OUTPUT_THRESHOLD_BYTES, CANONICAL_EXTRACTOR_VERSION, DEDUPE_PREVIEW_LEN, TERMINAL_ESCAPE_RE, ENGINE_VERSION;
|
|
3335
|
+
var IBAN_LENGTH, B58, B58_INDEX, XPRV_VERSIONS, MAX, UNTRUSTED_TOOLS, SIGNALS, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, MAX_DEPTH, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, DLP_SCAN_LIMITS, MAX_REGEX_LENGTH, REGEX_CACHE_MAX, regexCache, FORBIDDEN_PATH_SEGMENTS, syntax, sharedParser, MESSAGE_FLAGS, SHELL_INTERPRETERS, DOWNLOAD_CMDS, NORMALIZE_CACHE_MAX, normalizeCache, AST_CACHE_MAX, astCache, PARSE_FAIL, FS_READ_TOOLS, FS_OP_PRESCREEN_RE, HOME_CACHE_ALLOWLIST, SENSITIVE_PATH_RULES, BASH_TOOL_NAMES, AST_FS_REGEX_RULES, SQL_DB_CLIS, SQL_DDL_RE, CHMOD_OPEN_PERM_TOKENS, COMMAND_WRAPPERS, INLINE_INTERPRETER, RUNNER_WRAPPERS, _redirStdinOps, _listOps, WRAPPER_TAKES_TARGET, FIND_EXEC_FLAGS, INTERP_LEADING_TARGET, NET_BINARIES, VALUE_FLAGS, FS_OP_CACHE_MAX, fsOpCache, stripDotSlash, REDIR_TRUNCATE_OPS, REDIR_FILE_IN_OPS, REDIR_HEREDOC_OPS, isReaderWord, positionalAfter, DEFAULT_EGRESS_ALLOWLIST, SOURCE_COMMANDS, SINK_COMMANDS, OBFUSCATORS, SENSITIVE_PATTERNS, FLAGS_WITH_VALUES, SSRF_MAX_HOST, METADATA_ADDRESSES, STRICT_TIERS, METADATA_HOSTNAMES, v4Octets, TIER_REASON, DESTINATION_ARGS, VERDICT_RANK, SQL_DML_KEYWORDS, aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, BUILTIN_SHIELDS, LOOP_MAX_RECORDS, FINDING_TO_SIGNAL, SCAN_SIGNAL_WEIGHTS, LOOP_THRESHOLD_FOR_WASTE, DESTRUCTIVE_OP_RE, SENSITIVE_PATH_RE, FILE_TOOLS, PII_EMAIL_RE, PII_SSN_RE, PII_PHONE_RE, PII_CC16_RE, PII_CC15_RE, PII_IBAN_RE, REALTIME_PII_PATTERNS, MAX_PII_SCAN_BYTES, CANARY_MIN_LENGTH, MAX_TEXT, MAX_DEPTH2, MAX_JSON_PARSE, URL_DEPTH, B64_DEPTH, MIN_SEGMENT, SEPARATORS, stripSeparators, looksText, CANARY_DECODERS, VIEWS, LONG_OUTPUT_THRESHOLD_BYTES, CANONICAL_EXTRACTOR_VERSION, DEDUPE_PREVIEW_LEN, TERMINAL_ESCAPE_RE, ENGINE_VERSION;
|
|
3291
3336
|
var init_dist = __esm({
|
|
3292
3337
|
"packages/policy-engine/dist/index.mjs"() {
|
|
3293
3338
|
"use strict";
|
|
@@ -3905,13 +3950,32 @@ var init_dist = __esm({
|
|
|
3905
3950
|
})
|
|
3906
3951
|
);
|
|
3907
3952
|
SENSITIVE_PATH_PATTERNS = [
|
|
3908
|
-
/[/\\]\.ssh[/\\]/i,
|
|
3909
|
-
/[/\\]\.aws[/\\]/i,
|
|
3953
|
+
/[/\\]\.ssh([/\\]|$)/i,
|
|
3954
|
+
/[/\\]\.aws([/\\]|$)/i,
|
|
3910
3955
|
/[/\\]\.config[/\\]gcloud[/\\]/i,
|
|
3911
3956
|
/[/\\]\.azure[/\\]/i,
|
|
3912
3957
|
/[/\\]\.kube[/\\]config$/i,
|
|
3913
|
-
|
|
3914
|
-
// .
|
|
3958
|
+
// ⚠️ ONE SEMANTIC, FOUR COPIES. This is the AST tier's `.env` rule verbatim
|
|
3959
|
+
// (shell/index.ts SENSITIVE_PATH_RULES), whose reasoning is documented there:
|
|
3960
|
+
// structural suffix chain rather than a hand-written list, `example|sample|
|
|
3961
|
+
// template` exempt because a fixture stays a fixture whatever follows, and
|
|
3962
|
+
// `.test` anchored because `test` names an ENVIRONMENT -- `.env.test` is the
|
|
3963
|
+
// committed template, `.env.test.local` is gitignored and holds real values.
|
|
3964
|
+
//
|
|
3965
|
+
// It was previously `[/\\]\.env($|\.)` with NO exemptions, so `Read .env.example`
|
|
3966
|
+
// blocked while `cat .env.example` allowed: the same file, opposite verdicts,
|
|
3967
|
+
// decided only by which tool asked. See src/__tests__/jail-both-doors.test.ts,
|
|
3968
|
+
// which is the contract that now holds these copies in step, and stage 5 of
|
|
3969
|
+
// doc/credential-jail-architecture.md, which replaces them with one generated
|
|
3970
|
+
// source.
|
|
3971
|
+
// ⚠️ The `.local` branch comes FIRST and takes no exemption. A fixture stays a
|
|
3972
|
+
// fixture whatever follows it -- `.env.example.md` is documentation -- but
|
|
3973
|
+
// `.env.example.local` is gitignored by the `.env*.local` convention and holds
|
|
3974
|
+
// real values, exactly the reasoning that anchors `(?!\.test$)` rather than
|
|
3975
|
+
// using `\b`. Without this branch the fixture exemption also bought a two-step
|
|
3976
|
+
// bypass: `cp .env .env.sample`, then read the copy.
|
|
3977
|
+
/[/\\]\.env(?![\w-])(?:[\w.-]*\.local$|(?!\.(?:example|sample|template)\b)(?!\.test$)[\w.-]*$)/i,
|
|
3978
|
+
// .env + any suffix chain; fixtures exempt unless .local
|
|
3915
3979
|
/[/\\]\.git-credentials$/i,
|
|
3916
3980
|
/[/\\]\.npmrc$/i,
|
|
3917
3981
|
/[/\\]\.docker[/\\]config\.json$/i,
|
|
@@ -3993,6 +4057,10 @@ var init_dist = __esm({
|
|
|
3993
4057
|
"od",
|
|
3994
4058
|
"xxd",
|
|
3995
4059
|
"hexdump",
|
|
4060
|
+
// Emits the file's bytes, re-encoded, so it is a read by the set's own test
|
|
4061
|
+
// ("does it emit file contents"). Absent until 2026-09-10, which is why
|
|
4062
|
+
// `base64 ~/.ssh/id_rsa` printed a private key with no verdict.
|
|
4063
|
+
"base64",
|
|
3996
4064
|
"strings",
|
|
3997
4065
|
"sort",
|
|
3998
4066
|
"uniq",
|
|
@@ -4001,7 +4069,11 @@ var init_dist = __esm({
|
|
|
4001
4069
|
"dd"
|
|
4002
4070
|
]);
|
|
4003
4071
|
FS_OP_PRESCREEN_RE = new RegExp(
|
|
4004
|
-
|
|
4072
|
+
// A quote is a separator too: `eval "cat X"` and `sh -c 'cat X'` put the
|
|
4073
|
+
// reader right after `"` / `'`, and without these two characters the
|
|
4074
|
+
// prescreen rejected every string-wrapped read before the parser ran.
|
|
4075
|
+
// Found 2026-09-11 by instrumenting the walk -- no CallExpr was ever visited.
|
|
4076
|
+
`(?:^|[\\s|;&("'\`\\n])(?:rm|${[...FS_READ_TOOLS].map((t) => t.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|")})\\b|(?<!<)<(?!<)`
|
|
4005
4077
|
);
|
|
4006
4078
|
HOME_CACHE_ALLOWLIST = [
|
|
4007
4079
|
".cache",
|
|
@@ -4022,12 +4094,12 @@ var init_dist = __esm({
|
|
|
4022
4094
|
{
|
|
4023
4095
|
rule: "shield:project-jail:block-read-ssh",
|
|
4024
4096
|
reason: "Reading SSH private keys is blocked by project-jail shield",
|
|
4025
|
-
match: (p) => /(
|
|
4097
|
+
match: (p) => /([\\/]\.ssh[\\/]|^\.ssh[\\/]|^(?:[~/]|[A-Za-z]:).*[\\/]\.ssh$)/i.test(p)
|
|
4026
4098
|
},
|
|
4027
4099
|
{
|
|
4028
4100
|
rule: "shield:project-jail:block-read-aws",
|
|
4029
4101
|
reason: "Reading AWS credentials is blocked by project-jail shield",
|
|
4030
|
-
match: (p) => /(
|
|
4102
|
+
match: (p) => /([\\/]\.aws[\\/]|^\.aws[\\/]|^(?:[~/]|[A-Za-z]:).*[\\/]\.aws$)/i.test(p)
|
|
4031
4103
|
},
|
|
4032
4104
|
{
|
|
4033
4105
|
// Mirrors the JSON shield's `.env` pattern (project-jail.json's
|
|
@@ -4071,7 +4143,9 @@ var init_dist = __esm({
|
|
|
4071
4143
|
// symmetry — silently exempts every `.env.test.*` file.
|
|
4072
4144
|
//
|
|
4073
4145
|
// shields.test.ts:983-995 is the canonical contract; keep both in step.
|
|
4074
|
-
match: (p) => /(?:^|[\\/])\.env(?![\w-])(?!\.(?:example|sample|template)\b)(?!\.test$)[\w.-]
|
|
4146
|
+
match: (p) => /(?:^|[\\/])\.env(?![\w-])(?:[\w.-]*\.local$|(?!\.(?:example|sample|template)\b)(?!\.test$)[\w.-]*$)/i.test(
|
|
4147
|
+
p
|
|
4148
|
+
)
|
|
4075
4149
|
},
|
|
4076
4150
|
{
|
|
4077
4151
|
// verdict: 'review' (not 'block') is a deliberate design choice
|
|
@@ -4182,8 +4256,18 @@ var init_dist = __esm({
|
|
|
4182
4256
|
_redirStdinOps = null;
|
|
4183
4257
|
_listOps = null;
|
|
4184
4258
|
WRAPPER_TAKES_TARGET = /* @__PURE__ */ new Set(["chroot"]);
|
|
4259
|
+
FIND_EXEC_FLAGS = /* @__PURE__ */ new Set(["-exec", "-execdir", "-ok", "-okdir"]);
|
|
4185
4260
|
INTERP_LEADING_TARGET = /* @__PURE__ */ new Set(["su"]);
|
|
4186
|
-
NET_BINARIES = /* @__PURE__ */ new Set([
|
|
4261
|
+
NET_BINARIES = /* @__PURE__ */ new Set([
|
|
4262
|
+
"curl",
|
|
4263
|
+
"wget",
|
|
4264
|
+
"scp",
|
|
4265
|
+
"ssh",
|
|
4266
|
+
"nc",
|
|
4267
|
+
"ncat",
|
|
4268
|
+
"netcat",
|
|
4269
|
+
"rsync"
|
|
4270
|
+
]);
|
|
4187
4271
|
VALUE_FLAGS = {
|
|
4188
4272
|
curl: /* @__PURE__ */ new Set([
|
|
4189
4273
|
"-d",
|
|
@@ -4272,10 +4356,15 @@ var init_dist = __esm({
|
|
|
4272
4356
|
fsOpCache = /* @__PURE__ */ new Map();
|
|
4273
4357
|
stripDotSlash = (p) => p.replace(/^\.\//, "");
|
|
4274
4358
|
REDIR_TRUNCATE_OPS = /* @__PURE__ */ new Set([deriveRedirOp(">_f")]);
|
|
4359
|
+
REDIR_FILE_IN_OPS = new Set(
|
|
4360
|
+
[deriveRedirOp("cat < f"), deriveRedirOp("cat <> f")].filter((op) => op >= 0)
|
|
4361
|
+
);
|
|
4275
4362
|
REDIR_HEREDOC_OPS = /* @__PURE__ */ new Set([
|
|
4276
4363
|
deriveRedirOp("cat <<X\nX"),
|
|
4277
4364
|
deriveRedirOp("cat <<-X\nX")
|
|
4278
4365
|
]);
|
|
4366
|
+
isReaderWord = (w) => w !== null && FS_READ_TOOLS.has(w.split("/").pop()?.toLowerCase() ?? "");
|
|
4367
|
+
positionalAfter = (words, from, to = words.length) => words.slice(from, to).filter((w) => w !== null && !w.startsWith("-"));
|
|
4279
4368
|
DEFAULT_EGRESS_ALLOWLIST = [
|
|
4280
4369
|
// node9's own control plane (api, app, dev-api, staging and the apex).
|
|
4281
4370
|
// Without it, turning egress on asks the user to approve node9 itself.
|
|
@@ -4299,21 +4388,7 @@ var init_dist = __esm({
|
|
|
4299
4388
|
"deb.debian.org",
|
|
4300
4389
|
"*.ubuntu.com"
|
|
4301
4390
|
];
|
|
4302
|
-
SOURCE_COMMANDS = /* @__PURE__ */ new Set([
|
|
4303
|
-
"cat",
|
|
4304
|
-
"head",
|
|
4305
|
-
"tail",
|
|
4306
|
-
"grep",
|
|
4307
|
-
"awk",
|
|
4308
|
-
"sed",
|
|
4309
|
-
"cut",
|
|
4310
|
-
"sort",
|
|
4311
|
-
"tee",
|
|
4312
|
-
"less",
|
|
4313
|
-
"more",
|
|
4314
|
-
"strings",
|
|
4315
|
-
"xxd"
|
|
4316
|
-
]);
|
|
4391
|
+
SOURCE_COMMANDS = /* @__PURE__ */ new Set([...FS_READ_TOOLS, "tee"]);
|
|
4317
4392
|
SINK_COMMANDS = /* @__PURE__ */ new Set([
|
|
4318
4393
|
"curl",
|
|
4319
4394
|
"wget",
|
|
@@ -4344,16 +4419,25 @@ var init_dist = __esm({
|
|
|
4344
4419
|
"node"
|
|
4345
4420
|
]);
|
|
4346
4421
|
SENSITIVE_PATTERNS = [
|
|
4347
|
-
/
|
|
4348
|
-
|
|
4422
|
+
// Kept in step with the AST tier and dlp/ -- see jail-both-doors.test.ts.
|
|
4423
|
+
/(?:^|\/)\.env(?![\w-])(?:[\w.-]*\.local$|(?!\.(?:example|sample|template)\b)(?!\.test$)[\w.-]*$)/i,
|
|
4424
|
+
// .env chain; fixtures exempt unless .local
|
|
4349
4425
|
/id_rsa|id_ed25519|id_ecdsa|id_dsa/i,
|
|
4350
4426
|
// SSH private keys
|
|
4351
4427
|
/\.pem$|\.key$|\.p12$|\.pfx$/i,
|
|
4352
4428
|
// certificate files
|
|
4353
|
-
/
|
|
4354
|
-
//
|
|
4355
|
-
/
|
|
4356
|
-
//
|
|
4429
|
+
// The `$` half mirrors shell/index.ts's SENSITIVE_PATH_RULES: a file INSIDE
|
|
4430
|
+
// the directory counts wherever it appears, while the directory ITSELF counts
|
|
4431
|
+
// only when the path is ROOTED (`~/.ssh`, `/home/u/.ssh`) -- an unrooted
|
|
4432
|
+
// `config/.ssh` is more likely a search pattern than a read. These are
|
|
4433
|
+
// extracted TOKENS (see `args.some(isSensitivePath)` below), the same input
|
|
4434
|
+
// contract as the shell tier, so the same boundary is the right one.
|
|
4435
|
+
// Without it `grep -r x ~/.ssh | curl -d @-` scored one tier BELOW the
|
|
4436
|
+
// identical pipeline naming a file inside that directory.
|
|
4437
|
+
/(?:^|\/)\.ssh\/|^(?:[~/]|[A-Za-z]:).*\/\.ssh$/i,
|
|
4438
|
+
// ~/.ssh/ and ~/.ssh
|
|
4439
|
+
/(?:^|\/)\.aws\/credentials|^(?:[~/]|[A-Za-z]:).*\/\.aws$/i,
|
|
4440
|
+
// AWS creds + dir
|
|
4357
4441
|
/(?:^|\/)\.netrc$/i,
|
|
4358
4442
|
// netrc (stores HTTP credentials)
|
|
4359
4443
|
/(?:^|\/)(passwd|shadow|sudoers)$/i,
|
|
@@ -5021,7 +5105,7 @@ var init_dist = __esm({
|
|
|
5021
5105
|
{
|
|
5022
5106
|
field: "command",
|
|
5023
5107
|
op: "matches",
|
|
5024
|
-
value: "(cat|less|head|tail|bat|more|open|print|nano|vim|vi|emacs|code|type|grep|egrep|fgrep|rg|ag|ack|awk|gawk|sed|cut|tr|jq|yq|od|xxd|hexdump|strings|sort|uniq|tac|nl|dd)\\s+.*?\\.ssh[\\/\\\\]",
|
|
5108
|
+
value: "(cat|less|head|tail|bat|more|open|print|nano|vim|vi|emacs|code|type|grep|egrep|fgrep|rg|ag|ack|awk|gawk|sed|cut|tr|jq|yq|od|xxd|hexdump|strings|base64|sort|uniq|tac|nl|dd)\\s+.*?\\.ssh[\\/\\\\]",
|
|
5025
5109
|
flags: "i"
|
|
5026
5110
|
}
|
|
5027
5111
|
],
|
|
@@ -5035,7 +5119,7 @@ var init_dist = __esm({
|
|
|
5035
5119
|
{
|
|
5036
5120
|
field: "command",
|
|
5037
5121
|
op: "matches",
|
|
5038
|
-
value: "(cat|less|head|tail|bat|more|open|print|nano|vim|vi|emacs|code|type|grep|egrep|fgrep|rg|ag|ack|awk|gawk|sed|cut|tr|jq|yq|od|xxd|hexdump|strings|sort|uniq|tac|nl|dd)\\s+.*?\\.aws[\\/\\\\]",
|
|
5122
|
+
value: "(cat|less|head|tail|bat|more|open|print|nano|vim|vi|emacs|code|type|grep|egrep|fgrep|rg|ag|ack|awk|gawk|sed|cut|tr|jq|yq|od|xxd|hexdump|strings|base64|sort|uniq|tac|nl|dd)\\s+.*?\\.aws[\\/\\\\]",
|
|
5039
5123
|
flags: "i"
|
|
5040
5124
|
}
|
|
5041
5125
|
],
|
|
@@ -5049,7 +5133,7 @@ var init_dist = __esm({
|
|
|
5049
5133
|
{
|
|
5050
5134
|
field: "command",
|
|
5051
5135
|
op: "matches",
|
|
5052
|
-
value: "(cat|less|head|tail|bat|more|open|print|nano|vim|vi|emacs|code|type|grep|egrep|fgrep|rg|ag|ack|awk|gawk|sed|cut|tr|jq|yq|od|xxd|hexdump|strings|sort|uniq|tac|nl|dd)\\s+.*?\\.env(\\.(local|production|staging|development|production\\.local|staging\\.local|development\\.local))?(?=\\s|$|[;&|>)<])",
|
|
5136
|
+
value: "(cat|less|head|tail|bat|more|open|print|nano|vim|vi|emacs|code|type|grep|egrep|fgrep|rg|ag|ack|awk|gawk|sed|cut|tr|jq|yq|od|xxd|hexdump|strings|base64|sort|uniq|tac|nl|dd)\\s+.*?\\.env(\\.(local|production|staging|development|production\\.local|staging\\.local|development\\.local))?(?=\\s|$|[;&|>)<])",
|
|
5053
5137
|
flags: "i"
|
|
5054
5138
|
}
|
|
5055
5139
|
],
|
|
@@ -5063,7 +5147,7 @@ var init_dist = __esm({
|
|
|
5063
5147
|
{
|
|
5064
5148
|
field: "command",
|
|
5065
5149
|
op: "matches",
|
|
5066
|
-
value: "(cat|less|head|tail|bat|more|open|print|nano|vim|vi|emacs|code|type|grep|egrep|fgrep|rg|ag|ack|awk|gawk|sed|cut|tr|jq|yq|od|xxd|hexdump|strings|sort|uniq|tac|nl|dd)\\s+.*(credentials\\.json|\\.netrc|\\.npmrc|\\.docker[\\/\\\\]config\\.json|gcloud[\\/\\\\]credentials)",
|
|
5150
|
+
value: "(cat|less|head|tail|bat|more|open|print|nano|vim|vi|emacs|code|type|grep|egrep|fgrep|rg|ag|ack|awk|gawk|sed|cut|tr|jq|yq|od|xxd|hexdump|strings|base64|sort|uniq|tac|nl|dd)\\s+.*(credentials\\.json|\\.netrc|\\.npmrc|\\.docker[\\/\\\\]config\\.json|gcloud[\\/\\\\]credentials)",
|
|
5067
5151
|
flags: "i"
|
|
5068
5152
|
}
|
|
5069
5153
|
],
|
|
@@ -5077,7 +5161,7 @@ var init_dist = __esm({
|
|
|
5077
5161
|
{
|
|
5078
5162
|
field: "file_path",
|
|
5079
5163
|
op: "matches",
|
|
5080
|
-
value: "(
|
|
5164
|
+
value: "([\\/\\\\]\\.ssh([\\/\\\\]|$)|^\\.ssh[\\/\\\\])",
|
|
5081
5165
|
flags: "i"
|
|
5082
5166
|
}
|
|
5083
5167
|
],
|
|
@@ -5091,7 +5175,7 @@ var init_dist = __esm({
|
|
|
5091
5175
|
{
|
|
5092
5176
|
field: "file_path",
|
|
5093
5177
|
op: "matches",
|
|
5094
|
-
value: "(
|
|
5178
|
+
value: "([\\/\\\\]\\.aws([\\/\\\\]|$)|^\\.aws[\\/\\\\])",
|
|
5095
5179
|
flags: "i"
|
|
5096
5180
|
}
|
|
5097
5181
|
],
|
|
@@ -5105,7 +5189,7 @@ var init_dist = __esm({
|
|
|
5105
5189
|
{
|
|
5106
5190
|
field: "file_path",
|
|
5107
5191
|
op: "matches",
|
|
5108
|
-
value: "(^|[\\/\\\\])\\.env(
|
|
5192
|
+
value: "(^|[\\/\\\\])\\.env(?![\\w-])(?:[\\w.-]*\\.local$|(?!\\.(example|sample|template)\\b)(?!\\.test$)[\\w.-]*$)",
|
|
5109
5193
|
flags: "i"
|
|
5110
5194
|
}
|
|
5111
5195
|
],
|
|
@@ -5248,7 +5332,7 @@ var init_dist = __esm({
|
|
|
5248
5332
|
};
|
|
5249
5333
|
LOOP_THRESHOLD_FOR_WASTE = 3;
|
|
5250
5334
|
DESTRUCTIVE_OP_RE = /\brm\s+-[rRf]+\b|\bDROP\s+(TABLE|DATABASE|COLLECTION|SCHEMA)\b|\bTRUNCATE\s+TABLE\b|\bgit\s+push\s+(--force|-f)\b|\bFLUSHALL\b|\bFLUSHDB\b|\bkubectl\s+delete\b|\bhelm\s+uninstall\b/i;
|
|
5251
|
-
SENSITIVE_PATH_RE =
|
|
5335
|
+
SENSITIVE_PATH_RE = /[\\/]\.aws(?:[\\/]|$)|^\.aws[\\/]|[\\/]\.ssh(?:[\\/]|$)|^\.ssh[\\/]|(?:^|[\\/])\.env(?![\w-])(?:[\w.-]*\.local$|(?!\.(?:example|sample|template)\b)(?!\.test$)[\w.-]*$)|\.config\/gcloud\/credentials\.db\b|\.docker\/config\.json\b|\.netrc\b|\.npmrc\b|\.node9\/credentials\.json\b/i;
|
|
5252
5336
|
FILE_TOOLS = /* @__PURE__ */ new Set([
|
|
5253
5337
|
"read",
|
|
5254
5338
|
"read_file",
|
|
@@ -5325,7 +5409,7 @@ var init_dist = __esm({
|
|
|
5325
5409
|
{ view: "separators-stripped", decoder: "separators", stripped: true }
|
|
5326
5410
|
];
|
|
5327
5411
|
LONG_OUTPUT_THRESHOLD_BYTES = 100 * 1024;
|
|
5328
|
-
CANONICAL_EXTRACTOR_VERSION = "canonical-
|
|
5412
|
+
CANONICAL_EXTRACTOR_VERSION = "canonical-v13";
|
|
5329
5413
|
DEDUPE_PREVIEW_LEN = 120;
|
|
5330
5414
|
TERMINAL_ESCAPE_RE = // eslint-disable-next-line no-control-regex
|
|
5331
5415
|
/\x1b\[[0-9;?]*[A-Za-z]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b[@-_]|[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g;
|
|
@@ -8643,7 +8727,7 @@ function isNetworkTool(toolName, args) {
|
|
|
8643
8727
|
if (t === "bash" || t === "shell" || t === "run_shell_command" || t === "terminal.execute") {
|
|
8644
8728
|
const a = args;
|
|
8645
8729
|
const cmd = typeof a?.command === "string" ? a.command : typeof a?.cmd === "string" ? a.cmd : "";
|
|
8646
|
-
return
|
|
8730
|
+
return NETWORK_COMMAND_RE.test(cmd);
|
|
8647
8731
|
}
|
|
8648
8732
|
return false;
|
|
8649
8733
|
}
|
|
@@ -9518,7 +9602,7 @@ function canaryRecordById(id) {
|
|
|
9518
9602
|
return null;
|
|
9519
9603
|
}
|
|
9520
9604
|
}
|
|
9521
|
-
var WRITE_TOOLS;
|
|
9605
|
+
var WRITE_TOOLS, NETWORK_COMMAND_RE;
|
|
9522
9606
|
var init_orchestrator = __esm({
|
|
9523
9607
|
"src/auth/orchestrator.ts"() {
|
|
9524
9608
|
"use strict";
|
|
@@ -9548,6 +9632,7 @@ var init_orchestrator = __esm({
|
|
|
9548
9632
|
"notebook_edit",
|
|
9549
9633
|
"notebookedit"
|
|
9550
9634
|
]);
|
|
9635
|
+
NETWORK_COMMAND_RE = new RegExp(`(?<![.\\w-])(${[...NET_BINARIES].join("|")})\\b`);
|
|
9551
9636
|
}
|
|
9552
9637
|
});
|
|
9553
9638
|
|
|
@@ -12613,9 +12698,10 @@ async function ensurePricingLoaded() {
|
|
|
12613
12698
|
memCacheAt = Date.now();
|
|
12614
12699
|
lookupCache.clear();
|
|
12615
12700
|
}
|
|
12616
|
-
function pricingFor(model) {
|
|
12701
|
+
function pricingFor(model, options = {}) {
|
|
12617
12702
|
const norm = normalizeModel(model);
|
|
12618
|
-
const
|
|
12703
|
+
const lookupKey = options.exact ? `exact:${norm}` : norm;
|
|
12704
|
+
const cached = lookupCache.get(lookupKey);
|
|
12619
12705
|
if (cached !== void 0) return cached;
|
|
12620
12706
|
if (memCache === null && !diskChecked) {
|
|
12621
12707
|
diskChecked = true;
|
|
@@ -12635,6 +12721,7 @@ function pricingFor(model) {
|
|
|
12635
12721
|
resolved = exact;
|
|
12636
12722
|
break;
|
|
12637
12723
|
}
|
|
12724
|
+
if (options.exact) continue;
|
|
12638
12725
|
let best = null;
|
|
12639
12726
|
for (const key of Object.keys(source)) {
|
|
12640
12727
|
if (norm.startsWith(key.toLowerCase()) && (best === null || key.length > best.length)) {
|
|
@@ -12646,7 +12733,7 @@ function pricingFor(model) {
|
|
|
12646
12733
|
break;
|
|
12647
12734
|
}
|
|
12648
12735
|
}
|
|
12649
|
-
lookupCache.set(
|
|
12736
|
+
lookupCache.set(lookupKey, resolved);
|
|
12650
12737
|
return resolved;
|
|
12651
12738
|
}
|
|
12652
12739
|
var LITELLM_URL, BUNDLED_PRICING, CACHE_FILE, TTL_MS, memCache, memCacheAt, diskChecked, lookupCache;
|
|
@@ -12680,6 +12767,18 @@ var init_litellm = __esm({
|
|
|
12680
12767
|
"gpt-5": [125e-8, 1e-5, 0, 125e-9],
|
|
12681
12768
|
"gpt-5-codex": [125e-8, 1e-5, 0, 125e-9],
|
|
12682
12769
|
"gpt-5-mini": [25e-8, 2e-6, 0, 25e-9],
|
|
12770
|
+
// Codex offline rates checked against official OpenAI model pages, 2026-09-11.
|
|
12771
|
+
"gpt-5.1-codex": [125e-8, 1e-5, 0, 125e-9],
|
|
12772
|
+
"gpt-5.1-codex-max": [125e-8, 1e-5, 0, 125e-9],
|
|
12773
|
+
"gpt-5.1-codex-mini": [25e-8, 2e-6, 0, 25e-9],
|
|
12774
|
+
"gpt-5.2-codex": [175e-8, 14e-6, 0, 175e-9],
|
|
12775
|
+
"gpt-5.3-codex": [175e-8, 14e-6, 0, 175e-9],
|
|
12776
|
+
"gpt-5.4": [25e-7, 15e-6, 0, 25e-8],
|
|
12777
|
+
"gpt-5.4-mini": [75e-8, 45e-7, 0, 75e-9],
|
|
12778
|
+
"gpt-5.5": [5e-6, 3e-5, 0, 5e-7],
|
|
12779
|
+
"gpt-5.6-sol": [4e-6, 2e-5, 5e-6, 4e-7],
|
|
12780
|
+
"gpt-5.6-terra": [2e-6, 12e-6, 25e-7, 2e-7],
|
|
12781
|
+
"gpt-6-astra": [1e-5, 5e-5, 125e-7, 1e-6],
|
|
12683
12782
|
o3: [2e-6, 8e-6, 0, 5e-7],
|
|
12684
12783
|
"o4-mini": [11e-7, 44e-7, 0, 275e-9],
|
|
12685
12784
|
// Google. Values copied from the live LiteLLM table (verified 2026-06-14)
|
|
@@ -12851,103 +12950,237 @@ import fs21 from "fs";
|
|
|
12851
12950
|
import os20 from "os";
|
|
12852
12951
|
import path23 from "path";
|
|
12853
12952
|
function codexSessionsDir() {
|
|
12854
|
-
return path23.join(os20.homedir(), ".codex", "sessions");
|
|
12953
|
+
return path23.join(process.env.CODEX_HOME?.trim() || path23.join(os20.homedir(), ".codex"), "sessions");
|
|
12855
12954
|
}
|
|
12856
12955
|
function codexPriceFor(model) {
|
|
12857
|
-
return pricingFor(model) ?? CODEX_FALLBACK;
|
|
12956
|
+
return pricingFor(codexModel(model), { exact: true }) ?? CODEX_FALLBACK;
|
|
12858
12957
|
}
|
|
12859
|
-
function
|
|
12860
|
-
|
|
12861
|
-
const [pin, pout, , pcr] = codexPriceFor(model || "gpt-5");
|
|
12862
|
-
return nonCached * pin + tokens.cached * pcr + tokens.output * pout;
|
|
12958
|
+
function codexModel(model) {
|
|
12959
|
+
return normalizeModel(model.replace(/^openai\//i, "").replace(/-\d{4}-\d{2}-\d{2}$/, ""));
|
|
12863
12960
|
}
|
|
12864
|
-
function
|
|
12865
|
-
|
|
12866
|
-
|
|
12867
|
-
|
|
12868
|
-
|
|
12869
|
-
|
|
12870
|
-
|
|
12871
|
-
|
|
12872
|
-
|
|
12873
|
-
|
|
12874
|
-
|
|
12875
|
-
|
|
12876
|
-
|
|
12877
|
-
|
|
12961
|
+
function addTokens(previous, delta) {
|
|
12962
|
+
return {
|
|
12963
|
+
input: (previous?.input ?? 0) + delta.input,
|
|
12964
|
+
cached: (previous?.cached ?? 0) + delta.cached,
|
|
12965
|
+
output: (previous?.output ?? 0) + delta.output,
|
|
12966
|
+
cacheWrite: (previous?.cacheWrite ?? 0) + delta.cacheWrite
|
|
12967
|
+
};
|
|
12968
|
+
}
|
|
12969
|
+
function codexSessionCost(model, tokens, request2) {
|
|
12970
|
+
const input = tokenNumber(tokens.input);
|
|
12971
|
+
const cached = Math.min(input, tokenNumber(tokens.cached));
|
|
12972
|
+
const written = Math.min(input - cached, tokenNumber(tokens.cacheWrite));
|
|
12973
|
+
const [pin, pout, pcw, pcr] = codexPriceFor(model || "gpt-5");
|
|
12974
|
+
const longContext = request2 && request2.inputTokens > 272e3 && ["gpt-5.4", "gpt-5.5", "gpt-5.6-sol", "gpt-5.6-terra", "gpt-6-astra"].includes(
|
|
12975
|
+
codexModel(model)
|
|
12976
|
+
);
|
|
12977
|
+
const inputMultiplier = longContext ? 2 : 1;
|
|
12978
|
+
const outputMultiplier = longContext ? 1.5 : 1;
|
|
12979
|
+
const tierMultiplier = request2?.serviceTier === "flex" ? 0.5 : codexModel(model) === "gpt-6-astra" && (request2?.serviceTier === "fast" || request2?.serviceTier === "priority") ? 2 : 1;
|
|
12980
|
+
return (((input - cached - written) * pin + cached * pcr + written * (pcw || pin)) * inputMultiplier + tokenNumber(tokens.output) * pout * outputMultiplier) * tierMultiplier;
|
|
12981
|
+
}
|
|
12982
|
+
function listCodexSessionFiles(base = codexSessionsDir()) {
|
|
12983
|
+
const files = [];
|
|
12984
|
+
const walk = (dir) => {
|
|
12985
|
+
try {
|
|
12986
|
+
for (const entry of fs21.readdirSync(dir, { withFileTypes: true })) {
|
|
12987
|
+
const file = path23.join(dir, entry.name);
|
|
12988
|
+
if (entry.isDirectory()) walk(file);
|
|
12989
|
+
else if (entry.isFile() && entry.name.endsWith(".jsonl")) files.push(file);
|
|
12878
12990
|
}
|
|
12991
|
+
} catch {
|
|
12992
|
+
}
|
|
12993
|
+
};
|
|
12994
|
+
walk(base);
|
|
12995
|
+
if (path23.basename(base) === "sessions") walk(path23.join(path23.dirname(base), "archived_sessions"));
|
|
12996
|
+
const sessions = /* @__PURE__ */ new Map();
|
|
12997
|
+
for (const file of files.sort()) {
|
|
12998
|
+
try {
|
|
12999
|
+
const stat = fs21.statSync(file);
|
|
13000
|
+
let id = "";
|
|
13001
|
+
try {
|
|
13002
|
+
const first = JSON.parse(fs21.readFileSync(file, "utf8").split("\n", 1)[0]);
|
|
13003
|
+
if (first?.type === "session_meta" && typeof first.payload?.id === "string")
|
|
13004
|
+
id = first.payload.id;
|
|
13005
|
+
} catch {
|
|
13006
|
+
}
|
|
13007
|
+
const key = id ? `session:${id}` : `file:${file}`;
|
|
13008
|
+
const prior = sessions.get(key);
|
|
13009
|
+
if (!prior || stat.mtimeMs > prior.mtime || stat.mtimeMs === prior.mtime && stat.size > prior.size) {
|
|
13010
|
+
sessions.set(key, { file, mtime: stat.mtimeMs, size: stat.size });
|
|
13011
|
+
}
|
|
13012
|
+
} catch {
|
|
12879
13013
|
}
|
|
12880
13014
|
}
|
|
12881
|
-
return
|
|
13015
|
+
return [...sessions.values()].map((s) => s.file);
|
|
12882
13016
|
}
|
|
12883
|
-
function
|
|
12884
|
-
|
|
12885
|
-
return fs21.readdirSync(dir);
|
|
12886
|
-
} catch {
|
|
12887
|
-
return [];
|
|
12888
|
-
}
|
|
13017
|
+
function record(value) {
|
|
13018
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
12889
13019
|
}
|
|
12890
|
-
function
|
|
12891
|
-
|
|
12892
|
-
|
|
12893
|
-
|
|
12894
|
-
|
|
13020
|
+
function tokenNumber(value) {
|
|
13021
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : 0;
|
|
13022
|
+
}
|
|
13023
|
+
function usage(value, fallback) {
|
|
13024
|
+
const u = record(value);
|
|
13025
|
+
if (!["input_tokens", "output_tokens"].some((k) => typeof u[k] === "number")) return null;
|
|
13026
|
+
for (const key of [
|
|
13027
|
+
"input_tokens",
|
|
13028
|
+
"cached_input_tokens",
|
|
13029
|
+
"cache_read_input_tokens",
|
|
13030
|
+
"output_tokens",
|
|
13031
|
+
"cache_write_input_tokens"
|
|
13032
|
+
]) {
|
|
13033
|
+
if (u[key] !== void 0 && (typeof u[key] !== "number" || !Number.isFinite(u[key]) || u[key] < 0))
|
|
13034
|
+
return null;
|
|
12895
13035
|
}
|
|
13036
|
+
return {
|
|
13037
|
+
input: tokenNumber(u.input_tokens ?? fallback?.input),
|
|
13038
|
+
cached: tokenNumber(u.cached_input_tokens ?? u.cache_read_input_tokens ?? fallback?.cached),
|
|
13039
|
+
output: tokenNumber(u.output_tokens ?? fallback?.output),
|
|
13040
|
+
cacheWrite: tokenNumber(u.cache_write_input_tokens ?? fallback?.cacheWrite)
|
|
13041
|
+
};
|
|
12896
13042
|
}
|
|
12897
|
-
function
|
|
12898
|
-
|
|
12899
|
-
|
|
12900
|
-
|
|
12901
|
-
|
|
12902
|
-
|
|
12903
|
-
|
|
12904
|
-
|
|
12905
|
-
|
|
13043
|
+
function timestamp(value) {
|
|
13044
|
+
return typeof value === "string" && Number.isFinite(Date.parse(value)) ? new Date(value).toISOString() : "";
|
|
13045
|
+
}
|
|
13046
|
+
function parseCodexUsage(lines) {
|
|
13047
|
+
const result = {
|
|
13048
|
+
events: [],
|
|
13049
|
+
sessionStart: "",
|
|
13050
|
+
runId: "",
|
|
13051
|
+
workingDir: "",
|
|
13052
|
+
legacyModels: []
|
|
13053
|
+
};
|
|
13054
|
+
let model = "gpt-5";
|
|
13055
|
+
let serviceTier;
|
|
13056
|
+
let previous = null;
|
|
13057
|
+
const legacyModels = /* @__PURE__ */ new Set();
|
|
13058
|
+
const seenStandalone = /* @__PURE__ */ new Set();
|
|
12906
13059
|
for (const raw of lines) {
|
|
12907
|
-
if (!raw.trim()) continue;
|
|
12908
13060
|
let entry;
|
|
12909
13061
|
try {
|
|
12910
|
-
entry = JSON.parse(raw);
|
|
13062
|
+
entry = record(JSON.parse(raw));
|
|
12911
13063
|
} catch {
|
|
12912
13064
|
continue;
|
|
12913
13065
|
}
|
|
12914
|
-
const p = entry.payload
|
|
13066
|
+
const p = record(entry.payload);
|
|
12915
13067
|
if (entry.type === "session_meta") {
|
|
12916
|
-
|
|
12917
|
-
if (!runId && typeof p
|
|
12918
|
-
if (!
|
|
13068
|
+
result.sessionStart ||= timestamp(p.timestamp ?? entry.timestamp);
|
|
13069
|
+
if (!result.runId && typeof p.id === "string") result.runId = p.id;
|
|
13070
|
+
if (!result.workingDir && typeof p.cwd === "string") result.workingDir = p.cwd;
|
|
12919
13071
|
continue;
|
|
12920
13072
|
}
|
|
12921
13073
|
if (entry.type === "turn_context") {
|
|
12922
|
-
if (typeof p
|
|
12923
|
-
|
|
13074
|
+
if (typeof p.model === "string" && p.model) {
|
|
13075
|
+
model = p.model;
|
|
13076
|
+
legacyModels.add(normalizeModel(model));
|
|
13077
|
+
}
|
|
13078
|
+
if (!result.workingDir && typeof p.cwd === "string") result.workingDir = p.cwd;
|
|
13079
|
+
serviceTier = typeof p.service_tier === "string" ? p.service_tier : void 0;
|
|
12924
13080
|
continue;
|
|
12925
13081
|
}
|
|
12926
|
-
if (entry.type
|
|
12927
|
-
|
|
12928
|
-
|
|
12929
|
-
|
|
12930
|
-
|
|
12931
|
-
|
|
12932
|
-
|
|
13082
|
+
if (entry.type !== "event_msg" || p.type !== "token_count") continue;
|
|
13083
|
+
const info = record(p.info);
|
|
13084
|
+
const total = usage(info.total_token_usage, previous);
|
|
13085
|
+
const last = usage(info.last_token_usage);
|
|
13086
|
+
if (!total && !last) continue;
|
|
13087
|
+
const eventModel = [info.model, info.model_name, p.model].find(
|
|
13088
|
+
(v) => typeof v === "string" && v
|
|
13089
|
+
);
|
|
13090
|
+
if (typeof eventModel === "string") model = eventModel;
|
|
13091
|
+
let delta;
|
|
13092
|
+
if (total) {
|
|
13093
|
+
if (previous && Object.keys(total).every(
|
|
13094
|
+
(k) => total[k] === previous[k]
|
|
13095
|
+
))
|
|
13096
|
+
continue;
|
|
13097
|
+
const reset = previous && (total.input < previous.input || total.output < previous.output);
|
|
13098
|
+
delta = reset ? last ?? total : {
|
|
13099
|
+
input: Math.max(0, total.input - (previous?.input ?? 0)),
|
|
13100
|
+
cached: Math.max(0, total.cached - (previous?.cached ?? 0)),
|
|
13101
|
+
output: Math.max(0, total.output - (previous?.output ?? 0)),
|
|
13102
|
+
cacheWrite: Math.max(0, total.cacheWrite - (previous?.cacheWrite ?? 0))
|
|
13103
|
+
};
|
|
13104
|
+
previous = total;
|
|
13105
|
+
} else {
|
|
13106
|
+
delta = last;
|
|
13107
|
+
const key = JSON.stringify([entry.timestamp, model, delta]);
|
|
13108
|
+
if (entry.timestamp && seenStandalone.has(key)) continue;
|
|
13109
|
+
if (entry.timestamp) seenStandalone.add(key);
|
|
13110
|
+
previous = addTokens(previous, delta);
|
|
13111
|
+
}
|
|
13112
|
+
if (delta.input === 0 && delta.output === 0) continue;
|
|
13113
|
+
const ts = timestamp(entry.timestamp) || result.sessionStart;
|
|
13114
|
+
if (!ts) continue;
|
|
13115
|
+
const cached = Math.min(delta.input, delta.cached);
|
|
13116
|
+
const written = Math.min(delta.input - cached, delta.cacheWrite);
|
|
13117
|
+
result.events.push({
|
|
13118
|
+
timestamp: ts,
|
|
13119
|
+
date: ts.slice(0, 10),
|
|
13120
|
+
model: normalizeModel(model),
|
|
13121
|
+
workingDir: result.workingDir,
|
|
13122
|
+
runId: result.runId,
|
|
13123
|
+
costUSD: codexSessionCost(model, delta, {
|
|
13124
|
+
inputTokens: last?.input ?? delta.input,
|
|
13125
|
+
serviceTier: typeof info.service_tier === "string" ? info.service_tier : serviceTier
|
|
13126
|
+
}),
|
|
13127
|
+
inputTokens: delta.input - cached - written,
|
|
13128
|
+
outputTokens: delta.output,
|
|
13129
|
+
cacheReadTokens: cached,
|
|
13130
|
+
cacheWriteTokens: written
|
|
13131
|
+
});
|
|
13132
|
+
}
|
|
13133
|
+
result.legacyModels = [...legacyModels.size ? legacyModels : ["gpt-5"]];
|
|
13134
|
+
return result;
|
|
13135
|
+
}
|
|
13136
|
+
function codexUsageInWindow(usage2, start, end) {
|
|
13137
|
+
return usage2.events.filter(
|
|
13138
|
+
(e) => (!start || Date.parse(e.timestamp) >= start.getTime()) && (!end || Date.parse(e.timestamp) <= end.getTime())
|
|
13139
|
+
);
|
|
13140
|
+
}
|
|
13141
|
+
function parseCodexSession(lines) {
|
|
13142
|
+
const parsed = parseCodexUsage(lines);
|
|
13143
|
+
if (!parsed.events.length) return [];
|
|
13144
|
+
const rows = /* @__PURE__ */ new Map();
|
|
13145
|
+
if (parsed.sessionStart) {
|
|
13146
|
+
for (const model of parsed.legacyModels) {
|
|
13147
|
+
rows.set(`${parsed.sessionStart.slice(0, 10)}::${model}`, {
|
|
13148
|
+
date: parsed.sessionStart.slice(0, 10),
|
|
13149
|
+
model,
|
|
13150
|
+
workingDir: parsed.workingDir,
|
|
13151
|
+
runId: parsed.runId,
|
|
13152
|
+
costUSD: 0,
|
|
13153
|
+
inputTokens: 0,
|
|
13154
|
+
outputTokens: 0,
|
|
13155
|
+
cacheReadTokens: 0,
|
|
13156
|
+
cacheWriteTokens: 0
|
|
13157
|
+
});
|
|
12933
13158
|
}
|
|
12934
13159
|
}
|
|
12935
|
-
|
|
12936
|
-
|
|
12937
|
-
|
|
12938
|
-
|
|
12939
|
-
|
|
12940
|
-
|
|
12941
|
-
|
|
12942
|
-
|
|
12943
|
-
|
|
12944
|
-
|
|
12945
|
-
|
|
12946
|
-
|
|
12947
|
-
|
|
12948
|
-
|
|
12949
|
-
|
|
12950
|
-
|
|
13160
|
+
for (const event of parsed.events) {
|
|
13161
|
+
const e = {
|
|
13162
|
+
date: event.date,
|
|
13163
|
+
model: event.model,
|
|
13164
|
+
workingDir: event.workingDir,
|
|
13165
|
+
runId: event.runId,
|
|
13166
|
+
costUSD: event.costUSD,
|
|
13167
|
+
inputTokens: event.inputTokens,
|
|
13168
|
+
outputTokens: event.outputTokens,
|
|
13169
|
+
cacheReadTokens: event.cacheReadTokens,
|
|
13170
|
+
cacheWriteTokens: event.cacheWriteTokens
|
|
13171
|
+
};
|
|
13172
|
+
const key = `${e.date}::${e.model}`;
|
|
13173
|
+
const prev = rows.get(key);
|
|
13174
|
+
if (!prev) rows.set(key, { ...e });
|
|
13175
|
+
else {
|
|
13176
|
+
prev.costUSD += e.costUSD;
|
|
13177
|
+
prev.inputTokens += e.inputTokens;
|
|
13178
|
+
prev.outputTokens += e.outputTokens;
|
|
13179
|
+
prev.cacheReadTokens += e.cacheReadTokens;
|
|
13180
|
+
prev.cacheWriteTokens += e.cacheWriteTokens;
|
|
13181
|
+
}
|
|
13182
|
+
}
|
|
13183
|
+
return [...rows.values()];
|
|
12951
13184
|
}
|
|
12952
13185
|
var CODEX_FALLBACK, codexSource;
|
|
12953
13186
|
var init_cost_codex = __esm({
|
|
@@ -12957,43 +13190,17 @@ var init_cost_codex = __esm({
|
|
|
12957
13190
|
CODEX_FALLBACK = [5e-6, 15e-6, 0, 25e-7];
|
|
12958
13191
|
codexSource = {
|
|
12959
13192
|
id: "codex",
|
|
12960
|
-
available()
|
|
12961
|
-
try {
|
|
12962
|
-
return fs21.existsSync(codexSessionsDir());
|
|
12963
|
-
} catch {
|
|
12964
|
-
return false;
|
|
12965
|
-
}
|
|
12966
|
-
},
|
|
13193
|
+
available: () => fs21.existsSync(codexSessionsDir()) || fs21.existsSync(path23.join(path23.dirname(codexSessionsDir()), "archived_sessions")),
|
|
12967
13194
|
collect(sinceMs) {
|
|
12968
|
-
const
|
|
12969
|
-
const
|
|
12970
|
-
for (const file of listCodexSessionFiles(base)) {
|
|
13195
|
+
const entries = [];
|
|
13196
|
+
for (const file of listCodexSessionFiles()) {
|
|
12971
13197
|
try {
|
|
12972
13198
|
if (sinceMs !== void 0 && fs21.statSync(file).mtimeMs < sinceMs) continue;
|
|
13199
|
+
entries.push(...parseCodexSession(fs21.readFileSync(file, "utf8").split("\n")));
|
|
12973
13200
|
} catch {
|
|
12974
|
-
continue;
|
|
12975
|
-
}
|
|
12976
|
-
let content;
|
|
12977
|
-
try {
|
|
12978
|
-
content = fs21.readFileSync(file, "utf8");
|
|
12979
|
-
} catch {
|
|
12980
|
-
continue;
|
|
12981
|
-
}
|
|
12982
|
-
const e = parseCodexSession(content.split("\n"));
|
|
12983
|
-
if (!e) continue;
|
|
12984
|
-
const key = `${e.date}::${e.model}::${e.workingDir ?? ""}::${e.runId ?? ""}`;
|
|
12985
|
-
const prev = combined.get(key);
|
|
12986
|
-
if (prev) {
|
|
12987
|
-
prev.costUSD += e.costUSD;
|
|
12988
|
-
prev.inputTokens += e.inputTokens;
|
|
12989
|
-
prev.outputTokens += e.outputTokens;
|
|
12990
|
-
prev.cacheReadTokens += e.cacheReadTokens;
|
|
12991
|
-
prev.cacheWriteTokens += e.cacheWriteTokens;
|
|
12992
|
-
} else {
|
|
12993
|
-
combined.set(key, { ...e });
|
|
12994
13201
|
}
|
|
12995
13202
|
}
|
|
12996
|
-
return
|
|
13203
|
+
return entries;
|
|
12997
13204
|
}
|
|
12998
13205
|
};
|
|
12999
13206
|
}
|
|
@@ -13006,7 +13213,7 @@ import path24 from "path";
|
|
|
13006
13213
|
function copilotSessionsDir() {
|
|
13007
13214
|
return path24.join(os21.homedir(), ".copilot", "session-state");
|
|
13008
13215
|
}
|
|
13009
|
-
function
|
|
13216
|
+
function safeReaddir2(dir) {
|
|
13010
13217
|
try {
|
|
13011
13218
|
return fs22.readdirSync(dir);
|
|
13012
13219
|
} catch {
|
|
@@ -13092,7 +13299,7 @@ var init_cost_copilot = __esm({
|
|
|
13092
13299
|
collect(sinceMs) {
|
|
13093
13300
|
const base = copilotSessionsDir();
|
|
13094
13301
|
const combined = /* @__PURE__ */ new Map();
|
|
13095
|
-
for (const sid of
|
|
13302
|
+
for (const sid of safeReaddir2(base)) {
|
|
13096
13303
|
const file = path24.join(base, sid, "events.jsonl");
|
|
13097
13304
|
try {
|
|
13098
13305
|
if (sinceMs !== void 0 && fs22.statSync(file).mtimeMs < sinceMs) continue;
|
|
@@ -13707,8 +13914,8 @@ function originForRule(ruleName, sections, enabled) {
|
|
|
13707
13914
|
}
|
|
13708
13915
|
return "";
|
|
13709
13916
|
}
|
|
13710
|
-
function relativeDate(
|
|
13711
|
-
const t = new Date(
|
|
13917
|
+
function relativeDate(timestamp2, now = /* @__PURE__ */ new Date()) {
|
|
13918
|
+
const t = new Date(timestamp2).getTime();
|
|
13712
13919
|
if (Number.isNaN(t)) return "?";
|
|
13713
13920
|
const days = Math.floor((now.getTime() - t) / 864e5);
|
|
13714
13921
|
if (days < 1) return "today";
|
|
@@ -13819,7 +14026,7 @@ function readPreviousScan(opts = {}) {
|
|
|
13819
14026
|
return null;
|
|
13820
14027
|
}
|
|
13821
14028
|
}
|
|
13822
|
-
function appendScanHistory(
|
|
14029
|
+
function appendScanHistory(record2, opts = {}) {
|
|
13823
14030
|
const filePath = opts.path ?? defaultHistoryPath();
|
|
13824
14031
|
const cap = opts.cap ?? SCAN_HISTORY_CAP;
|
|
13825
14032
|
try {
|
|
@@ -13834,7 +14041,7 @@ function appendScanHistory(record, opts = {}) {
|
|
|
13834
14041
|
} catch {
|
|
13835
14042
|
}
|
|
13836
14043
|
}
|
|
13837
|
-
history.push(
|
|
14044
|
+
history.push(record2);
|
|
13838
14045
|
if (history.length > cap) {
|
|
13839
14046
|
history = history.slice(history.length - cap);
|
|
13840
14047
|
}
|
|
@@ -13895,17 +14102,17 @@ function parseJSONLFile(filePath, fallbackWorkingDir) {
|
|
|
13895
14102
|
if (row["type"] !== "assistant") continue;
|
|
13896
14103
|
const msg = row["message"];
|
|
13897
14104
|
if (!msg?.["usage"] || typeof msg["model"] !== "string") continue;
|
|
13898
|
-
const
|
|
14105
|
+
const usage2 = msg["usage"];
|
|
13899
14106
|
const model = msg["model"];
|
|
13900
|
-
const
|
|
13901
|
-
if (typeof
|
|
13902
|
-
const date =
|
|
14107
|
+
const timestamp2 = row["timestamp"];
|
|
14108
|
+
if (typeof timestamp2 !== "string" || timestamp2.length < 10) continue;
|
|
14109
|
+
const date = timestamp2.slice(0, 10);
|
|
13903
14110
|
const p = pricingFor(model);
|
|
13904
14111
|
if (!p) continue;
|
|
13905
|
-
const inp = Number(
|
|
13906
|
-
const out = Number(
|
|
13907
|
-
const cw = Number(
|
|
13908
|
-
const cr = Number(
|
|
14112
|
+
const inp = Number(usage2["input_tokens"] ?? 0);
|
|
14113
|
+
const out = Number(usage2["output_tokens"] ?? 0);
|
|
14114
|
+
const cw = Number(usage2["cache_creation_input_tokens"] ?? 0);
|
|
14115
|
+
const cr = Number(usage2["cache_read_input_tokens"] ?? 0);
|
|
13909
14116
|
const cost = inp * p[0] + out * p[1] + cw * p[2] + cr * p[3];
|
|
13910
14117
|
const rowCwd = typeof row["cwd"] === "string" ? row["cwd"] : null;
|
|
13911
14118
|
const workingDir = rowCwd && rowCwd.startsWith("/") ? rowCwd : fallbackWorkingDir;
|
|
@@ -14935,7 +15142,7 @@ function safeCanaryScanValues() {
|
|
|
14935
15142
|
return [];
|
|
14936
15143
|
}
|
|
14937
15144
|
}
|
|
14938
|
-
function recordCanaries(scanned, toolName,
|
|
15145
|
+
function recordCanaries(scanned, toolName, timestamp2, projLabel, sessionId, agent, result, dedup, values) {
|
|
14939
15146
|
if (values.length === 0) return [];
|
|
14940
15147
|
let pool = [...values];
|
|
14941
15148
|
const matched = [];
|
|
@@ -14949,8 +15156,8 @@ function recordCanaries(scanned, toolName, timestamp, projLabel, sessionId, agen
|
|
|
14949
15156
|
const existing = dedup.canaryIndex.get(key);
|
|
14950
15157
|
if (existing) {
|
|
14951
15158
|
existing.count++;
|
|
14952
|
-
if (
|
|
14953
|
-
existing.timestamp =
|
|
15159
|
+
if (timestamp2 && (!existing.timestamp || timestamp2 < existing.timestamp)) {
|
|
15160
|
+
existing.timestamp = timestamp2;
|
|
14954
15161
|
}
|
|
14955
15162
|
continue;
|
|
14956
15163
|
}
|
|
@@ -14963,7 +15170,7 @@ function recordCanaries(scanned, toolName, timestamp, projLabel, sessionId, agen
|
|
|
14963
15170
|
view: hit.view,
|
|
14964
15171
|
retired: hit.retired,
|
|
14965
15172
|
toolName,
|
|
14966
|
-
timestamp,
|
|
15173
|
+
timestamp: timestamp2,
|
|
14967
15174
|
project: projLabel,
|
|
14968
15175
|
sessionId,
|
|
14969
15176
|
agent,
|
|
@@ -14995,7 +15202,7 @@ function scrubDecoys(subject, values) {
|
|
|
14995
15202
|
};
|
|
14996
15203
|
return walk(subject, 0);
|
|
14997
15204
|
}
|
|
14998
|
-
function pushFsOpAstFinding(command, toolName, input,
|
|
15205
|
+
function pushFsOpAstFinding(command, toolName, input, timestamp2, projLabel, sessionId, agent, result, dedup) {
|
|
14999
15206
|
const fsVerdict = analyzeFsOperation(command);
|
|
15000
15207
|
if (!fsVerdict) return false;
|
|
15001
15208
|
const synthRule = {
|
|
@@ -15025,7 +15232,7 @@ function pushFsOpAstFinding(command, toolName, input, timestamp, projLabel, sess
|
|
|
15025
15232
|
source: synthSource,
|
|
15026
15233
|
toolName,
|
|
15027
15234
|
input,
|
|
15028
|
-
timestamp,
|
|
15235
|
+
timestamp: timestamp2,
|
|
15029
15236
|
project: projLabel,
|
|
15030
15237
|
sessionId,
|
|
15031
15238
|
agent
|
|
@@ -15033,9 +15240,9 @@ function pushFsOpAstFinding(command, toolName, input, timestamp, projLabel, sess
|
|
|
15033
15240
|
}
|
|
15034
15241
|
return true;
|
|
15035
15242
|
}
|
|
15036
|
-
function isStaleFinding(
|
|
15037
|
-
if (!
|
|
15038
|
-
const t = Date.parse(
|
|
15243
|
+
function isStaleFinding(timestamp2, now = Date.now()) {
|
|
15244
|
+
if (!timestamp2) return false;
|
|
15245
|
+
const t = Date.parse(timestamp2);
|
|
15039
15246
|
if (Number.isNaN(t)) return false;
|
|
15040
15247
|
const ageDays = (now - t) / 864e5;
|
|
15041
15248
|
return ageDays > STALE_AGE_DAYS;
|
|
@@ -15183,37 +15390,7 @@ function countScanFiles() {
|
|
|
15183
15390
|
} catch {
|
|
15184
15391
|
}
|
|
15185
15392
|
}
|
|
15186
|
-
|
|
15187
|
-
if (fs29.existsSync(codexDir)) {
|
|
15188
|
-
try {
|
|
15189
|
-
for (const year of fs29.readdirSync(codexDir)) {
|
|
15190
|
-
const yp = path31.join(codexDir, year);
|
|
15191
|
-
try {
|
|
15192
|
-
if (!fs29.statSync(yp).isDirectory()) continue;
|
|
15193
|
-
for (const month of fs29.readdirSync(yp)) {
|
|
15194
|
-
const mp = path31.join(yp, month);
|
|
15195
|
-
try {
|
|
15196
|
-
if (!fs29.statSync(mp).isDirectory()) continue;
|
|
15197
|
-
for (const day of fs29.readdirSync(mp)) {
|
|
15198
|
-
const dp = path31.join(mp, day);
|
|
15199
|
-
try {
|
|
15200
|
-
if (!fs29.statSync(dp).isDirectory()) continue;
|
|
15201
|
-
total += listSessionFiles(dp).length;
|
|
15202
|
-
} catch {
|
|
15203
|
-
continue;
|
|
15204
|
-
}
|
|
15205
|
-
}
|
|
15206
|
-
} catch {
|
|
15207
|
-
continue;
|
|
15208
|
-
}
|
|
15209
|
-
}
|
|
15210
|
-
} catch {
|
|
15211
|
-
continue;
|
|
15212
|
-
}
|
|
15213
|
-
}
|
|
15214
|
-
} catch {
|
|
15215
|
-
}
|
|
15216
|
-
}
|
|
15393
|
+
total += listCodexSessionFiles().length;
|
|
15217
15394
|
return total;
|
|
15218
15395
|
}
|
|
15219
15396
|
function renderProgressBar(done, total, lines) {
|
|
@@ -15336,12 +15513,12 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
|
|
|
15336
15513
|
}
|
|
15337
15514
|
continue;
|
|
15338
15515
|
}
|
|
15339
|
-
const
|
|
15516
|
+
const usage2 = entry.message?.usage;
|
|
15340
15517
|
const model = entry.message?.model;
|
|
15341
|
-
if (
|
|
15518
|
+
if (usage2 && model) {
|
|
15342
15519
|
const p = claudeModelPrice(model);
|
|
15343
15520
|
if (p) {
|
|
15344
|
-
const rowCost = (
|
|
15521
|
+
const rowCost = (usage2.input_tokens ?? 0) * p.i + (usage2.output_tokens ?? 0) * p.o + (usage2.cache_creation_input_tokens ?? 0) * p.cw + (usage2.cache_read_input_tokens ?? 0) * p.cr;
|
|
15345
15522
|
result.totalCostUSD += rowCost;
|
|
15346
15523
|
session.costUSD += rowCost;
|
|
15347
15524
|
}
|
|
@@ -15875,15 +16052,15 @@ function scanAntigravityHistory(startDate, onProgress, onLine) {
|
|
|
15875
16052
|
} catch {
|
|
15876
16053
|
continue;
|
|
15877
16054
|
}
|
|
15878
|
-
const
|
|
15879
|
-
if (startDate &&
|
|
16055
|
+
const timestamp2 = step.created_at ?? "";
|
|
16056
|
+
if (startDate && timestamp2 && new Date(timestamp2) < startDate) continue;
|
|
15880
16057
|
if (step.type === "USER_INPUT") {
|
|
15881
16058
|
const text = typeof step.content === "string" ? step.content : "";
|
|
15882
16059
|
if (text) {
|
|
15883
16060
|
const decoysHere5 = recordCanaries(
|
|
15884
16061
|
{ text },
|
|
15885
16062
|
"user-prompt",
|
|
15886
|
-
|
|
16063
|
+
timestamp2,
|
|
15887
16064
|
projLabel,
|
|
15888
16065
|
sessionId,
|
|
15889
16066
|
"antigravity",
|
|
@@ -15900,7 +16077,7 @@ function scanAntigravityHistory(startDate, onProgress, onLine) {
|
|
|
15900
16077
|
patternName: dlpMatch.patternName,
|
|
15901
16078
|
redactedSample: dlpMatch.redactedSample,
|
|
15902
16079
|
toolName: "user-prompt",
|
|
15903
|
-
timestamp,
|
|
16080
|
+
timestamp: timestamp2,
|
|
15904
16081
|
project: projLabel,
|
|
15905
16082
|
sessionId,
|
|
15906
16083
|
agent: "antigravity"
|
|
@@ -15911,16 +16088,16 @@ function scanAntigravityHistory(startDate, onProgress, onLine) {
|
|
|
15911
16088
|
continue;
|
|
15912
16089
|
}
|
|
15913
16090
|
if (!Array.isArray(step.tool_calls) || step.tool_calls.length === 0) continue;
|
|
15914
|
-
if (
|
|
15915
|
-
if (!result.firstDate ||
|
|
15916
|
-
if (!result.lastDate ||
|
|
16091
|
+
if (timestamp2) {
|
|
16092
|
+
if (!result.firstDate || timestamp2 < result.firstDate) result.firstDate = timestamp2;
|
|
16093
|
+
if (!result.lastDate || timestamp2 > result.lastDate) result.lastDate = timestamp2;
|
|
15917
16094
|
}
|
|
15918
16095
|
for (const tc of step.tool_calls) {
|
|
15919
16096
|
result.totalToolCalls++;
|
|
15920
16097
|
const toolName = tc.name ?? "";
|
|
15921
16098
|
const toolNameLower = toolName.toLowerCase();
|
|
15922
16099
|
const input = canonicalToolInput(toolName, tc.args ?? {});
|
|
15923
|
-
sessionCalls.push({ toolName, input, timestamp });
|
|
16100
|
+
sessionCalls.push({ toolName, input, timestamp: timestamp2 });
|
|
15924
16101
|
const isShellTool = toolNameLower === "run_command";
|
|
15925
16102
|
if (isShellTool) {
|
|
15926
16103
|
result.bashCalls++;
|
|
@@ -15935,7 +16112,7 @@ function scanAntigravityHistory(startDate, onProgress, onLine) {
|
|
|
15935
16112
|
const decoysHere6 = recordCanaries(
|
|
15936
16113
|
input,
|
|
15937
16114
|
toolName,
|
|
15938
|
-
|
|
16115
|
+
timestamp2,
|
|
15939
16116
|
projLabel,
|
|
15940
16117
|
sessionId,
|
|
15941
16118
|
"antigravity",
|
|
@@ -15952,7 +16129,7 @@ function scanAntigravityHistory(startDate, onProgress, onLine) {
|
|
|
15952
16129
|
patternName: dlpMatch.patternName,
|
|
15953
16130
|
redactedSample: dlpMatch.redactedSample,
|
|
15954
16131
|
toolName,
|
|
15955
|
-
timestamp,
|
|
16132
|
+
timestamp: timestamp2,
|
|
15956
16133
|
project: projLabel,
|
|
15957
16134
|
sessionId,
|
|
15958
16135
|
agent: "antigravity"
|
|
@@ -15965,7 +16142,7 @@ function scanAntigravityHistory(startDate, onProgress, onLine) {
|
|
|
15965
16142
|
String(input.command ?? ""),
|
|
15966
16143
|
toolName,
|
|
15967
16144
|
input,
|
|
15968
|
-
|
|
16145
|
+
timestamp2,
|
|
15969
16146
|
projLabel,
|
|
15970
16147
|
sessionId,
|
|
15971
16148
|
"antigravity",
|
|
@@ -15989,7 +16166,7 @@ function scanAntigravityHistory(startDate, onProgress, onLine) {
|
|
|
15989
16166
|
source,
|
|
15990
16167
|
toolName,
|
|
15991
16168
|
input,
|
|
15992
|
-
timestamp,
|
|
16169
|
+
timestamp: timestamp2,
|
|
15993
16170
|
project: projLabel,
|
|
15994
16171
|
sessionId,
|
|
15995
16172
|
agent: "antigravity"
|
|
@@ -16021,7 +16198,7 @@ function scanAntigravityHistory(startDate, onProgress, onLine) {
|
|
|
16021
16198
|
},
|
|
16022
16199
|
toolName,
|
|
16023
16200
|
input,
|
|
16024
|
-
timestamp,
|
|
16201
|
+
timestamp: timestamp2,
|
|
16025
16202
|
project: projLabel,
|
|
16026
16203
|
sessionId,
|
|
16027
16204
|
agent: "antigravity"
|
|
@@ -16091,7 +16268,7 @@ function scanCopilotHistory(startDate, onProgress, onLine) {
|
|
|
16091
16268
|
} catch {
|
|
16092
16269
|
continue;
|
|
16093
16270
|
}
|
|
16094
|
-
const
|
|
16271
|
+
const timestamp2 = ev.timestamp ?? "";
|
|
16095
16272
|
if (ev.type === "session.start") {
|
|
16096
16273
|
const cwd = ev.data?.context?.cwd;
|
|
16097
16274
|
if (typeof cwd === "string" && cwd) {
|
|
@@ -16099,14 +16276,14 @@ function scanCopilotHistory(startDate, onProgress, onLine) {
|
|
|
16099
16276
|
}
|
|
16100
16277
|
continue;
|
|
16101
16278
|
}
|
|
16102
|
-
if (startDate &&
|
|
16279
|
+
if (startDate && timestamp2 && new Date(timestamp2) < startDate) continue;
|
|
16103
16280
|
if (ev.type === "user.message") {
|
|
16104
16281
|
const text = ev.data?.content ?? ev.data?.text ?? "";
|
|
16105
16282
|
if (typeof text === "string" && text) {
|
|
16106
16283
|
const decoysHere7 = recordCanaries(
|
|
16107
16284
|
{ text },
|
|
16108
16285
|
"user-prompt",
|
|
16109
|
-
|
|
16286
|
+
timestamp2,
|
|
16110
16287
|
projLabel,
|
|
16111
16288
|
sessionId,
|
|
16112
16289
|
"copilot",
|
|
@@ -16123,7 +16300,7 @@ function scanCopilotHistory(startDate, onProgress, onLine) {
|
|
|
16123
16300
|
patternName: dlpMatch2.patternName,
|
|
16124
16301
|
redactedSample: dlpMatch2.redactedSample,
|
|
16125
16302
|
toolName: "user-prompt",
|
|
16126
|
-
timestamp,
|
|
16303
|
+
timestamp: timestamp2,
|
|
16127
16304
|
project: projLabel,
|
|
16128
16305
|
sessionId,
|
|
16129
16306
|
agent: "copilot"
|
|
@@ -16138,19 +16315,19 @@ function scanCopilotHistory(startDate, onProgress, onLine) {
|
|
|
16138
16315
|
const toolNameLower = toolName.toLowerCase();
|
|
16139
16316
|
const input = ev.data?.arguments ?? {};
|
|
16140
16317
|
result.totalToolCalls++;
|
|
16141
|
-
sessionCalls.push({ toolName, input, timestamp });
|
|
16318
|
+
sessionCalls.push({ toolName, input, timestamp: timestamp2 });
|
|
16142
16319
|
const isShellTool = isShellShapedTool(toolNameLower, toolInspectionMap);
|
|
16143
16320
|
if (isShellTool) result.bashCalls++;
|
|
16144
|
-
if (
|
|
16145
|
-
if (!result.firstDate ||
|
|
16146
|
-
if (!result.lastDate ||
|
|
16321
|
+
if (timestamp2) {
|
|
16322
|
+
if (!result.firstDate || timestamp2 < result.firstDate) result.firstDate = timestamp2;
|
|
16323
|
+
if (!result.lastDate || timestamp2 > result.lastDate) result.lastDate = timestamp2;
|
|
16147
16324
|
}
|
|
16148
16325
|
const rawCmd = String(input.command ?? "").trimStart();
|
|
16149
16326
|
if (/^node9\s+(scan|explain|report|tail|dlp|status|sessions|audit)\b/.test(rawCmd)) continue;
|
|
16150
16327
|
const decoysHere8 = recordCanaries(
|
|
16151
16328
|
input,
|
|
16152
16329
|
toolName,
|
|
16153
|
-
|
|
16330
|
+
timestamp2,
|
|
16154
16331
|
projLabel,
|
|
16155
16332
|
sessionId,
|
|
16156
16333
|
"copilot",
|
|
@@ -16167,7 +16344,7 @@ function scanCopilotHistory(startDate, onProgress, onLine) {
|
|
|
16167
16344
|
patternName: dlpMatch.patternName,
|
|
16168
16345
|
redactedSample: dlpMatch.redactedSample,
|
|
16169
16346
|
toolName,
|
|
16170
|
-
timestamp,
|
|
16347
|
+
timestamp: timestamp2,
|
|
16171
16348
|
project: projLabel,
|
|
16172
16349
|
sessionId,
|
|
16173
16350
|
agent: "copilot"
|
|
@@ -16180,7 +16357,7 @@ function scanCopilotHistory(startDate, onProgress, onLine) {
|
|
|
16180
16357
|
String(input.command ?? ""),
|
|
16181
16358
|
toolName,
|
|
16182
16359
|
input,
|
|
16183
|
-
|
|
16360
|
+
timestamp2,
|
|
16184
16361
|
projLabel,
|
|
16185
16362
|
sessionId,
|
|
16186
16363
|
"copilot",
|
|
@@ -16203,7 +16380,7 @@ function scanCopilotHistory(startDate, onProgress, onLine) {
|
|
|
16203
16380
|
source,
|
|
16204
16381
|
toolName,
|
|
16205
16382
|
input,
|
|
16206
|
-
timestamp,
|
|
16383
|
+
timestamp: timestamp2,
|
|
16207
16384
|
project: projLabel,
|
|
16208
16385
|
sessionId,
|
|
16209
16386
|
agent: "copilot"
|
|
@@ -16235,7 +16412,7 @@ function scanCopilotHistory(startDate, onProgress, onLine) {
|
|
|
16235
16412
|
},
|
|
16236
16413
|
toolName,
|
|
16237
16414
|
input,
|
|
16238
|
-
timestamp,
|
|
16415
|
+
timestamp: timestamp2,
|
|
16239
16416
|
project: projLabel,
|
|
16240
16417
|
sessionId,
|
|
16241
16418
|
agent: "copilot"
|
|
@@ -16250,7 +16427,6 @@ function scanCopilotHistory(startDate, onProgress, onLine) {
|
|
|
16250
16427
|
}
|
|
16251
16428
|
function scanCodexHistory(startDate, onProgress, onLine) {
|
|
16252
16429
|
const canaryVals = safeCanaryScanValues();
|
|
16253
|
-
const sessionsBase = path31.join(os27.homedir(), ".codex", "sessions");
|
|
16254
16430
|
const result = {
|
|
16255
16431
|
filesScanned: 0,
|
|
16256
16432
|
sessions: 0,
|
|
@@ -16267,39 +16443,7 @@ function scanCodexHistory(startDate, onProgress, onLine) {
|
|
|
16267
16443
|
perSession: []
|
|
16268
16444
|
};
|
|
16269
16445
|
const dedup = emptyScanDedup();
|
|
16270
|
-
|
|
16271
|
-
const jsonlFiles = [];
|
|
16272
|
-
try {
|
|
16273
|
-
for (const year of fs29.readdirSync(sessionsBase)) {
|
|
16274
|
-
const yearPath = path31.join(sessionsBase, year);
|
|
16275
|
-
try {
|
|
16276
|
-
if (!fs29.statSync(yearPath).isDirectory()) continue;
|
|
16277
|
-
} catch {
|
|
16278
|
-
continue;
|
|
16279
|
-
}
|
|
16280
|
-
for (const month of fs29.readdirSync(yearPath)) {
|
|
16281
|
-
const monthPath = path31.join(yearPath, month);
|
|
16282
|
-
try {
|
|
16283
|
-
if (!fs29.statSync(monthPath).isDirectory()) continue;
|
|
16284
|
-
} catch {
|
|
16285
|
-
continue;
|
|
16286
|
-
}
|
|
16287
|
-
for (const day of fs29.readdirSync(monthPath)) {
|
|
16288
|
-
const dayPath = path31.join(monthPath, day);
|
|
16289
|
-
try {
|
|
16290
|
-
if (!fs29.statSync(dayPath).isDirectory()) continue;
|
|
16291
|
-
} catch {
|
|
16292
|
-
continue;
|
|
16293
|
-
}
|
|
16294
|
-
for (const file of fs29.readdirSync(dayPath)) {
|
|
16295
|
-
if (file.endsWith(".jsonl")) jsonlFiles.push(path31.join(dayPath, file));
|
|
16296
|
-
}
|
|
16297
|
-
}
|
|
16298
|
-
}
|
|
16299
|
-
}
|
|
16300
|
-
} catch {
|
|
16301
|
-
return result;
|
|
16302
|
-
}
|
|
16446
|
+
const jsonlFiles = listCodexSessionFiles();
|
|
16303
16447
|
const ruleSources = buildRuleSources();
|
|
16304
16448
|
for (const filePath of jsonlFiles) {
|
|
16305
16449
|
result.filesScanned++;
|
|
@@ -16315,10 +16459,6 @@ function scanCodexHistory(startDate, onProgress, onLine) {
|
|
|
16315
16459
|
let projLabel = "";
|
|
16316
16460
|
result.sessions++;
|
|
16317
16461
|
const sessionCalls = [];
|
|
16318
|
-
let lastTotalInput = 0;
|
|
16319
|
-
let lastTotalCached = 0;
|
|
16320
|
-
let lastTotalOutput = 0;
|
|
16321
|
-
let model = "";
|
|
16322
16462
|
for (const line of lines) {
|
|
16323
16463
|
if (!line.trim()) continue;
|
|
16324
16464
|
onLine?.();
|
|
@@ -16336,18 +16476,6 @@ function scanCodexHistory(startDate, onProgress, onLine) {
|
|
|
16336
16476
|
projLabel = stripTerminalEscapes(cwd.replace(os27.homedir(), "~")).slice(0, 40);
|
|
16337
16477
|
continue;
|
|
16338
16478
|
}
|
|
16339
|
-
if (entry.type === "turn_context" && typeof payload["model"] === "string") {
|
|
16340
|
-
model = payload["model"];
|
|
16341
|
-
continue;
|
|
16342
|
-
}
|
|
16343
|
-
if (entry.type === "event_msg" && payload["type"] === "token_count") {
|
|
16344
|
-
const info = payload["info"];
|
|
16345
|
-
const usage = info?.["total_token_usage"] ?? {};
|
|
16346
|
-
lastTotalInput = usage["input_tokens"] ?? lastTotalInput;
|
|
16347
|
-
lastTotalCached = usage["cached_input_tokens"] ?? lastTotalCached;
|
|
16348
|
-
lastTotalOutput = usage["output_tokens"] ?? lastTotalOutput;
|
|
16349
|
-
continue;
|
|
16350
|
-
}
|
|
16351
16479
|
if (entry.type === "event_msg" && payload["type"] === "user_message") {
|
|
16352
16480
|
const text = String(payload["message"] ?? "");
|
|
16353
16481
|
if (text) {
|
|
@@ -16504,13 +16632,8 @@ function scanCodexHistory(startDate, onProgress, onLine) {
|
|
|
16504
16632
|
}
|
|
16505
16633
|
}
|
|
16506
16634
|
}
|
|
16507
|
-
const
|
|
16508
|
-
|
|
16509
|
-
result.totalCostUSD += codexSessionCost(model, {
|
|
16510
|
-
input: lastTotalInput,
|
|
16511
|
-
cached: lastTotalCached,
|
|
16512
|
-
output: lastTotalOutput
|
|
16513
|
-
});
|
|
16635
|
+
for (const event of codexUsageInWindow(parseCodexUsage(lines), startDate)) {
|
|
16636
|
+
result.totalCostUSD += event.costUSD;
|
|
16514
16637
|
}
|
|
16515
16638
|
result.loopFindings.push(...detectLoops(sessionCalls, projLabel, sessionId, "codex"));
|
|
16516
16639
|
}
|
|
@@ -17942,13 +18065,13 @@ var init_taint_store = __esm({
|
|
|
17942
18065
|
*/
|
|
17943
18066
|
check(filePath) {
|
|
17944
18067
|
const resolved = this._resolve(filePath);
|
|
17945
|
-
const
|
|
17946
|
-
if (!
|
|
17947
|
-
if (Date.now() >
|
|
18068
|
+
const record2 = this.records.get(resolved);
|
|
18069
|
+
if (!record2) return null;
|
|
18070
|
+
if (Date.now() > record2.expiresAt) {
|
|
17948
18071
|
this.records.delete(resolved);
|
|
17949
18072
|
return null;
|
|
17950
18073
|
}
|
|
17951
|
-
return
|
|
18074
|
+
return record2;
|
|
17952
18075
|
}
|
|
17953
18076
|
/**
|
|
17954
18077
|
* Propagate taint from sourcePath to destPath (e.g. cp, mv).
|
|
@@ -17969,8 +18092,8 @@ var init_taint_store = __esm({
|
|
|
17969
18092
|
/** Remove all expired records. Called periodically by the daemon. */
|
|
17970
18093
|
prune() {
|
|
17971
18094
|
const now = Date.now();
|
|
17972
|
-
for (const [key,
|
|
17973
|
-
if (now >
|
|
18095
|
+
for (const [key, record2] of this.records) {
|
|
18096
|
+
if (now > record2.expiresAt) this.records.delete(key);
|
|
17974
18097
|
}
|
|
17975
18098
|
}
|
|
17976
18099
|
/** Return all non-expired taint records (for audit/debug). */
|
|
@@ -18009,13 +18132,13 @@ var init_taint_store = __esm({
|
|
|
18009
18132
|
* Expired records are pruned on access. */
|
|
18010
18133
|
check(sessionId) {
|
|
18011
18134
|
if (!sessionId) return null;
|
|
18012
|
-
const
|
|
18013
|
-
if (!
|
|
18014
|
-
if (Date.now() >
|
|
18135
|
+
const record2 = this.records.get(sessionId);
|
|
18136
|
+
if (!record2) return null;
|
|
18137
|
+
if (Date.now() > record2.expiresAt) {
|
|
18015
18138
|
this.records.delete(sessionId);
|
|
18016
18139
|
return null;
|
|
18017
18140
|
}
|
|
18018
|
-
return
|
|
18141
|
+
return record2;
|
|
18019
18142
|
}
|
|
18020
18143
|
/** Clear a session's taint (e.g. the user resolved it). Returns true if a
|
|
18021
18144
|
* record was actually removed (false if the session wasn't tainted). */
|
|
@@ -18030,8 +18153,8 @@ var init_taint_store = __esm({
|
|
|
18030
18153
|
/** Remove all expired records. Called periodically by the daemon. */
|
|
18031
18154
|
prune() {
|
|
18032
18155
|
const now = Date.now();
|
|
18033
|
-
for (const [key,
|
|
18034
|
-
if (now >
|
|
18156
|
+
for (const [key, record2] of this.records) {
|
|
18157
|
+
if (now > record2.expiresAt) this.records.delete(key);
|
|
18035
18158
|
}
|
|
18036
18159
|
}
|
|
18037
18160
|
/** Remove all records. Used by tests to reset state between runs. */
|
|
@@ -20604,13 +20727,36 @@ function isPolicyStale(nowMs = Date.now(), health) {
|
|
|
20604
20727
|
if (Number.isNaN(last)) return false;
|
|
20605
20728
|
return nowMs - last > stalenessThresholdMs(effectiveSyncIntervalMs());
|
|
20606
20729
|
}
|
|
20607
|
-
function
|
|
20608
|
-
|
|
20730
|
+
function safeNode9Version() {
|
|
20731
|
+
let dir = __dirname;
|
|
20732
|
+
for (let up = 0; up < 5; up++) {
|
|
20733
|
+
try {
|
|
20734
|
+
const pkg = JSON.parse(fs41.readFileSync(path40.join(dir, "package.json"), "utf-8"));
|
|
20735
|
+
if (pkg.name === "@node9/proxy" || pkg.name === "node9-ai") {
|
|
20736
|
+
return pkg.version;
|
|
20737
|
+
}
|
|
20738
|
+
} catch {
|
|
20739
|
+
}
|
|
20740
|
+
const parent = path40.dirname(dir);
|
|
20741
|
+
if (parent === dir) break;
|
|
20742
|
+
dir = parent;
|
|
20743
|
+
}
|
|
20744
|
+
return void 0;
|
|
20745
|
+
}
|
|
20746
|
+
function buildPolicyPullHeaders(apiKey, ifNoneMatch, proxyVersion) {
|
|
20609
20747
|
const headers = {
|
|
20610
20748
|
Authorization: `Bearer ${apiKey}`,
|
|
20611
20749
|
"Content-Type": "application/json"
|
|
20612
20750
|
};
|
|
20613
20751
|
if (ifNoneMatch) headers["If-None-Match"] = `"${ifNoneMatch}"`;
|
|
20752
|
+
if (proxyVersion && proxyVersion !== "unknown") {
|
|
20753
|
+
headers["X-Node9-Version"] = proxyVersion;
|
|
20754
|
+
}
|
|
20755
|
+
return headers;
|
|
20756
|
+
}
|
|
20757
|
+
function fetchCloudPolicy(apiKey, apiUrl, ifNoneMatch) {
|
|
20758
|
+
const parsed = new URL(apiUrl);
|
|
20759
|
+
const headers = buildPolicyPullHeaders(apiKey, ifNoneMatch, safeNode9Version());
|
|
20614
20760
|
return new Promise((resolve2, reject) => {
|
|
20615
20761
|
const req = https4.request(
|
|
20616
20762
|
{
|
|
@@ -22742,10 +22888,10 @@ data: ${JSON.stringify(item.data)}
|
|
|
22742
22888
|
return res.end(JSON.stringify({ error: "all paths must be strings" }));
|
|
22743
22889
|
}
|
|
22744
22890
|
for (const p of body.paths) {
|
|
22745
|
-
const
|
|
22746
|
-
if (
|
|
22891
|
+
const record2 = taintStore.check(p);
|
|
22892
|
+
if (record2) {
|
|
22747
22893
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
22748
|
-
return res.end(JSON.stringify({ tainted: true, record }));
|
|
22894
|
+
return res.end(JSON.stringify({ tainted: true, record: record2 }));
|
|
22749
22895
|
}
|
|
22750
22896
|
}
|
|
22751
22897
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
@@ -22794,9 +22940,9 @@ data: ${JSON.stringify(item.data)}
|
|
|
22794
22940
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
22795
22941
|
return res.end(JSON.stringify({ error: "sessionId must be a string" }));
|
|
22796
22942
|
}
|
|
22797
|
-
const
|
|
22943
|
+
const record2 = sessionTaintStore.check(body.sessionId);
|
|
22798
22944
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
22799
|
-
return res.end(JSON.stringify(
|
|
22945
|
+
return res.end(JSON.stringify(record2 ? { tainted: true, record: record2 } : { tainted: false }));
|
|
22800
22946
|
} catch {
|
|
22801
22947
|
res.writeHead(400).end();
|
|
22802
22948
|
return;
|
|
@@ -28742,8 +28888,8 @@ var require_util2 = __commonJS({
|
|
|
28742
28888
|
request2.headersList.append("origin", serializedOrigin, true);
|
|
28743
28889
|
}
|
|
28744
28890
|
}
|
|
28745
|
-
function coarsenTime(
|
|
28746
|
-
return
|
|
28891
|
+
function coarsenTime(timestamp2, crossOriginIsolatedCapability) {
|
|
28892
|
+
return timestamp2;
|
|
28747
28893
|
}
|
|
28748
28894
|
function clampAndCoarsenConnectionTimingInfo(connectionTimingInfo, defaultStartTime, crossOriginIsolatedCapability) {
|
|
28749
28895
|
if (!connectionTimingInfo?.startTime || connectionTimingInfo.startTime < defaultStartTime) {
|
|
@@ -38824,20 +38970,20 @@ var require_dns = __commonJS({
|
|
|
38824
38970
|
return ip;
|
|
38825
38971
|
}
|
|
38826
38972
|
setRecords(origin, addresses) {
|
|
38827
|
-
const
|
|
38973
|
+
const timestamp2 = Date.now();
|
|
38828
38974
|
const records = { records: { 4: null, 6: null } };
|
|
38829
38975
|
let minTTL = this.#maxTTL;
|
|
38830
|
-
for (const
|
|
38831
|
-
|
|
38832
|
-
if (typeof
|
|
38833
|
-
|
|
38834
|
-
minTTL = Math.min(minTTL,
|
|
38976
|
+
for (const record2 of addresses) {
|
|
38977
|
+
record2.timestamp = timestamp2;
|
|
38978
|
+
if (typeof record2.ttl === "number") {
|
|
38979
|
+
record2.ttl = Math.min(record2.ttl, this.#maxTTL);
|
|
38980
|
+
minTTL = Math.min(minTTL, record2.ttl);
|
|
38835
38981
|
} else {
|
|
38836
|
-
|
|
38982
|
+
record2.ttl = this.#maxTTL;
|
|
38837
38983
|
}
|
|
38838
|
-
const familyRecords = records.records[
|
|
38839
|
-
familyRecords.ips.push(
|
|
38840
|
-
records.records[
|
|
38984
|
+
const familyRecords = records.records[record2.family] ?? { ips: [] };
|
|
38985
|
+
familyRecords.ips.push(record2);
|
|
38986
|
+
records.records[record2.family] = familyRecords;
|
|
38841
38987
|
}
|
|
38842
38988
|
this.storage.set(origin.hostname, records, { ttl: minTTL });
|
|
38843
38989
|
}
|
|
@@ -53501,15 +53647,15 @@ import chalk16 from "chalk";
|
|
|
53501
53647
|
import fs59 from "fs";
|
|
53502
53648
|
import path57 from "path";
|
|
53503
53649
|
import os53 from "os";
|
|
53504
|
-
function formatRelativeTime(
|
|
53505
|
-
const diff = Date.now() - new Date(
|
|
53650
|
+
function formatRelativeTime(timestamp2) {
|
|
53651
|
+
const diff = Date.now() - new Date(timestamp2).getTime();
|
|
53506
53652
|
const sec = Math.floor(diff / 1e3);
|
|
53507
53653
|
if (sec < 60) return `${sec}s ago`;
|
|
53508
53654
|
const min = Math.floor(sec / 60);
|
|
53509
53655
|
if (min < 60) return `${min}m ago`;
|
|
53510
53656
|
const hrs = Math.floor(min / 60);
|
|
53511
53657
|
if (hrs < 24) return `${hrs}h ago`;
|
|
53512
|
-
return new Date(
|
|
53658
|
+
return new Date(timestamp2).toLocaleDateString();
|
|
53513
53659
|
}
|
|
53514
53660
|
function registerAuditCommand(program2) {
|
|
53515
53661
|
program2.command("audit").description("View local execution audit log").option("--tail <n>", "Number of entries to show", "20").option("--tool <pattern>", "Filter by tool name (substring match)").option("--deny", "Show only denied actions").option("--json", "Output raw JSON").action((options) => {
|
|
@@ -53755,15 +53901,15 @@ function processClaudeCostProject(proj, projectsDir, start, end, acc) {
|
|
|
53755
53901
|
if (!entry.timestamp) continue;
|
|
53756
53902
|
const ts = new Date(entry.timestamp);
|
|
53757
53903
|
if (ts < start || ts > end) continue;
|
|
53758
|
-
const
|
|
53904
|
+
const usage2 = entry.message?.usage;
|
|
53759
53905
|
const model = entry.message?.model;
|
|
53760
|
-
if (!
|
|
53906
|
+
if (!usage2 || !model) continue;
|
|
53761
53907
|
const p = claudeModelPrice2(model);
|
|
53762
53908
|
if (!p) continue;
|
|
53763
|
-
const inp =
|
|
53764
|
-
const out =
|
|
53765
|
-
const cw =
|
|
53766
|
-
const cr =
|
|
53909
|
+
const inp = usage2.input_tokens ?? 0;
|
|
53910
|
+
const out = usage2.output_tokens ?? 0;
|
|
53911
|
+
const cw = usage2.cache_creation_input_tokens ?? 0;
|
|
53912
|
+
const cr = usage2.cache_read_input_tokens ?? 0;
|
|
53767
53913
|
const cost = inp * p.i + out * p.o + cw * p.cw + cr * p.cr;
|
|
53768
53914
|
acc.total += cost;
|
|
53769
53915
|
acc.inputTokens += inp;
|
|
@@ -53811,90 +53957,21 @@ function processCodexCostFile(filePath, start, end, acc) {
|
|
|
53811
53957
|
} catch {
|
|
53812
53958
|
return;
|
|
53813
53959
|
}
|
|
53814
|
-
|
|
53815
|
-
|
|
53816
|
-
|
|
53817
|
-
|
|
53818
|
-
|
|
53819
|
-
|
|
53960
|
+
const parsed = parseCodexUsage(lines);
|
|
53961
|
+
for (const event of codexUsageInWindow(parsed, start, end)) {
|
|
53962
|
+
acc.total += event.costUSD;
|
|
53963
|
+
acc.byDay.set(event.date, (acc.byDay.get(event.date) ?? 0) + event.costUSD);
|
|
53964
|
+
acc.byModel.set(event.model, (acc.byModel.get(event.model) ?? 0) + event.costUSD);
|
|
53965
|
+
}
|
|
53820
53966
|
for (const line of lines) {
|
|
53821
|
-
if (!line.trim()) continue;
|
|
53822
|
-
let entry;
|
|
53823
53967
|
try {
|
|
53824
|
-
entry = JSON.parse(line);
|
|
53968
|
+
const entry = JSON.parse(line);
|
|
53969
|
+
if (entry?.type !== "response_item" || entry.payload?.type !== "function_call") continue;
|
|
53970
|
+
const ts = new Date(entry.timestamp ?? parsed.sessionStart);
|
|
53971
|
+
if (ts >= start && ts <= end) acc.toolCalls++;
|
|
53825
53972
|
} catch {
|
|
53826
|
-
continue;
|
|
53827
|
-
}
|
|
53828
|
-
const p = entry.payload ?? {};
|
|
53829
|
-
if (entry.type === "session_meta") {
|
|
53830
|
-
sessionStart = String(p["timestamp"] ?? "");
|
|
53831
|
-
continue;
|
|
53832
|
-
}
|
|
53833
|
-
if (entry.type === "turn_context" && typeof p["model"] === "string") {
|
|
53834
|
-
model = p["model"];
|
|
53835
|
-
continue;
|
|
53836
|
-
}
|
|
53837
|
-
if (entry.type === "event_msg" && p["type"] === "token_count") {
|
|
53838
|
-
const info = p["info"] ?? {};
|
|
53839
|
-
const usage = info["total_token_usage"] ?? {};
|
|
53840
|
-
lastTotalInput = usage["input_tokens"] ?? lastTotalInput;
|
|
53841
|
-
lastTotalCached = usage["cached_input_tokens"] ?? lastTotalCached;
|
|
53842
|
-
lastTotalOutput = usage["output_tokens"] ?? lastTotalOutput;
|
|
53843
|
-
}
|
|
53844
|
-
if (entry.type === "response_item" && p["type"] === "function_call") {
|
|
53845
|
-
sessionToolCalls++;
|
|
53846
53973
|
}
|
|
53847
53974
|
}
|
|
53848
|
-
if (!sessionStart) return;
|
|
53849
|
-
const ts = new Date(sessionStart);
|
|
53850
|
-
if (ts < start || ts > end) return;
|
|
53851
|
-
const cost = codexSessionCost(model, {
|
|
53852
|
-
input: lastTotalInput,
|
|
53853
|
-
cached: lastTotalCached,
|
|
53854
|
-
output: lastTotalOutput
|
|
53855
|
-
});
|
|
53856
|
-
acc.total += cost;
|
|
53857
|
-
acc.toolCalls += sessionToolCalls;
|
|
53858
|
-
const dateKey = sessionStart.slice(0, 10);
|
|
53859
|
-
acc.byDay.set(dateKey, (acc.byDay.get(dateKey) ?? 0) + cost);
|
|
53860
|
-
const normModel = normalizeModel(model || "gpt-5");
|
|
53861
|
-
acc.byModel.set(normModel, (acc.byModel.get(normModel) ?? 0) + cost);
|
|
53862
|
-
}
|
|
53863
|
-
function listCodexSessionFiles2(sessionsBase) {
|
|
53864
|
-
const jsonlFiles = [];
|
|
53865
|
-
if (!fs60.existsSync(sessionsBase)) return jsonlFiles;
|
|
53866
|
-
try {
|
|
53867
|
-
for (const year of fs60.readdirSync(sessionsBase)) {
|
|
53868
|
-
const yearPath = path58.join(sessionsBase, year);
|
|
53869
|
-
try {
|
|
53870
|
-
if (!fs60.statSync(yearPath).isDirectory()) continue;
|
|
53871
|
-
} catch {
|
|
53872
|
-
continue;
|
|
53873
|
-
}
|
|
53874
|
-
for (const month of fs60.readdirSync(yearPath)) {
|
|
53875
|
-
const monthPath = path58.join(yearPath, month);
|
|
53876
|
-
try {
|
|
53877
|
-
if (!fs60.statSync(monthPath).isDirectory()) continue;
|
|
53878
|
-
} catch {
|
|
53879
|
-
continue;
|
|
53880
|
-
}
|
|
53881
|
-
for (const day of fs60.readdirSync(monthPath)) {
|
|
53882
|
-
const dayPath = path58.join(monthPath, day);
|
|
53883
|
-
try {
|
|
53884
|
-
if (!fs60.statSync(dayPath).isDirectory()) continue;
|
|
53885
|
-
} catch {
|
|
53886
|
-
continue;
|
|
53887
|
-
}
|
|
53888
|
-
for (const file of fs60.readdirSync(dayPath)) {
|
|
53889
|
-
if (file.endsWith(".jsonl")) jsonlFiles.push(path58.join(dayPath, file));
|
|
53890
|
-
}
|
|
53891
|
-
}
|
|
53892
|
-
}
|
|
53893
|
-
}
|
|
53894
|
-
} catch {
|
|
53895
|
-
return [];
|
|
53896
|
-
}
|
|
53897
|
-
return jsonlFiles;
|
|
53898
53975
|
}
|
|
53899
53976
|
function mergeByModel(...maps) {
|
|
53900
53977
|
const out = /* @__PURE__ */ new Map();
|
|
@@ -53910,7 +53987,7 @@ function loadCodexCost(start, end, sessionsBase) {
|
|
|
53910
53987
|
byDay: /* @__PURE__ */ new Map(),
|
|
53911
53988
|
byModel: /* @__PURE__ */ new Map()
|
|
53912
53989
|
};
|
|
53913
|
-
const files =
|
|
53990
|
+
const files = listCodexSessionFiles(sessionsBase);
|
|
53914
53991
|
for (const filePath of files) {
|
|
53915
53992
|
processCodexCostFile(filePath, start, end, acc);
|
|
53916
53993
|
}
|
|
@@ -54050,7 +54127,7 @@ function aggregateReportFromAudit(period, opts = {}) {
|
|
|
54050
54127
|
const now = opts.now ?? /* @__PURE__ */ new Date();
|
|
54051
54128
|
const auditLogPath = opts.auditLogPath ?? path58.join(os54.homedir(), ".node9", "audit.log");
|
|
54052
54129
|
const claudeProjectsDir = opts.claudeProjectsDir ?? path58.join(os54.homedir(), ".claude", "projects");
|
|
54053
|
-
const codexSessionsDir2 = opts.codexSessionsDir ??
|
|
54130
|
+
const codexSessionsDir2 = opts.codexSessionsDir ?? codexSessionsDir();
|
|
54054
54131
|
const geminiTmpDir2 = opts.geminiTmpDir ?? path58.join(os54.homedir(), ".gemini", "tmp");
|
|
54055
54132
|
const hasAuditFile = fs60.existsSync(auditLogPath);
|
|
54056
54133
|
const allEntries = opts.preloadedAuditEntries ?? parseAuditLog(auditLogPath);
|
|
@@ -60249,12 +60326,12 @@ function parseSessionLines(lines) {
|
|
|
60249
60326
|
continue;
|
|
60250
60327
|
}
|
|
60251
60328
|
if (entry.type !== "assistant") continue;
|
|
60252
|
-
const
|
|
60329
|
+
const usage2 = entry.message?.usage;
|
|
60253
60330
|
const model = entry.message?.model;
|
|
60254
|
-
if (
|
|
60331
|
+
if (usage2 && model) {
|
|
60255
60332
|
const p = modelPrice(model);
|
|
60256
60333
|
if (p) {
|
|
60257
|
-
costUSD += (
|
|
60334
|
+
costUSD += (usage2.input_tokens ?? 0) * p.i + (usage2.output_tokens ?? 0) * p.o + (usage2.cache_creation_input_tokens ?? 0) * p.cw + (usage2.cache_read_input_tokens ?? 0) * p.cr;
|
|
60258
60335
|
}
|
|
60259
60336
|
}
|
|
60260
60337
|
const content = entry.message?.content;
|
|
@@ -60434,46 +60511,13 @@ function buildGeminiSessions(days, allAuditEntries) {
|
|
|
60434
60511
|
return summaries;
|
|
60435
60512
|
}
|
|
60436
60513
|
function buildCodexSessions(days, allAuditEntries) {
|
|
60437
|
-
const sessionsBase = path69.join(os61.homedir(), ".codex", "sessions");
|
|
60438
|
-
if (!fs74.existsSync(sessionsBase)) return [];
|
|
60439
60514
|
const cutoff = days !== null ? (() => {
|
|
60440
60515
|
const d = /* @__PURE__ */ new Date();
|
|
60441
60516
|
d.setDate(d.getDate() - days);
|
|
60442
60517
|
d.setHours(0, 0, 0, 0);
|
|
60443
60518
|
return d;
|
|
60444
60519
|
})() : null;
|
|
60445
|
-
const jsonlFiles =
|
|
60446
|
-
try {
|
|
60447
|
-
for (const year of fs74.readdirSync(sessionsBase)) {
|
|
60448
|
-
const yearPath = path69.join(sessionsBase, year);
|
|
60449
|
-
try {
|
|
60450
|
-
if (!fs74.statSync(yearPath).isDirectory()) continue;
|
|
60451
|
-
} catch {
|
|
60452
|
-
continue;
|
|
60453
|
-
}
|
|
60454
|
-
for (const month of fs74.readdirSync(yearPath)) {
|
|
60455
|
-
const monthPath = path69.join(yearPath, month);
|
|
60456
|
-
try {
|
|
60457
|
-
if (!fs74.statSync(monthPath).isDirectory()) continue;
|
|
60458
|
-
} catch {
|
|
60459
|
-
continue;
|
|
60460
|
-
}
|
|
60461
|
-
for (const day of fs74.readdirSync(monthPath)) {
|
|
60462
|
-
const dayPath = path69.join(monthPath, day);
|
|
60463
|
-
try {
|
|
60464
|
-
if (!fs74.statSync(dayPath).isDirectory()) continue;
|
|
60465
|
-
} catch {
|
|
60466
|
-
continue;
|
|
60467
|
-
}
|
|
60468
|
-
for (const file of fs74.readdirSync(dayPath)) {
|
|
60469
|
-
if (file.endsWith(".jsonl")) jsonlFiles.push(path69.join(dayPath, file));
|
|
60470
|
-
}
|
|
60471
|
-
}
|
|
60472
|
-
}
|
|
60473
|
-
}
|
|
60474
|
-
} catch {
|
|
60475
|
-
return [];
|
|
60476
|
-
}
|
|
60520
|
+
const jsonlFiles = listCodexSessionFiles();
|
|
60477
60521
|
const summaries = [];
|
|
60478
60522
|
for (const filePath of jsonlFiles) {
|
|
60479
60523
|
let lines;
|
|
@@ -60488,10 +60532,6 @@ function buildCodexSessions(days, allAuditEntries) {
|
|
|
60488
60532
|
let firstPrompt = "";
|
|
60489
60533
|
const toolCalls = [];
|
|
60490
60534
|
let lastToolTs = "";
|
|
60491
|
-
let lastTotalInput = 0;
|
|
60492
|
-
let lastTotalCached = 0;
|
|
60493
|
-
let lastTotalOutput = 0;
|
|
60494
|
-
let model = "";
|
|
60495
60535
|
for (const line of lines) {
|
|
60496
60536
|
if (!line.trim()) continue;
|
|
60497
60537
|
let entry;
|
|
@@ -60507,22 +60547,10 @@ function buildCodexSessions(days, allAuditEntries) {
|
|
|
60507
60547
|
cwd = String(p["cwd"] ?? "");
|
|
60508
60548
|
continue;
|
|
60509
60549
|
}
|
|
60510
|
-
if (entry.type === "turn_context" && typeof p["model"] === "string") {
|
|
60511
|
-
model = p["model"];
|
|
60512
|
-
continue;
|
|
60513
|
-
}
|
|
60514
60550
|
if (entry.type === "event_msg" && p["type"] === "user_message" && !firstPrompt) {
|
|
60515
60551
|
firstPrompt = String(p["message"] ?? "");
|
|
60516
60552
|
continue;
|
|
60517
60553
|
}
|
|
60518
|
-
if (entry.type === "event_msg" && p["type"] === "token_count") {
|
|
60519
|
-
const info = p["info"] ?? {};
|
|
60520
|
-
const usage = info["total_token_usage"] ?? {};
|
|
60521
|
-
lastTotalInput = usage["input_tokens"] ?? lastTotalInput;
|
|
60522
|
-
lastTotalCached = usage["cached_input_tokens"] ?? lastTotalCached;
|
|
60523
|
-
lastTotalOutput = usage["output_tokens"] ?? lastTotalOutput;
|
|
60524
|
-
continue;
|
|
60525
|
-
}
|
|
60526
60554
|
if (entry.type === "response_item" && p["type"] === "function_call") {
|
|
60527
60555
|
const tool = String(p["name"] ?? "");
|
|
60528
60556
|
let input = {};
|
|
@@ -60536,12 +60564,13 @@ function buildCodexSessions(days, allAuditEntries) {
|
|
|
60536
60564
|
}
|
|
60537
60565
|
}
|
|
60538
60566
|
if (!sessionId || !startTime) continue;
|
|
60539
|
-
|
|
60540
|
-
const
|
|
60541
|
-
|
|
60542
|
-
|
|
60543
|
-
|
|
60544
|
-
|
|
60567
|
+
const parsedUsage = parseCodexUsage(lines);
|
|
60568
|
+
const usageEvents = codexUsageInWindow(parsedUsage, cutoff);
|
|
60569
|
+
if (cutoff && new Date(startTime) < cutoff && usageEvents.length === 0 && !toolCalls.some((call) => new Date(call.timestamp) >= cutoff))
|
|
60570
|
+
continue;
|
|
60571
|
+
const costUSD = usageEvents.reduce((sum, event) => sum + event.costUSD, 0);
|
|
60572
|
+
const lastUsageTs = parsedUsage.events.at(-1)?.timestamp ?? "";
|
|
60573
|
+
if (lastUsageTs > lastToolTs) lastToolTs = lastUsageTs;
|
|
60545
60574
|
const windowEnd = new Date(
|
|
60546
60575
|
Math.max(new Date(startTime).getTime(), lastToolTs ? new Date(lastToolTs).getTime() : 0) + 5 * 60 * 1e3
|
|
60547
60576
|
).toISOString();
|