@node9/proxy 2.7.2 → 2.7.4
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 +177 -169
- package/dist/cli.mjs +175 -167
- package/dist/dashboard.mjs +9 -5
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -11883,6 +11883,132 @@ var init_cost_codex = __esm({
|
|
|
11883
11883
|
}
|
|
11884
11884
|
});
|
|
11885
11885
|
|
|
11886
|
+
// src/cost-copilot.ts
|
|
11887
|
+
function copilotSessionsDir() {
|
|
11888
|
+
return import_path22.default.join(import_os19.default.homedir(), ".copilot", "session-state");
|
|
11889
|
+
}
|
|
11890
|
+
function safeReaddir3(dir) {
|
|
11891
|
+
try {
|
|
11892
|
+
return import_fs20.default.readdirSync(dir);
|
|
11893
|
+
} catch {
|
|
11894
|
+
return [];
|
|
11895
|
+
}
|
|
11896
|
+
}
|
|
11897
|
+
function priceTokens(model, u) {
|
|
11898
|
+
const tuple = pricingFor(model);
|
|
11899
|
+
if (!tuple || !u) return 0;
|
|
11900
|
+
const [pin, pout, pcw, pcr] = tuple;
|
|
11901
|
+
return (u.inputTokens ?? 0) * pin + (u.outputTokens ?? 0) * pout + (u.cacheWriteTokens ?? 0) * pcw + (u.cacheReadTokens ?? 0) * pcr;
|
|
11902
|
+
}
|
|
11903
|
+
function parseCopilotSession(lines) {
|
|
11904
|
+
let sessionId = "";
|
|
11905
|
+
let cwd = "";
|
|
11906
|
+
let startDate = "";
|
|
11907
|
+
let shutdownDate = "";
|
|
11908
|
+
let modelMetrics = null;
|
|
11909
|
+
for (const raw of lines) {
|
|
11910
|
+
if (!raw.trim()) continue;
|
|
11911
|
+
let o;
|
|
11912
|
+
try {
|
|
11913
|
+
o = JSON.parse(raw);
|
|
11914
|
+
} catch {
|
|
11915
|
+
continue;
|
|
11916
|
+
}
|
|
11917
|
+
const d = o.data ?? {};
|
|
11918
|
+
if (o.type === "session.start") {
|
|
11919
|
+
if (typeof d["sessionId"] === "string") sessionId = d["sessionId"];
|
|
11920
|
+
if (typeof d["startTime"] === "string") startDate = d["startTime"];
|
|
11921
|
+
const ctx = d["context"] ?? {};
|
|
11922
|
+
if (typeof ctx["cwd"] === "string") cwd = ctx["cwd"];
|
|
11923
|
+
} else if (o.type === "session.shutdown") {
|
|
11924
|
+
if (d["modelMetrics"] && typeof d["modelMetrics"] === "object") {
|
|
11925
|
+
modelMetrics = d["modelMetrics"];
|
|
11926
|
+
}
|
|
11927
|
+
if (typeof o.timestamp === "string") shutdownDate = o.timestamp;
|
|
11928
|
+
}
|
|
11929
|
+
}
|
|
11930
|
+
if (!modelMetrics) return [];
|
|
11931
|
+
const date = (startDate || shutdownDate).slice(0, 10);
|
|
11932
|
+
if (!date) return [];
|
|
11933
|
+
const rows = [];
|
|
11934
|
+
for (const [rawModel, m] of Object.entries(modelMetrics)) {
|
|
11935
|
+
const u = m.usage ?? {};
|
|
11936
|
+
const inputTokens = u.inputTokens ?? 0;
|
|
11937
|
+
const outputTokens = u.outputTokens ?? 0;
|
|
11938
|
+
const cacheReadTokens = u.cacheReadTokens ?? 0;
|
|
11939
|
+
const cacheWriteTokens = u.cacheWriteTokens ?? 0;
|
|
11940
|
+
if (inputTokens === 0 && outputTokens === 0 && cacheReadTokens === 0 && cacheWriteTokens === 0) {
|
|
11941
|
+
continue;
|
|
11942
|
+
}
|
|
11943
|
+
const model = normalizeModel(rawModel);
|
|
11944
|
+
const costUSD = typeof m.requests?.cost === "number" && m.requests.cost > 0 ? m.requests.cost : priceTokens(rawModel, u);
|
|
11945
|
+
rows.push({
|
|
11946
|
+
date,
|
|
11947
|
+
model,
|
|
11948
|
+
workingDir: cwd,
|
|
11949
|
+
runId: sessionId,
|
|
11950
|
+
costUSD,
|
|
11951
|
+
inputTokens,
|
|
11952
|
+
outputTokens,
|
|
11953
|
+
cacheReadTokens,
|
|
11954
|
+
cacheWriteTokens
|
|
11955
|
+
});
|
|
11956
|
+
}
|
|
11957
|
+
return rows;
|
|
11958
|
+
}
|
|
11959
|
+
var import_fs20, import_os19, import_path22, copilotSource;
|
|
11960
|
+
var init_cost_copilot = __esm({
|
|
11961
|
+
"src/cost-copilot.ts"() {
|
|
11962
|
+
"use strict";
|
|
11963
|
+
import_fs20 = __toESM(require("fs"));
|
|
11964
|
+
import_os19 = __toESM(require("os"));
|
|
11965
|
+
import_path22 = __toESM(require("path"));
|
|
11966
|
+
init_litellm();
|
|
11967
|
+
copilotSource = {
|
|
11968
|
+
id: "copilot",
|
|
11969
|
+
available() {
|
|
11970
|
+
try {
|
|
11971
|
+
return import_fs20.default.existsSync(copilotSessionsDir());
|
|
11972
|
+
} catch {
|
|
11973
|
+
return false;
|
|
11974
|
+
}
|
|
11975
|
+
},
|
|
11976
|
+
collect(sinceMs) {
|
|
11977
|
+
const base = copilotSessionsDir();
|
|
11978
|
+
const combined = /* @__PURE__ */ new Map();
|
|
11979
|
+
for (const sid of safeReaddir3(base)) {
|
|
11980
|
+
const file = import_path22.default.join(base, sid, "events.jsonl");
|
|
11981
|
+
try {
|
|
11982
|
+
if (sinceMs !== void 0 && import_fs20.default.statSync(file).mtimeMs < sinceMs) continue;
|
|
11983
|
+
} catch {
|
|
11984
|
+
continue;
|
|
11985
|
+
}
|
|
11986
|
+
let content;
|
|
11987
|
+
try {
|
|
11988
|
+
content = import_fs20.default.readFileSync(file, "utf8");
|
|
11989
|
+
} catch {
|
|
11990
|
+
continue;
|
|
11991
|
+
}
|
|
11992
|
+
for (const e of parseCopilotSession(content.split("\n"))) {
|
|
11993
|
+
const key = `${e.date}::${e.model}::${e.workingDir ?? ""}::${e.runId ?? ""}`;
|
|
11994
|
+
const prev = combined.get(key);
|
|
11995
|
+
if (prev) {
|
|
11996
|
+
prev.costUSD += e.costUSD;
|
|
11997
|
+
prev.inputTokens += e.inputTokens;
|
|
11998
|
+
prev.outputTokens += e.outputTokens;
|
|
11999
|
+
prev.cacheReadTokens += e.cacheReadTokens;
|
|
12000
|
+
prev.cacheWriteTokens += e.cacheWriteTokens;
|
|
12001
|
+
} else {
|
|
12002
|
+
combined.set(key, { ...e });
|
|
12003
|
+
}
|
|
12004
|
+
}
|
|
12005
|
+
}
|
|
12006
|
+
return [...combined.values()];
|
|
12007
|
+
}
|
|
12008
|
+
};
|
|
12009
|
+
}
|
|
12010
|
+
});
|
|
12011
|
+
|
|
11886
12012
|
// src/utils/hook-payload.ts
|
|
11887
12013
|
function extractToolName(payload, defaultValue = "") {
|
|
11888
12014
|
return payload.tool_name ?? payload.name ?? payload.toolCall?.name ?? defaultValue;
|
|
@@ -12197,79 +12323,79 @@ var init_scan_summary = __esm({
|
|
|
12197
12323
|
function buildSensitivePaths(home, cwd) {
|
|
12198
12324
|
return [
|
|
12199
12325
|
{
|
|
12200
|
-
full:
|
|
12326
|
+
full: import_path23.default.join(home, ".ssh", "id_rsa"),
|
|
12201
12327
|
label: "~/.ssh/id_rsa",
|
|
12202
12328
|
description: "RSA private key \u2014 grants SSH access to your servers",
|
|
12203
12329
|
score: 20
|
|
12204
12330
|
},
|
|
12205
12331
|
{
|
|
12206
|
-
full:
|
|
12332
|
+
full: import_path23.default.join(home, ".ssh", "id_ed25519"),
|
|
12207
12333
|
label: "~/.ssh/id_ed25519",
|
|
12208
12334
|
description: "Ed25519 private key \u2014 grants SSH access to your servers",
|
|
12209
12335
|
score: 20
|
|
12210
12336
|
},
|
|
12211
12337
|
{
|
|
12212
|
-
full:
|
|
12338
|
+
full: import_path23.default.join(home, ".ssh", "id_ecdsa"),
|
|
12213
12339
|
label: "~/.ssh/id_ecdsa",
|
|
12214
12340
|
description: "ECDSA private key \u2014 grants SSH access to your servers",
|
|
12215
12341
|
score: 20
|
|
12216
12342
|
},
|
|
12217
12343
|
{
|
|
12218
|
-
full:
|
|
12344
|
+
full: import_path23.default.join(home, ".aws", "credentials"),
|
|
12219
12345
|
label: "~/.aws/credentials",
|
|
12220
12346
|
description: "AWS access keys \u2014 full cloud account access",
|
|
12221
12347
|
score: 20
|
|
12222
12348
|
},
|
|
12223
12349
|
{
|
|
12224
|
-
full:
|
|
12350
|
+
full: import_path23.default.join(home, ".aws", "config"),
|
|
12225
12351
|
label: "~/.aws/config",
|
|
12226
12352
|
description: "AWS configuration \u2014 account and region settings",
|
|
12227
12353
|
score: 5
|
|
12228
12354
|
},
|
|
12229
12355
|
{
|
|
12230
|
-
full:
|
|
12356
|
+
full: import_path23.default.join(home, ".config", "gcloud", "credentials.db"),
|
|
12231
12357
|
label: "~/.config/gcloud/credentials.db",
|
|
12232
12358
|
description: "Google Cloud credentials",
|
|
12233
12359
|
score: 15
|
|
12234
12360
|
},
|
|
12235
12361
|
{
|
|
12236
|
-
full:
|
|
12362
|
+
full: import_path23.default.join(home, ".docker", "config.json"),
|
|
12237
12363
|
label: "~/.docker/config.json",
|
|
12238
12364
|
description: "Docker registry auth tokens",
|
|
12239
12365
|
score: 10
|
|
12240
12366
|
},
|
|
12241
12367
|
{
|
|
12242
|
-
full:
|
|
12368
|
+
full: import_path23.default.join(home, ".netrc"),
|
|
12243
12369
|
label: "~/.netrc",
|
|
12244
12370
|
description: "FTP/HTTP credentials in plain text",
|
|
12245
12371
|
score: 15
|
|
12246
12372
|
},
|
|
12247
12373
|
{
|
|
12248
|
-
full:
|
|
12374
|
+
full: import_path23.default.join(home, ".npmrc"),
|
|
12249
12375
|
label: "~/.npmrc",
|
|
12250
12376
|
description: "npm auth token \u2014 can publish packages as you",
|
|
12251
12377
|
score: 10
|
|
12252
12378
|
},
|
|
12253
12379
|
{
|
|
12254
|
-
full:
|
|
12380
|
+
full: import_path23.default.join(home, ".node9", "credentials.json"),
|
|
12255
12381
|
label: "~/.node9/credentials.json",
|
|
12256
12382
|
description: "Node9 cloud API key",
|
|
12257
12383
|
score: 10
|
|
12258
12384
|
},
|
|
12259
12385
|
{
|
|
12260
|
-
full:
|
|
12386
|
+
full: import_path23.default.join(cwd, ".env"),
|
|
12261
12387
|
label: ".env (current folder)",
|
|
12262
12388
|
description: "App secrets \u2014 database passwords, API keys",
|
|
12263
12389
|
score: 20
|
|
12264
12390
|
},
|
|
12265
12391
|
{
|
|
12266
|
-
full:
|
|
12392
|
+
full: import_path23.default.join(cwd, ".env.local"),
|
|
12267
12393
|
label: ".env.local (current folder)",
|
|
12268
12394
|
description: "Local overrides \u2014 often contains real credentials",
|
|
12269
12395
|
score: 15
|
|
12270
12396
|
},
|
|
12271
12397
|
{
|
|
12272
|
-
full:
|
|
12398
|
+
full: import_path23.default.join(cwd, ".env.production"),
|
|
12273
12399
|
label: ".env.production (current folder)",
|
|
12274
12400
|
description: "Production secrets",
|
|
12275
12401
|
score: 20
|
|
@@ -12278,7 +12404,7 @@ function buildSensitivePaths(home, cwd) {
|
|
|
12278
12404
|
}
|
|
12279
12405
|
function isReadable(filePath) {
|
|
12280
12406
|
try {
|
|
12281
|
-
|
|
12407
|
+
import_fs21.default.accessSync(filePath, import_fs21.default.constants.R_OK);
|
|
12282
12408
|
return true;
|
|
12283
12409
|
} catch {
|
|
12284
12410
|
return false;
|
|
@@ -12291,13 +12417,13 @@ function scoreLabel(score) {
|
|
|
12291
12417
|
return import_chalk3.default.red.bold(`${score}/100 Critical`);
|
|
12292
12418
|
}
|
|
12293
12419
|
function runBlast() {
|
|
12294
|
-
const home =
|
|
12420
|
+
const home = import_os20.default.homedir();
|
|
12295
12421
|
const cwd = process.cwd();
|
|
12296
12422
|
const paths = buildSensitivePaths(home, cwd);
|
|
12297
12423
|
let scoreDeduction = 0;
|
|
12298
12424
|
const reachable = [];
|
|
12299
12425
|
for (const p of paths) {
|
|
12300
|
-
if (
|
|
12426
|
+
if (import_fs21.default.existsSync(p.full) && isReadable(p.full)) {
|
|
12301
12427
|
reachable.push(p);
|
|
12302
12428
|
scoreDeduction += p.score;
|
|
12303
12429
|
}
|
|
@@ -12315,7 +12441,7 @@ function runBlast() {
|
|
|
12315
12441
|
}
|
|
12316
12442
|
function registerBlastCommand(program2) {
|
|
12317
12443
|
program2.command("blast").description("Map what an AI agent can currently reach on this machine").action(() => {
|
|
12318
|
-
const home =
|
|
12444
|
+
const home = import_os20.default.homedir();
|
|
12319
12445
|
const cwd = process.cwd();
|
|
12320
12446
|
const { reachable, envFindings, score } = runBlast();
|
|
12321
12447
|
console.log("");
|
|
@@ -12362,14 +12488,14 @@ function registerBlastCommand(program2) {
|
|
|
12362
12488
|
console.log("");
|
|
12363
12489
|
});
|
|
12364
12490
|
}
|
|
12365
|
-
var import_chalk3,
|
|
12491
|
+
var import_chalk3, import_fs21, import_path23, import_os20;
|
|
12366
12492
|
var init_blast = __esm({
|
|
12367
12493
|
"src/cli/commands/blast.ts"() {
|
|
12368
12494
|
"use strict";
|
|
12369
12495
|
import_chalk3 = __toESM(require("chalk"));
|
|
12370
|
-
|
|
12371
|
-
|
|
12372
|
-
|
|
12496
|
+
import_fs21 = __toESM(require("fs"));
|
|
12497
|
+
import_path23 = __toESM(require("path"));
|
|
12498
|
+
import_os20 = __toESM(require("os"));
|
|
12373
12499
|
init_dlp();
|
|
12374
12500
|
}
|
|
12375
12501
|
});
|
|
@@ -12514,13 +12640,13 @@ function listSessionFiles(dir, maxDepth = 6) {
|
|
|
12514
12640
|
if (depth > maxDepth) return;
|
|
12515
12641
|
let entries;
|
|
12516
12642
|
try {
|
|
12517
|
-
entries =
|
|
12643
|
+
entries = fs23.readdirSync(d, { withFileTypes: true });
|
|
12518
12644
|
} catch {
|
|
12519
12645
|
return;
|
|
12520
12646
|
}
|
|
12521
12647
|
for (const e of entries) {
|
|
12522
|
-
const childRel = rel ?
|
|
12523
|
-
if (e.isDirectory()) walk(
|
|
12648
|
+
const childRel = rel ? path25.join(rel, e.name) : e.name;
|
|
12649
|
+
if (e.isDirectory()) walk(path25.join(d, e.name), childRel, depth + 1);
|
|
12524
12650
|
else if (e.name.endsWith(".jsonl")) out.push(childRel);
|
|
12525
12651
|
}
|
|
12526
12652
|
};
|
|
@@ -12528,26 +12654,26 @@ function listSessionFiles(dir, maxDepth = 6) {
|
|
|
12528
12654
|
return out;
|
|
12529
12655
|
}
|
|
12530
12656
|
function sessionIdOf(relPath) {
|
|
12531
|
-
return
|
|
12657
|
+
return path25.basename(relPath).replace(/\.jsonl$/, "");
|
|
12532
12658
|
}
|
|
12533
|
-
var
|
|
12659
|
+
var fs23, path25;
|
|
12534
12660
|
var init_session_files = __esm({
|
|
12535
12661
|
"src/session-files.ts"() {
|
|
12536
12662
|
"use strict";
|
|
12537
|
-
|
|
12538
|
-
|
|
12663
|
+
fs23 = __toESM(require("fs"));
|
|
12664
|
+
path25 = __toESM(require("path"));
|
|
12539
12665
|
}
|
|
12540
12666
|
});
|
|
12541
12667
|
|
|
12542
12668
|
// src/cli/render/scan-history.ts
|
|
12543
12669
|
function defaultHistoryPath() {
|
|
12544
|
-
return
|
|
12670
|
+
return import_path24.default.join(import_os21.default.homedir(), ".node9", "scan-history.json");
|
|
12545
12671
|
}
|
|
12546
12672
|
function readPreviousScan(opts = {}) {
|
|
12547
12673
|
const filePath = opts.path ?? defaultHistoryPath();
|
|
12548
12674
|
try {
|
|
12549
|
-
if (!
|
|
12550
|
-
const raw =
|
|
12675
|
+
if (!import_fs22.default.existsSync(filePath)) return null;
|
|
12676
|
+
const raw = import_fs22.default.readFileSync(filePath, "utf8");
|
|
12551
12677
|
const parsed = JSON.parse(raw);
|
|
12552
12678
|
if (!Array.isArray(parsed) || parsed.length === 0) return null;
|
|
12553
12679
|
const last = parsed[parsed.length - 1];
|
|
@@ -12561,11 +12687,11 @@ function appendScanHistory(record, opts = {}) {
|
|
|
12561
12687
|
const filePath = opts.path ?? defaultHistoryPath();
|
|
12562
12688
|
const cap = opts.cap ?? SCAN_HISTORY_CAP;
|
|
12563
12689
|
try {
|
|
12564
|
-
|
|
12690
|
+
import_fs22.default.mkdirSync(import_path24.default.dirname(filePath), { recursive: true });
|
|
12565
12691
|
let history = [];
|
|
12566
|
-
if (
|
|
12692
|
+
if (import_fs22.default.existsSync(filePath)) {
|
|
12567
12693
|
try {
|
|
12568
|
-
const parsed = JSON.parse(
|
|
12694
|
+
const parsed = JSON.parse(import_fs22.default.readFileSync(filePath, "utf8"));
|
|
12569
12695
|
if (Array.isArray(parsed)) {
|
|
12570
12696
|
history = parsed.filter(isValidRecord);
|
|
12571
12697
|
}
|
|
@@ -12576,7 +12702,7 @@ function appendScanHistory(record, opts = {}) {
|
|
|
12576
12702
|
if (history.length > cap) {
|
|
12577
12703
|
history = history.slice(history.length - cap);
|
|
12578
12704
|
}
|
|
12579
|
-
|
|
12705
|
+
import_fs22.default.writeFileSync(filePath, JSON.stringify(history, null, 2));
|
|
12580
12706
|
} catch (err2) {
|
|
12581
12707
|
process.stderr.write(
|
|
12582
12708
|
`[node9] Warning: could not write scan-history.json: ${err2.message}
|
|
@@ -12598,140 +12724,14 @@ function isValidRecord(x) {
|
|
|
12598
12724
|
const r = x;
|
|
12599
12725
|
return typeof r.timestamp === "string" && typeof r.score === "number" && typeof r.blocked === "number" && typeof r.review === "number" && typeof r.leaks === "number" && typeof r.loops === "number" && typeof r.totalCalls === "number";
|
|
12600
12726
|
}
|
|
12601
|
-
var
|
|
12727
|
+
var import_fs22, import_path24, import_os21, SCAN_HISTORY_CAP;
|
|
12602
12728
|
var init_scan_history = __esm({
|
|
12603
12729
|
"src/cli/render/scan-history.ts"() {
|
|
12604
|
-
"use strict";
|
|
12605
|
-
import_fs21 = __toESM(require("fs"));
|
|
12606
|
-
import_path23 = __toESM(require("path"));
|
|
12607
|
-
import_os20 = __toESM(require("os"));
|
|
12608
|
-
SCAN_HISTORY_CAP = 30;
|
|
12609
|
-
}
|
|
12610
|
-
});
|
|
12611
|
-
|
|
12612
|
-
// src/cost-copilot.ts
|
|
12613
|
-
function copilotSessionsDir() {
|
|
12614
|
-
return import_path24.default.join(import_os21.default.homedir(), ".copilot", "session-state");
|
|
12615
|
-
}
|
|
12616
|
-
function safeReaddir3(dir) {
|
|
12617
|
-
try {
|
|
12618
|
-
return import_fs22.default.readdirSync(dir);
|
|
12619
|
-
} catch {
|
|
12620
|
-
return [];
|
|
12621
|
-
}
|
|
12622
|
-
}
|
|
12623
|
-
function priceTokens(model, u) {
|
|
12624
|
-
const tuple = pricingFor(model);
|
|
12625
|
-
if (!tuple || !u) return 0;
|
|
12626
|
-
const [pin, pout, pcw, pcr] = tuple;
|
|
12627
|
-
return (u.inputTokens ?? 0) * pin + (u.outputTokens ?? 0) * pout + (u.cacheWriteTokens ?? 0) * pcw + (u.cacheReadTokens ?? 0) * pcr;
|
|
12628
|
-
}
|
|
12629
|
-
function parseCopilotSession(lines) {
|
|
12630
|
-
let sessionId = "";
|
|
12631
|
-
let cwd = "";
|
|
12632
|
-
let startDate = "";
|
|
12633
|
-
let shutdownDate = "";
|
|
12634
|
-
let modelMetrics = null;
|
|
12635
|
-
for (const raw of lines) {
|
|
12636
|
-
if (!raw.trim()) continue;
|
|
12637
|
-
let o;
|
|
12638
|
-
try {
|
|
12639
|
-
o = JSON.parse(raw);
|
|
12640
|
-
} catch {
|
|
12641
|
-
continue;
|
|
12642
|
-
}
|
|
12643
|
-
const d = o.data ?? {};
|
|
12644
|
-
if (o.type === "session.start") {
|
|
12645
|
-
if (typeof d["sessionId"] === "string") sessionId = d["sessionId"];
|
|
12646
|
-
if (typeof d["startTime"] === "string") startDate = d["startTime"];
|
|
12647
|
-
const ctx = d["context"] ?? {};
|
|
12648
|
-
if (typeof ctx["cwd"] === "string") cwd = ctx["cwd"];
|
|
12649
|
-
} else if (o.type === "session.shutdown") {
|
|
12650
|
-
if (d["modelMetrics"] && typeof d["modelMetrics"] === "object") {
|
|
12651
|
-
modelMetrics = d["modelMetrics"];
|
|
12652
|
-
}
|
|
12653
|
-
if (typeof o.timestamp === "string") shutdownDate = o.timestamp;
|
|
12654
|
-
}
|
|
12655
|
-
}
|
|
12656
|
-
if (!modelMetrics) return [];
|
|
12657
|
-
const date = (startDate || shutdownDate).slice(0, 10);
|
|
12658
|
-
if (!date) return [];
|
|
12659
|
-
const rows = [];
|
|
12660
|
-
for (const [rawModel, m] of Object.entries(modelMetrics)) {
|
|
12661
|
-
const u = m.usage ?? {};
|
|
12662
|
-
const inputTokens = u.inputTokens ?? 0;
|
|
12663
|
-
const outputTokens = u.outputTokens ?? 0;
|
|
12664
|
-
const cacheReadTokens = u.cacheReadTokens ?? 0;
|
|
12665
|
-
const cacheWriteTokens = u.cacheWriteTokens ?? 0;
|
|
12666
|
-
if (inputTokens === 0 && outputTokens === 0 && cacheReadTokens === 0 && cacheWriteTokens === 0) {
|
|
12667
|
-
continue;
|
|
12668
|
-
}
|
|
12669
|
-
const model = normalizeModel(rawModel);
|
|
12670
|
-
const costUSD = typeof m.requests?.cost === "number" && m.requests.cost > 0 ? m.requests.cost : priceTokens(rawModel, u);
|
|
12671
|
-
rows.push({
|
|
12672
|
-
date,
|
|
12673
|
-
model,
|
|
12674
|
-
workingDir: cwd,
|
|
12675
|
-
runId: sessionId,
|
|
12676
|
-
costUSD,
|
|
12677
|
-
inputTokens,
|
|
12678
|
-
outputTokens,
|
|
12679
|
-
cacheReadTokens,
|
|
12680
|
-
cacheWriteTokens
|
|
12681
|
-
});
|
|
12682
|
-
}
|
|
12683
|
-
return rows;
|
|
12684
|
-
}
|
|
12685
|
-
var import_fs22, import_os21, import_path24, copilotSource;
|
|
12686
|
-
var init_cost_copilot = __esm({
|
|
12687
|
-
"src/cost-copilot.ts"() {
|
|
12688
12730
|
"use strict";
|
|
12689
12731
|
import_fs22 = __toESM(require("fs"));
|
|
12690
|
-
import_os21 = __toESM(require("os"));
|
|
12691
12732
|
import_path24 = __toESM(require("path"));
|
|
12692
|
-
|
|
12693
|
-
|
|
12694
|
-
id: "copilot",
|
|
12695
|
-
available() {
|
|
12696
|
-
try {
|
|
12697
|
-
return import_fs22.default.existsSync(copilotSessionsDir());
|
|
12698
|
-
} catch {
|
|
12699
|
-
return false;
|
|
12700
|
-
}
|
|
12701
|
-
},
|
|
12702
|
-
collect(sinceMs) {
|
|
12703
|
-
const base = copilotSessionsDir();
|
|
12704
|
-
const combined = /* @__PURE__ */ new Map();
|
|
12705
|
-
for (const sid of safeReaddir3(base)) {
|
|
12706
|
-
const file = import_path24.default.join(base, sid, "events.jsonl");
|
|
12707
|
-
try {
|
|
12708
|
-
if (sinceMs !== void 0 && import_fs22.default.statSync(file).mtimeMs < sinceMs) continue;
|
|
12709
|
-
} catch {
|
|
12710
|
-
continue;
|
|
12711
|
-
}
|
|
12712
|
-
let content;
|
|
12713
|
-
try {
|
|
12714
|
-
content = import_fs22.default.readFileSync(file, "utf8");
|
|
12715
|
-
} catch {
|
|
12716
|
-
continue;
|
|
12717
|
-
}
|
|
12718
|
-
for (const e of parseCopilotSession(content.split("\n"))) {
|
|
12719
|
-
const key = `${e.date}::${e.model}::${e.workingDir ?? ""}::${e.runId ?? ""}`;
|
|
12720
|
-
const prev = combined.get(key);
|
|
12721
|
-
if (prev) {
|
|
12722
|
-
prev.costUSD += e.costUSD;
|
|
12723
|
-
prev.inputTokens += e.inputTokens;
|
|
12724
|
-
prev.outputTokens += e.outputTokens;
|
|
12725
|
-
prev.cacheReadTokens += e.cacheReadTokens;
|
|
12726
|
-
prev.cacheWriteTokens += e.cacheWriteTokens;
|
|
12727
|
-
} else {
|
|
12728
|
-
combined.set(key, { ...e });
|
|
12729
|
-
}
|
|
12730
|
-
}
|
|
12731
|
-
}
|
|
12732
|
-
return [...combined.values()];
|
|
12733
|
-
}
|
|
12734
|
-
};
|
|
12733
|
+
import_os21 = __toESM(require("os"));
|
|
12734
|
+
SCAN_HISTORY_CAP = 30;
|
|
12735
12735
|
}
|
|
12736
12736
|
});
|
|
12737
12737
|
|
|
@@ -14776,6 +14776,10 @@ function scanCopilotHistory(startDate, onProgress, onLine) {
|
|
|
14776
14776
|
let projLabel = sessionId.slice(0, 8);
|
|
14777
14777
|
const sessionCalls = [];
|
|
14778
14778
|
result.sessions++;
|
|
14779
|
+
for (const row of parseCopilotSession(raw.split("\n"))) {
|
|
14780
|
+
if (startDate && row.date && new Date(row.date) < startDate) continue;
|
|
14781
|
+
result.totalCostUSD += row.costUSD;
|
|
14782
|
+
}
|
|
14779
14783
|
for (const line of raw.split("\n")) {
|
|
14780
14784
|
if (!line.trim()) continue;
|
|
14781
14785
|
onLine?.();
|
|
@@ -15152,11 +15156,14 @@ function scanCodexHistory(startDate, onProgress, onLine) {
|
|
|
15152
15156
|
}
|
|
15153
15157
|
}
|
|
15154
15158
|
}
|
|
15155
|
-
|
|
15156
|
-
|
|
15157
|
-
|
|
15158
|
-
|
|
15159
|
-
|
|
15159
|
+
const withinWindow = !startDate || startTime !== "" && new Date(startTime) >= startDate;
|
|
15160
|
+
if (withinWindow) {
|
|
15161
|
+
result.totalCostUSD += codexSessionCost(model, {
|
|
15162
|
+
input: lastTotalInput,
|
|
15163
|
+
cached: lastTotalCached,
|
|
15164
|
+
output: lastTotalOutput
|
|
15165
|
+
});
|
|
15166
|
+
}
|
|
15160
15167
|
result.loopFindings.push(...detectLoops(sessionCalls, projLabel, sessionId, "codex"));
|
|
15161
15168
|
}
|
|
15162
15169
|
return result;
|
|
@@ -16253,6 +16260,7 @@ var init_scan = __esm({
|
|
|
16253
16260
|
init_litellm();
|
|
16254
16261
|
init_cost_gemini();
|
|
16255
16262
|
init_cost_codex();
|
|
16263
|
+
init_cost_copilot();
|
|
16256
16264
|
init_hook_payload();
|
|
16257
16265
|
init_dist();
|
|
16258
16266
|
init_scan_summary();
|
package/dist/cli.mjs
CHANGED
|
@@ -11884,6 +11884,132 @@ var init_cost_codex = __esm({
|
|
|
11884
11884
|
}
|
|
11885
11885
|
});
|
|
11886
11886
|
|
|
11887
|
+
// src/cost-copilot.ts
|
|
11888
|
+
import fs21 from "fs";
|
|
11889
|
+
import os20 from "os";
|
|
11890
|
+
import path23 from "path";
|
|
11891
|
+
function copilotSessionsDir() {
|
|
11892
|
+
return path23.join(os20.homedir(), ".copilot", "session-state");
|
|
11893
|
+
}
|
|
11894
|
+
function safeReaddir3(dir) {
|
|
11895
|
+
try {
|
|
11896
|
+
return fs21.readdirSync(dir);
|
|
11897
|
+
} catch {
|
|
11898
|
+
return [];
|
|
11899
|
+
}
|
|
11900
|
+
}
|
|
11901
|
+
function priceTokens(model, u) {
|
|
11902
|
+
const tuple = pricingFor(model);
|
|
11903
|
+
if (!tuple || !u) return 0;
|
|
11904
|
+
const [pin, pout, pcw, pcr] = tuple;
|
|
11905
|
+
return (u.inputTokens ?? 0) * pin + (u.outputTokens ?? 0) * pout + (u.cacheWriteTokens ?? 0) * pcw + (u.cacheReadTokens ?? 0) * pcr;
|
|
11906
|
+
}
|
|
11907
|
+
function parseCopilotSession(lines) {
|
|
11908
|
+
let sessionId = "";
|
|
11909
|
+
let cwd = "";
|
|
11910
|
+
let startDate = "";
|
|
11911
|
+
let shutdownDate = "";
|
|
11912
|
+
let modelMetrics = null;
|
|
11913
|
+
for (const raw of lines) {
|
|
11914
|
+
if (!raw.trim()) continue;
|
|
11915
|
+
let o;
|
|
11916
|
+
try {
|
|
11917
|
+
o = JSON.parse(raw);
|
|
11918
|
+
} catch {
|
|
11919
|
+
continue;
|
|
11920
|
+
}
|
|
11921
|
+
const d = o.data ?? {};
|
|
11922
|
+
if (o.type === "session.start") {
|
|
11923
|
+
if (typeof d["sessionId"] === "string") sessionId = d["sessionId"];
|
|
11924
|
+
if (typeof d["startTime"] === "string") startDate = d["startTime"];
|
|
11925
|
+
const ctx = d["context"] ?? {};
|
|
11926
|
+
if (typeof ctx["cwd"] === "string") cwd = ctx["cwd"];
|
|
11927
|
+
} else if (o.type === "session.shutdown") {
|
|
11928
|
+
if (d["modelMetrics"] && typeof d["modelMetrics"] === "object") {
|
|
11929
|
+
modelMetrics = d["modelMetrics"];
|
|
11930
|
+
}
|
|
11931
|
+
if (typeof o.timestamp === "string") shutdownDate = o.timestamp;
|
|
11932
|
+
}
|
|
11933
|
+
}
|
|
11934
|
+
if (!modelMetrics) return [];
|
|
11935
|
+
const date = (startDate || shutdownDate).slice(0, 10);
|
|
11936
|
+
if (!date) return [];
|
|
11937
|
+
const rows = [];
|
|
11938
|
+
for (const [rawModel, m] of Object.entries(modelMetrics)) {
|
|
11939
|
+
const u = m.usage ?? {};
|
|
11940
|
+
const inputTokens = u.inputTokens ?? 0;
|
|
11941
|
+
const outputTokens = u.outputTokens ?? 0;
|
|
11942
|
+
const cacheReadTokens = u.cacheReadTokens ?? 0;
|
|
11943
|
+
const cacheWriteTokens = u.cacheWriteTokens ?? 0;
|
|
11944
|
+
if (inputTokens === 0 && outputTokens === 0 && cacheReadTokens === 0 && cacheWriteTokens === 0) {
|
|
11945
|
+
continue;
|
|
11946
|
+
}
|
|
11947
|
+
const model = normalizeModel(rawModel);
|
|
11948
|
+
const costUSD = typeof m.requests?.cost === "number" && m.requests.cost > 0 ? m.requests.cost : priceTokens(rawModel, u);
|
|
11949
|
+
rows.push({
|
|
11950
|
+
date,
|
|
11951
|
+
model,
|
|
11952
|
+
workingDir: cwd,
|
|
11953
|
+
runId: sessionId,
|
|
11954
|
+
costUSD,
|
|
11955
|
+
inputTokens,
|
|
11956
|
+
outputTokens,
|
|
11957
|
+
cacheReadTokens,
|
|
11958
|
+
cacheWriteTokens
|
|
11959
|
+
});
|
|
11960
|
+
}
|
|
11961
|
+
return rows;
|
|
11962
|
+
}
|
|
11963
|
+
var copilotSource;
|
|
11964
|
+
var init_cost_copilot = __esm({
|
|
11965
|
+
"src/cost-copilot.ts"() {
|
|
11966
|
+
"use strict";
|
|
11967
|
+
init_litellm();
|
|
11968
|
+
copilotSource = {
|
|
11969
|
+
id: "copilot",
|
|
11970
|
+
available() {
|
|
11971
|
+
try {
|
|
11972
|
+
return fs21.existsSync(copilotSessionsDir());
|
|
11973
|
+
} catch {
|
|
11974
|
+
return false;
|
|
11975
|
+
}
|
|
11976
|
+
},
|
|
11977
|
+
collect(sinceMs) {
|
|
11978
|
+
const base = copilotSessionsDir();
|
|
11979
|
+
const combined = /* @__PURE__ */ new Map();
|
|
11980
|
+
for (const sid of safeReaddir3(base)) {
|
|
11981
|
+
const file = path23.join(base, sid, "events.jsonl");
|
|
11982
|
+
try {
|
|
11983
|
+
if (sinceMs !== void 0 && fs21.statSync(file).mtimeMs < sinceMs) continue;
|
|
11984
|
+
} catch {
|
|
11985
|
+
continue;
|
|
11986
|
+
}
|
|
11987
|
+
let content;
|
|
11988
|
+
try {
|
|
11989
|
+
content = fs21.readFileSync(file, "utf8");
|
|
11990
|
+
} catch {
|
|
11991
|
+
continue;
|
|
11992
|
+
}
|
|
11993
|
+
for (const e of parseCopilotSession(content.split("\n"))) {
|
|
11994
|
+
const key = `${e.date}::${e.model}::${e.workingDir ?? ""}::${e.runId ?? ""}`;
|
|
11995
|
+
const prev = combined.get(key);
|
|
11996
|
+
if (prev) {
|
|
11997
|
+
prev.costUSD += e.costUSD;
|
|
11998
|
+
prev.inputTokens += e.inputTokens;
|
|
11999
|
+
prev.outputTokens += e.outputTokens;
|
|
12000
|
+
prev.cacheReadTokens += e.cacheReadTokens;
|
|
12001
|
+
prev.cacheWriteTokens += e.cacheWriteTokens;
|
|
12002
|
+
} else {
|
|
12003
|
+
combined.set(key, { ...e });
|
|
12004
|
+
}
|
|
12005
|
+
}
|
|
12006
|
+
}
|
|
12007
|
+
return [...combined.values()];
|
|
12008
|
+
}
|
|
12009
|
+
};
|
|
12010
|
+
}
|
|
12011
|
+
});
|
|
12012
|
+
|
|
11887
12013
|
// src/utils/hook-payload.ts
|
|
11888
12014
|
function extractToolName(payload, defaultValue = "") {
|
|
11889
12015
|
return payload.tool_name ?? payload.name ?? payload.toolCall?.name ?? defaultValue;
|
|
@@ -12196,85 +12322,85 @@ var init_scan_summary = __esm({
|
|
|
12196
12322
|
|
|
12197
12323
|
// src/cli/commands/blast.ts
|
|
12198
12324
|
import chalk3 from "chalk";
|
|
12199
|
-
import
|
|
12200
|
-
import
|
|
12201
|
-
import
|
|
12325
|
+
import fs22 from "fs";
|
|
12326
|
+
import path24 from "path";
|
|
12327
|
+
import os21 from "os";
|
|
12202
12328
|
function buildSensitivePaths(home, cwd) {
|
|
12203
12329
|
return [
|
|
12204
12330
|
{
|
|
12205
|
-
full:
|
|
12331
|
+
full: path24.join(home, ".ssh", "id_rsa"),
|
|
12206
12332
|
label: "~/.ssh/id_rsa",
|
|
12207
12333
|
description: "RSA private key \u2014 grants SSH access to your servers",
|
|
12208
12334
|
score: 20
|
|
12209
12335
|
},
|
|
12210
12336
|
{
|
|
12211
|
-
full:
|
|
12337
|
+
full: path24.join(home, ".ssh", "id_ed25519"),
|
|
12212
12338
|
label: "~/.ssh/id_ed25519",
|
|
12213
12339
|
description: "Ed25519 private key \u2014 grants SSH access to your servers",
|
|
12214
12340
|
score: 20
|
|
12215
12341
|
},
|
|
12216
12342
|
{
|
|
12217
|
-
full:
|
|
12343
|
+
full: path24.join(home, ".ssh", "id_ecdsa"),
|
|
12218
12344
|
label: "~/.ssh/id_ecdsa",
|
|
12219
12345
|
description: "ECDSA private key \u2014 grants SSH access to your servers",
|
|
12220
12346
|
score: 20
|
|
12221
12347
|
},
|
|
12222
12348
|
{
|
|
12223
|
-
full:
|
|
12349
|
+
full: path24.join(home, ".aws", "credentials"),
|
|
12224
12350
|
label: "~/.aws/credentials",
|
|
12225
12351
|
description: "AWS access keys \u2014 full cloud account access",
|
|
12226
12352
|
score: 20
|
|
12227
12353
|
},
|
|
12228
12354
|
{
|
|
12229
|
-
full:
|
|
12355
|
+
full: path24.join(home, ".aws", "config"),
|
|
12230
12356
|
label: "~/.aws/config",
|
|
12231
12357
|
description: "AWS configuration \u2014 account and region settings",
|
|
12232
12358
|
score: 5
|
|
12233
12359
|
},
|
|
12234
12360
|
{
|
|
12235
|
-
full:
|
|
12361
|
+
full: path24.join(home, ".config", "gcloud", "credentials.db"),
|
|
12236
12362
|
label: "~/.config/gcloud/credentials.db",
|
|
12237
12363
|
description: "Google Cloud credentials",
|
|
12238
12364
|
score: 15
|
|
12239
12365
|
},
|
|
12240
12366
|
{
|
|
12241
|
-
full:
|
|
12367
|
+
full: path24.join(home, ".docker", "config.json"),
|
|
12242
12368
|
label: "~/.docker/config.json",
|
|
12243
12369
|
description: "Docker registry auth tokens",
|
|
12244
12370
|
score: 10
|
|
12245
12371
|
},
|
|
12246
12372
|
{
|
|
12247
|
-
full:
|
|
12373
|
+
full: path24.join(home, ".netrc"),
|
|
12248
12374
|
label: "~/.netrc",
|
|
12249
12375
|
description: "FTP/HTTP credentials in plain text",
|
|
12250
12376
|
score: 15
|
|
12251
12377
|
},
|
|
12252
12378
|
{
|
|
12253
|
-
full:
|
|
12379
|
+
full: path24.join(home, ".npmrc"),
|
|
12254
12380
|
label: "~/.npmrc",
|
|
12255
12381
|
description: "npm auth token \u2014 can publish packages as you",
|
|
12256
12382
|
score: 10
|
|
12257
12383
|
},
|
|
12258
12384
|
{
|
|
12259
|
-
full:
|
|
12385
|
+
full: path24.join(home, ".node9", "credentials.json"),
|
|
12260
12386
|
label: "~/.node9/credentials.json",
|
|
12261
12387
|
description: "Node9 cloud API key",
|
|
12262
12388
|
score: 10
|
|
12263
12389
|
},
|
|
12264
12390
|
{
|
|
12265
|
-
full:
|
|
12391
|
+
full: path24.join(cwd, ".env"),
|
|
12266
12392
|
label: ".env (current folder)",
|
|
12267
12393
|
description: "App secrets \u2014 database passwords, API keys",
|
|
12268
12394
|
score: 20
|
|
12269
12395
|
},
|
|
12270
12396
|
{
|
|
12271
|
-
full:
|
|
12397
|
+
full: path24.join(cwd, ".env.local"),
|
|
12272
12398
|
label: ".env.local (current folder)",
|
|
12273
12399
|
description: "Local overrides \u2014 often contains real credentials",
|
|
12274
12400
|
score: 15
|
|
12275
12401
|
},
|
|
12276
12402
|
{
|
|
12277
|
-
full:
|
|
12403
|
+
full: path24.join(cwd, ".env.production"),
|
|
12278
12404
|
label: ".env.production (current folder)",
|
|
12279
12405
|
description: "Production secrets",
|
|
12280
12406
|
score: 20
|
|
@@ -12283,7 +12409,7 @@ function buildSensitivePaths(home, cwd) {
|
|
|
12283
12409
|
}
|
|
12284
12410
|
function isReadable(filePath) {
|
|
12285
12411
|
try {
|
|
12286
|
-
|
|
12412
|
+
fs22.accessSync(filePath, fs22.constants.R_OK);
|
|
12287
12413
|
return true;
|
|
12288
12414
|
} catch {
|
|
12289
12415
|
return false;
|
|
@@ -12296,13 +12422,13 @@ function scoreLabel(score) {
|
|
|
12296
12422
|
return chalk3.red.bold(`${score}/100 Critical`);
|
|
12297
12423
|
}
|
|
12298
12424
|
function runBlast() {
|
|
12299
|
-
const home =
|
|
12425
|
+
const home = os21.homedir();
|
|
12300
12426
|
const cwd = process.cwd();
|
|
12301
12427
|
const paths = buildSensitivePaths(home, cwd);
|
|
12302
12428
|
let scoreDeduction = 0;
|
|
12303
12429
|
const reachable = [];
|
|
12304
12430
|
for (const p of paths) {
|
|
12305
|
-
if (
|
|
12431
|
+
if (fs22.existsSync(p.full) && isReadable(p.full)) {
|
|
12306
12432
|
reachable.push(p);
|
|
12307
12433
|
scoreDeduction += p.score;
|
|
12308
12434
|
}
|
|
@@ -12320,7 +12446,7 @@ function runBlast() {
|
|
|
12320
12446
|
}
|
|
12321
12447
|
function registerBlastCommand(program2) {
|
|
12322
12448
|
program2.command("blast").description("Map what an AI agent can currently reach on this machine").action(() => {
|
|
12323
|
-
const home =
|
|
12449
|
+
const home = os21.homedir();
|
|
12324
12450
|
const cwd = process.cwd();
|
|
12325
12451
|
const { reachable, envFindings, score } = runBlast();
|
|
12326
12452
|
console.log("");
|
|
@@ -12508,21 +12634,21 @@ var init_scan_json = __esm({
|
|
|
12508
12634
|
});
|
|
12509
12635
|
|
|
12510
12636
|
// src/session-files.ts
|
|
12511
|
-
import * as
|
|
12512
|
-
import * as
|
|
12637
|
+
import * as fs23 from "fs";
|
|
12638
|
+
import * as path25 from "path";
|
|
12513
12639
|
function listSessionFiles(dir, maxDepth = 6) {
|
|
12514
12640
|
const out = [];
|
|
12515
12641
|
const walk = (d, rel, depth) => {
|
|
12516
12642
|
if (depth > maxDepth) return;
|
|
12517
12643
|
let entries;
|
|
12518
12644
|
try {
|
|
12519
|
-
entries =
|
|
12645
|
+
entries = fs23.readdirSync(d, { withFileTypes: true });
|
|
12520
12646
|
} catch {
|
|
12521
12647
|
return;
|
|
12522
12648
|
}
|
|
12523
12649
|
for (const e of entries) {
|
|
12524
|
-
const childRel = rel ?
|
|
12525
|
-
if (e.isDirectory()) walk(
|
|
12650
|
+
const childRel = rel ? path25.join(rel, e.name) : e.name;
|
|
12651
|
+
if (e.isDirectory()) walk(path25.join(d, e.name), childRel, depth + 1);
|
|
12526
12652
|
else if (e.name.endsWith(".jsonl")) out.push(childRel);
|
|
12527
12653
|
}
|
|
12528
12654
|
};
|
|
@@ -12530,7 +12656,7 @@ function listSessionFiles(dir, maxDepth = 6) {
|
|
|
12530
12656
|
return out;
|
|
12531
12657
|
}
|
|
12532
12658
|
function sessionIdOf(relPath) {
|
|
12533
|
-
return
|
|
12659
|
+
return path25.basename(relPath).replace(/\.jsonl$/, "");
|
|
12534
12660
|
}
|
|
12535
12661
|
var init_session_files = __esm({
|
|
12536
12662
|
"src/session-files.ts"() {
|
|
@@ -12539,17 +12665,17 @@ var init_session_files = __esm({
|
|
|
12539
12665
|
});
|
|
12540
12666
|
|
|
12541
12667
|
// src/cli/render/scan-history.ts
|
|
12542
|
-
import
|
|
12543
|
-
import
|
|
12544
|
-
import
|
|
12668
|
+
import fs24 from "fs";
|
|
12669
|
+
import path26 from "path";
|
|
12670
|
+
import os22 from "os";
|
|
12545
12671
|
function defaultHistoryPath() {
|
|
12546
|
-
return
|
|
12672
|
+
return path26.join(os22.homedir(), ".node9", "scan-history.json");
|
|
12547
12673
|
}
|
|
12548
12674
|
function readPreviousScan(opts = {}) {
|
|
12549
12675
|
const filePath = opts.path ?? defaultHistoryPath();
|
|
12550
12676
|
try {
|
|
12551
|
-
if (!
|
|
12552
|
-
const raw =
|
|
12677
|
+
if (!fs24.existsSync(filePath)) return null;
|
|
12678
|
+
const raw = fs24.readFileSync(filePath, "utf8");
|
|
12553
12679
|
const parsed = JSON.parse(raw);
|
|
12554
12680
|
if (!Array.isArray(parsed) || parsed.length === 0) return null;
|
|
12555
12681
|
const last = parsed[parsed.length - 1];
|
|
@@ -12563,11 +12689,11 @@ function appendScanHistory(record, opts = {}) {
|
|
|
12563
12689
|
const filePath = opts.path ?? defaultHistoryPath();
|
|
12564
12690
|
const cap = opts.cap ?? SCAN_HISTORY_CAP;
|
|
12565
12691
|
try {
|
|
12566
|
-
|
|
12692
|
+
fs24.mkdirSync(path26.dirname(filePath), { recursive: true });
|
|
12567
12693
|
let history = [];
|
|
12568
|
-
if (
|
|
12694
|
+
if (fs24.existsSync(filePath)) {
|
|
12569
12695
|
try {
|
|
12570
|
-
const parsed = JSON.parse(
|
|
12696
|
+
const parsed = JSON.parse(fs24.readFileSync(filePath, "utf8"));
|
|
12571
12697
|
if (Array.isArray(parsed)) {
|
|
12572
12698
|
history = parsed.filter(isValidRecord);
|
|
12573
12699
|
}
|
|
@@ -12578,7 +12704,7 @@ function appendScanHistory(record, opts = {}) {
|
|
|
12578
12704
|
if (history.length > cap) {
|
|
12579
12705
|
history = history.slice(history.length - cap);
|
|
12580
12706
|
}
|
|
12581
|
-
|
|
12707
|
+
fs24.writeFileSync(filePath, JSON.stringify(history, null, 2));
|
|
12582
12708
|
} catch (err2) {
|
|
12583
12709
|
process.stderr.write(
|
|
12584
12710
|
`[node9] Warning: could not write scan-history.json: ${err2.message}
|
|
@@ -12608,132 +12734,6 @@ var init_scan_history = __esm({
|
|
|
12608
12734
|
}
|
|
12609
12735
|
});
|
|
12610
12736
|
|
|
12611
|
-
// src/cost-copilot.ts
|
|
12612
|
-
import fs24 from "fs";
|
|
12613
|
-
import os22 from "os";
|
|
12614
|
-
import path26 from "path";
|
|
12615
|
-
function copilotSessionsDir() {
|
|
12616
|
-
return path26.join(os22.homedir(), ".copilot", "session-state");
|
|
12617
|
-
}
|
|
12618
|
-
function safeReaddir3(dir) {
|
|
12619
|
-
try {
|
|
12620
|
-
return fs24.readdirSync(dir);
|
|
12621
|
-
} catch {
|
|
12622
|
-
return [];
|
|
12623
|
-
}
|
|
12624
|
-
}
|
|
12625
|
-
function priceTokens(model, u) {
|
|
12626
|
-
const tuple = pricingFor(model);
|
|
12627
|
-
if (!tuple || !u) return 0;
|
|
12628
|
-
const [pin, pout, pcw, pcr] = tuple;
|
|
12629
|
-
return (u.inputTokens ?? 0) * pin + (u.outputTokens ?? 0) * pout + (u.cacheWriteTokens ?? 0) * pcw + (u.cacheReadTokens ?? 0) * pcr;
|
|
12630
|
-
}
|
|
12631
|
-
function parseCopilotSession(lines) {
|
|
12632
|
-
let sessionId = "";
|
|
12633
|
-
let cwd = "";
|
|
12634
|
-
let startDate = "";
|
|
12635
|
-
let shutdownDate = "";
|
|
12636
|
-
let modelMetrics = null;
|
|
12637
|
-
for (const raw of lines) {
|
|
12638
|
-
if (!raw.trim()) continue;
|
|
12639
|
-
let o;
|
|
12640
|
-
try {
|
|
12641
|
-
o = JSON.parse(raw);
|
|
12642
|
-
} catch {
|
|
12643
|
-
continue;
|
|
12644
|
-
}
|
|
12645
|
-
const d = o.data ?? {};
|
|
12646
|
-
if (o.type === "session.start") {
|
|
12647
|
-
if (typeof d["sessionId"] === "string") sessionId = d["sessionId"];
|
|
12648
|
-
if (typeof d["startTime"] === "string") startDate = d["startTime"];
|
|
12649
|
-
const ctx = d["context"] ?? {};
|
|
12650
|
-
if (typeof ctx["cwd"] === "string") cwd = ctx["cwd"];
|
|
12651
|
-
} else if (o.type === "session.shutdown") {
|
|
12652
|
-
if (d["modelMetrics"] && typeof d["modelMetrics"] === "object") {
|
|
12653
|
-
modelMetrics = d["modelMetrics"];
|
|
12654
|
-
}
|
|
12655
|
-
if (typeof o.timestamp === "string") shutdownDate = o.timestamp;
|
|
12656
|
-
}
|
|
12657
|
-
}
|
|
12658
|
-
if (!modelMetrics) return [];
|
|
12659
|
-
const date = (startDate || shutdownDate).slice(0, 10);
|
|
12660
|
-
if (!date) return [];
|
|
12661
|
-
const rows = [];
|
|
12662
|
-
for (const [rawModel, m] of Object.entries(modelMetrics)) {
|
|
12663
|
-
const u = m.usage ?? {};
|
|
12664
|
-
const inputTokens = u.inputTokens ?? 0;
|
|
12665
|
-
const outputTokens = u.outputTokens ?? 0;
|
|
12666
|
-
const cacheReadTokens = u.cacheReadTokens ?? 0;
|
|
12667
|
-
const cacheWriteTokens = u.cacheWriteTokens ?? 0;
|
|
12668
|
-
if (inputTokens === 0 && outputTokens === 0 && cacheReadTokens === 0 && cacheWriteTokens === 0) {
|
|
12669
|
-
continue;
|
|
12670
|
-
}
|
|
12671
|
-
const model = normalizeModel(rawModel);
|
|
12672
|
-
const costUSD = typeof m.requests?.cost === "number" && m.requests.cost > 0 ? m.requests.cost : priceTokens(rawModel, u);
|
|
12673
|
-
rows.push({
|
|
12674
|
-
date,
|
|
12675
|
-
model,
|
|
12676
|
-
workingDir: cwd,
|
|
12677
|
-
runId: sessionId,
|
|
12678
|
-
costUSD,
|
|
12679
|
-
inputTokens,
|
|
12680
|
-
outputTokens,
|
|
12681
|
-
cacheReadTokens,
|
|
12682
|
-
cacheWriteTokens
|
|
12683
|
-
});
|
|
12684
|
-
}
|
|
12685
|
-
return rows;
|
|
12686
|
-
}
|
|
12687
|
-
var copilotSource;
|
|
12688
|
-
var init_cost_copilot = __esm({
|
|
12689
|
-
"src/cost-copilot.ts"() {
|
|
12690
|
-
"use strict";
|
|
12691
|
-
init_litellm();
|
|
12692
|
-
copilotSource = {
|
|
12693
|
-
id: "copilot",
|
|
12694
|
-
available() {
|
|
12695
|
-
try {
|
|
12696
|
-
return fs24.existsSync(copilotSessionsDir());
|
|
12697
|
-
} catch {
|
|
12698
|
-
return false;
|
|
12699
|
-
}
|
|
12700
|
-
},
|
|
12701
|
-
collect(sinceMs) {
|
|
12702
|
-
const base = copilotSessionsDir();
|
|
12703
|
-
const combined = /* @__PURE__ */ new Map();
|
|
12704
|
-
for (const sid of safeReaddir3(base)) {
|
|
12705
|
-
const file = path26.join(base, sid, "events.jsonl");
|
|
12706
|
-
try {
|
|
12707
|
-
if (sinceMs !== void 0 && fs24.statSync(file).mtimeMs < sinceMs) continue;
|
|
12708
|
-
} catch {
|
|
12709
|
-
continue;
|
|
12710
|
-
}
|
|
12711
|
-
let content;
|
|
12712
|
-
try {
|
|
12713
|
-
content = fs24.readFileSync(file, "utf8");
|
|
12714
|
-
} catch {
|
|
12715
|
-
continue;
|
|
12716
|
-
}
|
|
12717
|
-
for (const e of parseCopilotSession(content.split("\n"))) {
|
|
12718
|
-
const key = `${e.date}::${e.model}::${e.workingDir ?? ""}::${e.runId ?? ""}`;
|
|
12719
|
-
const prev = combined.get(key);
|
|
12720
|
-
if (prev) {
|
|
12721
|
-
prev.costUSD += e.costUSD;
|
|
12722
|
-
prev.inputTokens += e.inputTokens;
|
|
12723
|
-
prev.outputTokens += e.outputTokens;
|
|
12724
|
-
prev.cacheReadTokens += e.cacheReadTokens;
|
|
12725
|
-
prev.cacheWriteTokens += e.cacheWriteTokens;
|
|
12726
|
-
} else {
|
|
12727
|
-
combined.set(key, { ...e });
|
|
12728
|
-
}
|
|
12729
|
-
}
|
|
12730
|
-
}
|
|
12731
|
-
return [...combined.values()];
|
|
12732
|
-
}
|
|
12733
|
-
};
|
|
12734
|
-
}
|
|
12735
|
-
});
|
|
12736
|
-
|
|
12737
12737
|
// src/costSync.ts
|
|
12738
12738
|
import fs25 from "fs";
|
|
12739
12739
|
import path27 from "path";
|
|
@@ -14780,6 +14780,10 @@ function scanCopilotHistory(startDate, onProgress, onLine) {
|
|
|
14780
14780
|
let projLabel = sessionId.slice(0, 8);
|
|
14781
14781
|
const sessionCalls = [];
|
|
14782
14782
|
result.sessions++;
|
|
14783
|
+
for (const row of parseCopilotSession(raw.split("\n"))) {
|
|
14784
|
+
if (startDate && row.date && new Date(row.date) < startDate) continue;
|
|
14785
|
+
result.totalCostUSD += row.costUSD;
|
|
14786
|
+
}
|
|
14783
14787
|
for (const line of raw.split("\n")) {
|
|
14784
14788
|
if (!line.trim()) continue;
|
|
14785
14789
|
onLine?.();
|
|
@@ -15156,11 +15160,14 @@ function scanCodexHistory(startDate, onProgress, onLine) {
|
|
|
15156
15160
|
}
|
|
15157
15161
|
}
|
|
15158
15162
|
}
|
|
15159
|
-
|
|
15160
|
-
|
|
15161
|
-
|
|
15162
|
-
|
|
15163
|
-
|
|
15163
|
+
const withinWindow = !startDate || startTime !== "" && new Date(startTime) >= startDate;
|
|
15164
|
+
if (withinWindow) {
|
|
15165
|
+
result.totalCostUSD += codexSessionCost(model, {
|
|
15166
|
+
input: lastTotalInput,
|
|
15167
|
+
cached: lastTotalCached,
|
|
15168
|
+
output: lastTotalOutput
|
|
15169
|
+
});
|
|
15170
|
+
}
|
|
15164
15171
|
result.loopFindings.push(...detectLoops(sessionCalls, projLabel, sessionId, "codex"));
|
|
15165
15172
|
}
|
|
15166
15173
|
return result;
|
|
@@ -16253,6 +16260,7 @@ var init_scan = __esm({
|
|
|
16253
16260
|
init_litellm();
|
|
16254
16261
|
init_cost_gemini();
|
|
16255
16262
|
init_cost_codex();
|
|
16263
|
+
init_cost_copilot();
|
|
16256
16264
|
init_hook_payload();
|
|
16257
16265
|
init_dist();
|
|
16258
16266
|
init_scan_summary();
|
package/dist/dashboard.mjs
CHANGED
|
@@ -5829,11 +5829,14 @@ function scanCodexHistory(startDate, onProgress, onLine) {
|
|
|
5829
5829
|
}
|
|
5830
5830
|
}
|
|
5831
5831
|
}
|
|
5832
|
-
|
|
5833
|
-
|
|
5834
|
-
|
|
5835
|
-
|
|
5836
|
-
|
|
5832
|
+
const withinWindow = !startDate || startTime !== "" && new Date(startTime) >= startDate;
|
|
5833
|
+
if (withinWindow) {
|
|
5834
|
+
result.totalCostUSD += codexSessionCost(model, {
|
|
5835
|
+
input: lastTotalInput,
|
|
5836
|
+
cached: lastTotalCached,
|
|
5837
|
+
output: lastTotalOutput
|
|
5838
|
+
});
|
|
5839
|
+
}
|
|
5837
5840
|
result.loopFindings.push(...detectLoops(sessionCalls, projLabel, sessionId, "codex"));
|
|
5838
5841
|
}
|
|
5839
5842
|
return result;
|
|
@@ -5850,6 +5853,7 @@ var init_scan = __esm({
|
|
|
5850
5853
|
init_litellm();
|
|
5851
5854
|
init_cost_gemini();
|
|
5852
5855
|
init_cost_codex();
|
|
5856
|
+
init_cost_copilot();
|
|
5853
5857
|
init_hook_payload();
|
|
5854
5858
|
init_dist();
|
|
5855
5859
|
init_scan_summary();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@node9/proxy",
|
|
3
|
-
"version": "2.7.
|
|
3
|
+
"version": "2.7.4",
|
|
4
4
|
"description": "The Sudo Command for AI Agents. Execution Security for Claude Code, Codex, Gemini, Cursor, Opencode, Pi, and any MCP server.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|