@node9/proxy 1.10.0 → 1.10.2
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 +550 -281
- package/dist/cli.mjs +546 -277
- package/dist/index.js +61 -20
- package/dist/index.mjs +61 -20
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -168,8 +168,8 @@ function sanitizeConfig(raw) {
|
|
|
168
168
|
}
|
|
169
169
|
}
|
|
170
170
|
const lines = result.error.issues.map((issue) => {
|
|
171
|
-
const
|
|
172
|
-
return ` \u2022 ${
|
|
171
|
+
const path35 = issue.path.length > 0 ? issue.path.join(".") : "root";
|
|
172
|
+
return ` \u2022 ${path35}: ${issue.message}`;
|
|
173
173
|
});
|
|
174
174
|
return {
|
|
175
175
|
sanitized,
|
|
@@ -253,7 +253,8 @@ var init_config_schema = __esm({
|
|
|
253
253
|
slackEnabled: import_zod.z.boolean().optional(),
|
|
254
254
|
enableTrustSessions: import_zod.z.boolean().optional(),
|
|
255
255
|
allowGlobalPause: import_zod.z.boolean().optional(),
|
|
256
|
-
auditHashArgs: import_zod.z.boolean().optional()
|
|
256
|
+
auditHashArgs: import_zod.z.boolean().optional(),
|
|
257
|
+
agentPolicy: import_zod.z.enum(["require_approval", "block_on_rules"]).optional()
|
|
257
258
|
}).optional(),
|
|
258
259
|
policy: import_zod.z.object({
|
|
259
260
|
sandboxPaths: import_zod.z.array(import_zod.z.string()).optional(),
|
|
@@ -1726,9 +1727,9 @@ function matchesPattern(text, patterns) {
|
|
|
1726
1727
|
const withoutDotSlash = text.replace(/^\.\//, "");
|
|
1727
1728
|
return isMatch(withoutDotSlash) || isMatch(`./${withoutDotSlash}`);
|
|
1728
1729
|
}
|
|
1729
|
-
function getNestedValue(obj,
|
|
1730
|
+
function getNestedValue(obj, path35) {
|
|
1730
1731
|
if (!obj || typeof obj !== "object") return null;
|
|
1731
|
-
return
|
|
1732
|
+
return path35.split(".").reduce((prev, curr) => prev?.[curr], obj);
|
|
1732
1733
|
}
|
|
1733
1734
|
function shouldSnapshot(toolName, args, config) {
|
|
1734
1735
|
if (!config.settings.enableUndo) return false;
|
|
@@ -2456,19 +2457,44 @@ function getInternalToken() {
|
|
|
2456
2457
|
function isDaemonRunning() {
|
|
2457
2458
|
const pidFile = import_path10.default.join(import_os8.default.homedir(), ".node9", "daemon.pid");
|
|
2458
2459
|
if (import_fs9.default.existsSync(pidFile)) {
|
|
2460
|
+
let pid;
|
|
2461
|
+
let port;
|
|
2459
2462
|
try {
|
|
2460
|
-
const
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
return true;
|
|
2463
|
+
const data = JSON.parse(import_fs9.default.readFileSync(pidFile, "utf-8"));
|
|
2464
|
+
pid = data.pid;
|
|
2465
|
+
port = data.port;
|
|
2464
2466
|
} catch {
|
|
2465
2467
|
return false;
|
|
2466
2468
|
}
|
|
2469
|
+
if (port !== DAEMON_PORT) {
|
|
2470
|
+
return false;
|
|
2471
|
+
}
|
|
2472
|
+
const MAX_PID = 4194304;
|
|
2473
|
+
if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0 || pid > MAX_PID) {
|
|
2474
|
+
return false;
|
|
2475
|
+
}
|
|
2476
|
+
try {
|
|
2477
|
+
process.kill(pid, 0);
|
|
2478
|
+
} catch (err2) {
|
|
2479
|
+
if (err2 instanceof Error && "code" in err2 && err2.code === "ESRCH") {
|
|
2480
|
+
try {
|
|
2481
|
+
import_fs9.default.unlinkSync(pidFile);
|
|
2482
|
+
} catch {
|
|
2483
|
+
}
|
|
2484
|
+
}
|
|
2485
|
+
return false;
|
|
2486
|
+
}
|
|
2487
|
+
const r = (0, import_child_process.spawnSync)("ss", ["-Htnp", `sport = :${DAEMON_PORT}`], {
|
|
2488
|
+
encoding: "utf8",
|
|
2489
|
+
timeout: 300
|
|
2490
|
+
});
|
|
2491
|
+
if (r.status === 0 && (r.stdout ?? "").includes(`:${DAEMON_PORT}`)) return true;
|
|
2492
|
+
return false;
|
|
2467
2493
|
}
|
|
2468
2494
|
try {
|
|
2469
2495
|
const r = (0, import_child_process.spawnSync)("ss", ["-Htnp", `sport = :${DAEMON_PORT}`], {
|
|
2470
2496
|
encoding: "utf8",
|
|
2471
|
-
timeout:
|
|
2497
|
+
timeout: 300
|
|
2472
2498
|
});
|
|
2473
2499
|
return r.status === 0 && (r.stdout ?? "").includes(`:${DAEMON_PORT}`);
|
|
2474
2500
|
} catch {
|
|
@@ -2811,11 +2837,12 @@ ${smartTruncate(str, 500)}`
|
|
|
2811
2837
|
function escapePango(text) {
|
|
2812
2838
|
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
2813
2839
|
}
|
|
2814
|
-
function buildPlainMessage(toolName, formattedArgs, agent, explainableLabel, locked, allowCount = 1) {
|
|
2840
|
+
function buildPlainMessage(toolName, formattedArgs, agent, explainableLabel, locked, allowCount = 1, ruleDescription) {
|
|
2815
2841
|
const lines = [];
|
|
2816
2842
|
if (locked) lines.push("\u26A0\uFE0F LOCKED BY ADMIN POLICY\n");
|
|
2817
2843
|
lines.push(`\u{1F916} ${agent || "AI Agent"} | \u{1F527} ${toolName}`);
|
|
2818
2844
|
lines.push(`\u{1F6E1}\uFE0F ${explainableLabel || "Security Policy"}`);
|
|
2845
|
+
if (ruleDescription) lines.push(`\u2139 ${ruleDescription}`);
|
|
2819
2846
|
lines.push("");
|
|
2820
2847
|
lines.push(formattedArgs);
|
|
2821
2848
|
if (allowCount >= 3) {
|
|
@@ -2828,7 +2855,7 @@ function buildPlainMessage(toolName, formattedArgs, agent, explainableLabel, loc
|
|
|
2828
2855
|
}
|
|
2829
2856
|
return lines.join("\n");
|
|
2830
2857
|
}
|
|
2831
|
-
function buildPangoMessage(toolName, formattedArgs, agent, explainableLabel, locked, allowCount = 1) {
|
|
2858
|
+
function buildPangoMessage(toolName, formattedArgs, agent, explainableLabel, locked, allowCount = 1, ruleDescription) {
|
|
2832
2859
|
const lines = [];
|
|
2833
2860
|
if (locked) {
|
|
2834
2861
|
lines.push('<span foreground="red" weight="bold">\u26A0\uFE0F LOCKED BY ADMIN POLICY</span>');
|
|
@@ -2838,6 +2865,7 @@ function buildPangoMessage(toolName, formattedArgs, agent, explainableLabel, loc
|
|
|
2838
2865
|
`<b>\u{1F916} ${escapePango(agent || "AI Agent")}</b> | <b>\u{1F527} <tt>${escapePango(toolName)}</tt></b>`
|
|
2839
2866
|
);
|
|
2840
2867
|
lines.push(`<i>\u{1F6E1}\uFE0F ${escapePango(explainableLabel || "Security Policy")}</i>`);
|
|
2868
|
+
if (ruleDescription) lines.push(`<i>\u2139 ${escapePango(ruleDescription)}</i>`);
|
|
2841
2869
|
lines.push("");
|
|
2842
2870
|
lines.push(`<tt>${escapePango(formattedArgs)}</tt>`);
|
|
2843
2871
|
if (allowCount >= 3) {
|
|
@@ -2854,7 +2882,7 @@ function buildPangoMessage(toolName, formattedArgs, agent, explainableLabel, loc
|
|
|
2854
2882
|
}
|
|
2855
2883
|
return lines.join("\n");
|
|
2856
2884
|
}
|
|
2857
|
-
async function askNativePopup(toolName, args, agent, explainableLabel, locked = false, signal, matchedField, matchedWord, allowCount = 1) {
|
|
2885
|
+
async function askNativePopup(toolName, args, agent, explainableLabel, locked = false, signal, matchedField, matchedWord, allowCount = 1, ruleDescription) {
|
|
2858
2886
|
if (isTestEnv()) return "deny";
|
|
2859
2887
|
const { message: formattedArgs, intent } = formatArgs(args, matchedField, matchedWord);
|
|
2860
2888
|
const intentLabel = intent === "EDIT" ? "Code Edit" : "Action Approval";
|
|
@@ -2865,7 +2893,8 @@ async function askNativePopup(toolName, args, agent, explainableLabel, locked =
|
|
|
2865
2893
|
agent,
|
|
2866
2894
|
explainableLabel,
|
|
2867
2895
|
locked,
|
|
2868
|
-
allowCount
|
|
2896
|
+
allowCount,
|
|
2897
|
+
ruleDescription
|
|
2869
2898
|
);
|
|
2870
2899
|
return new Promise((resolve) => {
|
|
2871
2900
|
let childProcess = null;
|
|
@@ -2899,7 +2928,8 @@ end run`;
|
|
|
2899
2928
|
agent,
|
|
2900
2929
|
explainableLabel,
|
|
2901
2930
|
locked,
|
|
2902
|
-
allowCount
|
|
2931
|
+
allowCount,
|
|
2932
|
+
ruleDescription
|
|
2903
2933
|
);
|
|
2904
2934
|
const argsList = [
|
|
2905
2935
|
locked ? "--info" : "--question",
|
|
@@ -2972,7 +3002,7 @@ function auditLocalAllow(toolName, args, checkedBy, creds, meta) {
|
|
|
2972
3002
|
}).catch(() => {
|
|
2973
3003
|
});
|
|
2974
3004
|
}
|
|
2975
|
-
async function initNode9SaaS(toolName, args, creds, meta, riskMetadata) {
|
|
3005
|
+
async function initNode9SaaS(toolName, args, creds, meta, riskMetadata, agentPolicy, forceReview) {
|
|
2976
3006
|
const controller = new AbortController();
|
|
2977
3007
|
const timeout = setTimeout(() => controller.abort(), 1e4);
|
|
2978
3008
|
if (!creds.apiKey) throw new Error("Node9 API Key is missing");
|
|
@@ -3017,7 +3047,9 @@ async function initNode9SaaS(toolName, args, creds, meta, riskMetadata) {
|
|
|
3017
3047
|
platform: import_os9.default.platform()
|
|
3018
3048
|
},
|
|
3019
3049
|
...riskMetadata && { riskMetadata },
|
|
3020
|
-
...ciContext && { ciContext }
|
|
3050
|
+
...ciContext && { ciContext },
|
|
3051
|
+
...agentPolicy && { policy: agentPolicy },
|
|
3052
|
+
...forceReview && { forceReview: true }
|
|
3021
3053
|
}),
|
|
3022
3054
|
signal: controller.signal
|
|
3023
3055
|
});
|
|
@@ -3414,6 +3446,7 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
3414
3446
|
policyMatchedWord,
|
|
3415
3447
|
policyResult.ruleName
|
|
3416
3448
|
);
|
|
3449
|
+
if (policyRuleDescription) riskMetadata.ruleDescription = policyRuleDescription.slice(0, 200);
|
|
3417
3450
|
const persistent = policyResult.ruleName ? null : getPersistentDecision(toolName);
|
|
3418
3451
|
if (persistent === "allow") {
|
|
3419
3452
|
if (approvers.cloud && creds?.apiKey)
|
|
@@ -3448,9 +3481,18 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
3448
3481
|
}
|
|
3449
3482
|
let cloudRequestId = null;
|
|
3450
3483
|
const cloudEnforced = approvers.cloud && !!creds?.apiKey;
|
|
3451
|
-
|
|
3484
|
+
const forceReview = localSmartRuleMatched === true || options?.localSmartRuleMatched === true || void 0;
|
|
3485
|
+
if (cloudEnforced) {
|
|
3452
3486
|
try {
|
|
3453
|
-
const initResult = await initNode9SaaS(
|
|
3487
|
+
const initResult = await initNode9SaaS(
|
|
3488
|
+
toolName,
|
|
3489
|
+
args,
|
|
3490
|
+
creds,
|
|
3491
|
+
meta,
|
|
3492
|
+
riskMetadata,
|
|
3493
|
+
config.settings.agentPolicy,
|
|
3494
|
+
forceReview
|
|
3495
|
+
);
|
|
3454
3496
|
if (!initResult.pending) {
|
|
3455
3497
|
if (initResult.shadowMode) {
|
|
3456
3498
|
return { approved: true, checkedBy: "cloud" };
|
|
@@ -3465,9 +3507,7 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
3465
3507
|
};
|
|
3466
3508
|
}
|
|
3467
3509
|
}
|
|
3468
|
-
if (
|
|
3469
|
-
cloudRequestId = initResult.requestId || null;
|
|
3470
|
-
}
|
|
3510
|
+
if (initResult.pending) cloudRequestId = initResult.requestId || null;
|
|
3471
3511
|
if (!taintWarning) explainableLabel = "Organization Policy (SaaS)";
|
|
3472
3512
|
} catch {
|
|
3473
3513
|
}
|
|
@@ -3524,7 +3564,7 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
3524
3564
|
}
|
|
3525
3565
|
}
|
|
3526
3566
|
}
|
|
3527
|
-
if (cloudEnforced && cloudRequestId
|
|
3567
|
+
if (cloudEnforced && cloudRequestId) {
|
|
3528
3568
|
racePromises.push(
|
|
3529
3569
|
(async () => {
|
|
3530
3570
|
try {
|
|
@@ -3556,7 +3596,8 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
3556
3596
|
signal,
|
|
3557
3597
|
policyMatchedField,
|
|
3558
3598
|
policyMatchedWord,
|
|
3559
|
-
daemonAllowCount
|
|
3599
|
+
daemonAllowCount,
|
|
3600
|
+
riskMetadata?.ruleDescription
|
|
3560
3601
|
);
|
|
3561
3602
|
if (decision === "always_allow") {
|
|
3562
3603
|
writeTrustSession(toolName, 36e5);
|
|
@@ -6059,8 +6100,175 @@ var init_patch = __esm({
|
|
|
6059
6100
|
}
|
|
6060
6101
|
});
|
|
6061
6102
|
|
|
6103
|
+
// src/costSync.ts
|
|
6104
|
+
function normalizeModel(raw) {
|
|
6105
|
+
return raw.replace(/-\d{8}$/, "");
|
|
6106
|
+
}
|
|
6107
|
+
function pricingFor(model) {
|
|
6108
|
+
const norm = normalizeModel(model);
|
|
6109
|
+
if (PRICING[norm]) return PRICING[norm];
|
|
6110
|
+
let best = null;
|
|
6111
|
+
for (const key of Object.keys(PRICING)) {
|
|
6112
|
+
if (norm.startsWith(key) && (best === null || key.length > best.length)) best = key;
|
|
6113
|
+
}
|
|
6114
|
+
return best ? PRICING[best] : null;
|
|
6115
|
+
}
|
|
6116
|
+
function parseJSONLFile(filePath) {
|
|
6117
|
+
let content;
|
|
6118
|
+
try {
|
|
6119
|
+
content = import_fs16.default.readFileSync(filePath, "utf8");
|
|
6120
|
+
} catch {
|
|
6121
|
+
return /* @__PURE__ */ new Map();
|
|
6122
|
+
}
|
|
6123
|
+
const daily = /* @__PURE__ */ new Map();
|
|
6124
|
+
for (const line of content.split("\n")) {
|
|
6125
|
+
if (!line.trim()) continue;
|
|
6126
|
+
let row;
|
|
6127
|
+
try {
|
|
6128
|
+
row = JSON.parse(line);
|
|
6129
|
+
} catch {
|
|
6130
|
+
continue;
|
|
6131
|
+
}
|
|
6132
|
+
if (row["type"] !== "assistant") continue;
|
|
6133
|
+
const msg = row["message"];
|
|
6134
|
+
if (!msg?.["usage"] || typeof msg["model"] !== "string") continue;
|
|
6135
|
+
const usage = msg["usage"];
|
|
6136
|
+
const model = msg["model"];
|
|
6137
|
+
const timestamp = row["timestamp"];
|
|
6138
|
+
if (typeof timestamp !== "string" || timestamp.length < 10) continue;
|
|
6139
|
+
const date = timestamp.slice(0, 10);
|
|
6140
|
+
const p = pricingFor(model);
|
|
6141
|
+
if (!p) continue;
|
|
6142
|
+
const inp = Number(usage["input_tokens"] ?? 0);
|
|
6143
|
+
const out = Number(usage["output_tokens"] ?? 0);
|
|
6144
|
+
const cw = Number(usage["cache_creation_input_tokens"] ?? 0);
|
|
6145
|
+
const cr = Number(usage["cache_read_input_tokens"] ?? 0);
|
|
6146
|
+
const cost = inp * p[0] + out * p[1] + cw * p[2] + cr * p[3];
|
|
6147
|
+
const norm = normalizeModel(model);
|
|
6148
|
+
const key = `${date}::${norm}`;
|
|
6149
|
+
const prev = daily.get(key);
|
|
6150
|
+
if (prev) {
|
|
6151
|
+
prev.costUSD += cost;
|
|
6152
|
+
prev.inputTokens += inp;
|
|
6153
|
+
prev.outputTokens += out;
|
|
6154
|
+
prev.cacheWriteTokens += cw;
|
|
6155
|
+
prev.cacheReadTokens += cr;
|
|
6156
|
+
} else {
|
|
6157
|
+
daily.set(key, {
|
|
6158
|
+
date,
|
|
6159
|
+
model: norm,
|
|
6160
|
+
costUSD: cost,
|
|
6161
|
+
inputTokens: inp,
|
|
6162
|
+
outputTokens: out,
|
|
6163
|
+
cacheWriteTokens: cw,
|
|
6164
|
+
cacheReadTokens: cr
|
|
6165
|
+
});
|
|
6166
|
+
}
|
|
6167
|
+
}
|
|
6168
|
+
return daily;
|
|
6169
|
+
}
|
|
6170
|
+
function collectEntries() {
|
|
6171
|
+
const projectsDir = import_path19.default.join(import_os14.default.homedir(), ".claude", "projects");
|
|
6172
|
+
if (!import_fs16.default.existsSync(projectsDir)) return [];
|
|
6173
|
+
const combined = /* @__PURE__ */ new Map();
|
|
6174
|
+
let dirs;
|
|
6175
|
+
try {
|
|
6176
|
+
dirs = import_fs16.default.readdirSync(projectsDir);
|
|
6177
|
+
} catch {
|
|
6178
|
+
return [];
|
|
6179
|
+
}
|
|
6180
|
+
for (const dir of dirs) {
|
|
6181
|
+
const dirPath = import_path19.default.join(projectsDir, dir);
|
|
6182
|
+
try {
|
|
6183
|
+
if (!import_fs16.default.statSync(dirPath).isDirectory()) continue;
|
|
6184
|
+
} catch {
|
|
6185
|
+
continue;
|
|
6186
|
+
}
|
|
6187
|
+
let files;
|
|
6188
|
+
try {
|
|
6189
|
+
files = import_fs16.default.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
|
|
6190
|
+
} catch {
|
|
6191
|
+
continue;
|
|
6192
|
+
}
|
|
6193
|
+
for (const file of files) {
|
|
6194
|
+
const entries = parseJSONLFile(import_path19.default.join(dirPath, file));
|
|
6195
|
+
for (const [key, e] of entries) {
|
|
6196
|
+
const prev = combined.get(key);
|
|
6197
|
+
if (prev) {
|
|
6198
|
+
prev.costUSD += e.costUSD;
|
|
6199
|
+
prev.inputTokens += e.inputTokens;
|
|
6200
|
+
prev.outputTokens += e.outputTokens;
|
|
6201
|
+
prev.cacheWriteTokens += e.cacheWriteTokens;
|
|
6202
|
+
prev.cacheReadTokens += e.cacheReadTokens;
|
|
6203
|
+
} else {
|
|
6204
|
+
combined.set(key, { ...e });
|
|
6205
|
+
}
|
|
6206
|
+
}
|
|
6207
|
+
}
|
|
6208
|
+
}
|
|
6209
|
+
return [...combined.values()];
|
|
6210
|
+
}
|
|
6211
|
+
async function syncCost() {
|
|
6212
|
+
const creds = getCredentials();
|
|
6213
|
+
if (!creds?.apiKey || !creds?.apiUrl) return;
|
|
6214
|
+
const entries = collectEntries();
|
|
6215
|
+
if (entries.length === 0) return;
|
|
6216
|
+
let username = "unknown";
|
|
6217
|
+
try {
|
|
6218
|
+
username = import_os14.default.userInfo().username;
|
|
6219
|
+
} catch {
|
|
6220
|
+
}
|
|
6221
|
+
const machineId = `${import_os14.default.hostname()}:${username}`;
|
|
6222
|
+
try {
|
|
6223
|
+
const res = await fetch(`${creds.apiUrl}/cost-sync`, {
|
|
6224
|
+
method: "POST",
|
|
6225
|
+
headers: { "Content-Type": "application/json", Authorization: `Bearer ${creds.apiKey}` },
|
|
6226
|
+
body: JSON.stringify({ machineId, entries }),
|
|
6227
|
+
signal: AbortSignal.timeout(15e3)
|
|
6228
|
+
});
|
|
6229
|
+
if (!res.ok) {
|
|
6230
|
+
import_fs16.default.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] HTTP ${res.status}
|
|
6231
|
+
`);
|
|
6232
|
+
}
|
|
6233
|
+
} catch (err2) {
|
|
6234
|
+
import_fs16.default.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] ${err2.message}
|
|
6235
|
+
`);
|
|
6236
|
+
}
|
|
6237
|
+
}
|
|
6238
|
+
function startCostSync() {
|
|
6239
|
+
syncCost().catch(() => {
|
|
6240
|
+
});
|
|
6241
|
+
const timer = setInterval(() => {
|
|
6242
|
+
syncCost().catch(() => {
|
|
6243
|
+
});
|
|
6244
|
+
}, SYNC_INTERVAL_MS);
|
|
6245
|
+
timer.unref();
|
|
6246
|
+
}
|
|
6247
|
+
var import_fs16, import_path19, import_os14, SYNC_INTERVAL_MS, PRICING;
|
|
6248
|
+
var init_costSync = __esm({
|
|
6249
|
+
"src/costSync.ts"() {
|
|
6250
|
+
"use strict";
|
|
6251
|
+
import_fs16 = __toESM(require("fs"));
|
|
6252
|
+
import_path19 = __toESM(require("path"));
|
|
6253
|
+
import_os14 = __toESM(require("os"));
|
|
6254
|
+
init_config();
|
|
6255
|
+
init_audit();
|
|
6256
|
+
SYNC_INTERVAL_MS = 10 * 60 * 1e3;
|
|
6257
|
+
PRICING = {
|
|
6258
|
+
"claude-opus-4": [5e-6, 25e-6, 625e-8, 5e-7],
|
|
6259
|
+
"claude-sonnet-4": [3e-6, 15e-6, 375e-8, 3e-7],
|
|
6260
|
+
"claude-haiku-4": [8e-7, 4e-6, 1e-6, 8e-8],
|
|
6261
|
+
"claude-3-7-sonnet": [3e-6, 15e-6, 375e-8, 3e-7],
|
|
6262
|
+
"claude-3-5-sonnet": [3e-6, 15e-6, 375e-8, 3e-7],
|
|
6263
|
+
"claude-3-5-haiku": [8e-7, 4e-6, 1e-6, 8e-8],
|
|
6264
|
+
"claude-3-haiku": [25e-8, 125e-8, 3e-7, 3e-8]
|
|
6265
|
+
};
|
|
6266
|
+
}
|
|
6267
|
+
});
|
|
6268
|
+
|
|
6062
6269
|
// src/daemon/server.ts
|
|
6063
6270
|
function startDaemon() {
|
|
6271
|
+
startCostSync();
|
|
6064
6272
|
loadInsightCounts();
|
|
6065
6273
|
const csrfToken = (0, import_crypto7.randomUUID)();
|
|
6066
6274
|
const internalToken = (0, import_crypto7.randomUUID)();
|
|
@@ -6076,7 +6284,7 @@ function startDaemon() {
|
|
|
6076
6284
|
idleTimer = setTimeout(() => {
|
|
6077
6285
|
if (autoStarted) {
|
|
6078
6286
|
try {
|
|
6079
|
-
|
|
6287
|
+
import_fs17.default.unlinkSync(DAEMON_PID_FILE);
|
|
6080
6288
|
} catch {
|
|
6081
6289
|
}
|
|
6082
6290
|
}
|
|
@@ -6239,7 +6447,7 @@ data: ${JSON.stringify(item.data)}
|
|
|
6239
6447
|
status: "pending"
|
|
6240
6448
|
});
|
|
6241
6449
|
}
|
|
6242
|
-
const projectCwd = typeof cwd === "string" &&
|
|
6450
|
+
const projectCwd = typeof cwd === "string" && import_path20.default.isAbsolute(cwd) ? cwd : void 0;
|
|
6243
6451
|
const projectConfig = getConfig(projectCwd);
|
|
6244
6452
|
const browserEnabled = projectConfig.settings.approvers?.browser !== false;
|
|
6245
6453
|
const terminalEnabled = projectConfig.settings.approvers?.terminal !== false;
|
|
@@ -6629,8 +6837,8 @@ data: ${JSON.stringify(item.data)}
|
|
|
6629
6837
|
const body = await readBody(req);
|
|
6630
6838
|
const data = body ? JSON.parse(body) : {};
|
|
6631
6839
|
const configPath = data.configPath ?? GLOBAL_CONFIG_PATH;
|
|
6632
|
-
const node9Dir =
|
|
6633
|
-
if (!
|
|
6840
|
+
const node9Dir = import_path20.default.dirname(GLOBAL_CONFIG_PATH);
|
|
6841
|
+
if (!import_path20.default.resolve(configPath).startsWith(node9Dir + import_path20.default.sep)) {
|
|
6634
6842
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
6635
6843
|
return res.end(
|
|
6636
6844
|
JSON.stringify({ error: "configPath must be within the node9 config directory" })
|
|
@@ -6741,14 +6949,14 @@ data: ${JSON.stringify(item.data)}
|
|
|
6741
6949
|
server.on("error", (e) => {
|
|
6742
6950
|
if (e.code === "EADDRINUSE") {
|
|
6743
6951
|
try {
|
|
6744
|
-
if (
|
|
6745
|
-
const { pid } = JSON.parse(
|
|
6952
|
+
if (import_fs17.default.existsSync(DAEMON_PID_FILE)) {
|
|
6953
|
+
const { pid } = JSON.parse(import_fs17.default.readFileSync(DAEMON_PID_FILE, "utf-8"));
|
|
6746
6954
|
process.kill(pid, 0);
|
|
6747
6955
|
return process.exit(0);
|
|
6748
6956
|
}
|
|
6749
6957
|
} catch {
|
|
6750
6958
|
try {
|
|
6751
|
-
|
|
6959
|
+
import_fs17.default.unlinkSync(DAEMON_PID_FILE);
|
|
6752
6960
|
} catch {
|
|
6753
6961
|
}
|
|
6754
6962
|
server.listen(DAEMON_PORT, DAEMON_HOST);
|
|
@@ -6807,13 +7015,13 @@ data: ${JSON.stringify(item.data)}
|
|
|
6807
7015
|
}
|
|
6808
7016
|
startActivitySocket();
|
|
6809
7017
|
}
|
|
6810
|
-
var import_http,
|
|
7018
|
+
var import_http, import_fs17, import_path20, import_crypto7, import_child_process4, import_chalk2;
|
|
6811
7019
|
var init_server = __esm({
|
|
6812
7020
|
"src/daemon/server.ts"() {
|
|
6813
7021
|
"use strict";
|
|
6814
7022
|
import_http = __toESM(require("http"));
|
|
6815
|
-
|
|
6816
|
-
|
|
7023
|
+
import_fs17 = __toESM(require("fs"));
|
|
7024
|
+
import_path20 = __toESM(require("path"));
|
|
6817
7025
|
import_crypto7 = require("crypto");
|
|
6818
7026
|
import_child_process4 = require("child_process");
|
|
6819
7027
|
import_chalk2 = __toESM(require("chalk"));
|
|
@@ -6823,29 +7031,30 @@ var init_server = __esm({
|
|
|
6823
7031
|
init_state2();
|
|
6824
7032
|
init_patch();
|
|
6825
7033
|
init_config_schema();
|
|
7034
|
+
init_costSync();
|
|
6826
7035
|
}
|
|
6827
7036
|
});
|
|
6828
7037
|
|
|
6829
7038
|
// src/daemon/index.ts
|
|
6830
7039
|
function stopDaemon() {
|
|
6831
|
-
if (!
|
|
7040
|
+
if (!import_fs18.default.existsSync(DAEMON_PID_FILE)) return console.log(import_chalk3.default.yellow("Not running."));
|
|
6832
7041
|
try {
|
|
6833
|
-
const { pid } = JSON.parse(
|
|
7042
|
+
const { pid } = JSON.parse(import_fs18.default.readFileSync(DAEMON_PID_FILE, "utf-8"));
|
|
6834
7043
|
process.kill(pid, "SIGTERM");
|
|
6835
7044
|
console.log(import_chalk3.default.green("\u2705 Stopped."));
|
|
6836
7045
|
} catch {
|
|
6837
7046
|
console.log(import_chalk3.default.gray("Cleaned up stale PID file."));
|
|
6838
7047
|
} finally {
|
|
6839
7048
|
try {
|
|
6840
|
-
|
|
7049
|
+
import_fs18.default.unlinkSync(DAEMON_PID_FILE);
|
|
6841
7050
|
} catch {
|
|
6842
7051
|
}
|
|
6843
7052
|
}
|
|
6844
7053
|
}
|
|
6845
7054
|
function daemonStatus() {
|
|
6846
|
-
if (
|
|
7055
|
+
if (import_fs18.default.existsSync(DAEMON_PID_FILE)) {
|
|
6847
7056
|
try {
|
|
6848
|
-
const { pid } = JSON.parse(
|
|
7057
|
+
const { pid } = JSON.parse(import_fs18.default.readFileSync(DAEMON_PID_FILE, "utf-8"));
|
|
6849
7058
|
process.kill(pid, 0);
|
|
6850
7059
|
console.log(import_chalk3.default.green("Node9 daemon: running"));
|
|
6851
7060
|
return;
|
|
@@ -6864,11 +7073,11 @@ function daemonStatus() {
|
|
|
6864
7073
|
console.log(import_chalk3.default.yellow("Node9 daemon: not running"));
|
|
6865
7074
|
}
|
|
6866
7075
|
}
|
|
6867
|
-
var
|
|
7076
|
+
var import_fs18, import_chalk3, import_child_process5;
|
|
6868
7077
|
var init_daemon2 = __esm({
|
|
6869
7078
|
"src/daemon/index.ts"() {
|
|
6870
7079
|
"use strict";
|
|
6871
|
-
|
|
7080
|
+
import_fs18 = __toESM(require("fs"));
|
|
6872
7081
|
import_chalk3 = __toESM(require("chalk"));
|
|
6873
7082
|
import_child_process5 = require("child_process");
|
|
6874
7083
|
init_server();
|
|
@@ -6902,7 +7111,7 @@ function formatBase(activity) {
|
|
|
6902
7111
|
const time = new Date(activity.ts).toLocaleTimeString([], { hour12: false });
|
|
6903
7112
|
const icon = getIcon(activity.tool);
|
|
6904
7113
|
const toolName = activity.tool.slice(0, 16).padEnd(16);
|
|
6905
|
-
const argsStr = JSON.stringify(activity.args ?? {}).replace(/\s+/g, " ").replaceAll(
|
|
7114
|
+
const argsStr = JSON.stringify(activity.args ?? {}).replace(/\s+/g, " ").replaceAll(import_os25.default.homedir(), "~");
|
|
6906
7115
|
const argsPreview = argsStr.length > 70 ? argsStr.slice(0, 70) + "\u2026" : argsStr;
|
|
6907
7116
|
return `${import_chalk19.default.gray(time)} ${icon} ${import_chalk19.default.white.bold(toolName)} ${import_chalk19.default.dim(argsPreview)}`;
|
|
6908
7117
|
}
|
|
@@ -6941,9 +7150,9 @@ function renderPending(activity) {
|
|
|
6941
7150
|
}
|
|
6942
7151
|
async function ensureDaemon() {
|
|
6943
7152
|
let pidPort = null;
|
|
6944
|
-
if (
|
|
7153
|
+
if (import_fs29.default.existsSync(PID_FILE)) {
|
|
6945
7154
|
try {
|
|
6946
|
-
const { port } = JSON.parse(
|
|
7155
|
+
const { port } = JSON.parse(import_fs29.default.readFileSync(PID_FILE, "utf-8"));
|
|
6947
7156
|
pidPort = port;
|
|
6948
7157
|
} catch {
|
|
6949
7158
|
console.error(import_chalk19.default.dim("\u26A0\uFE0F Could not read PID file; falling back to default port."));
|
|
@@ -7020,6 +7229,9 @@ function buildCardLines(req, localCount = 0) {
|
|
|
7020
7229
|
`${CYAN}\u2551${RESET2} Tool: ${BOLD2}${req.toolName}${RESET2}`,
|
|
7021
7230
|
`${CYAN}\u2551${RESET2} Reason: ${tierLabel} \u2014 ${blockedBy}${RESET2}`
|
|
7022
7231
|
];
|
|
7232
|
+
if (req.riskMetadata?.ruleDescription) {
|
|
7233
|
+
lines.push(`${CYAN}\u2551${RESET2} ${YELLOW}\u2139 ${req.riskMetadata.ruleDescription}${RESET2}`);
|
|
7234
|
+
}
|
|
7023
7235
|
if (req.riskMetadata?.ruleName && blockedBy.includes("Taint")) {
|
|
7024
7236
|
lines.push(`${CYAN}\u2551${RESET2} ${YELLOW}\u26A0 ${req.riskMetadata.ruleName}${RESET2}`);
|
|
7025
7237
|
}
|
|
@@ -7063,9 +7275,9 @@ function buildRecoveryCardLines(req) {
|
|
|
7063
7275
|
];
|
|
7064
7276
|
}
|
|
7065
7277
|
function readApproversFromDisk() {
|
|
7066
|
-
const configPath =
|
|
7278
|
+
const configPath = import_path32.default.join(import_os25.default.homedir(), ".node9", "config.json");
|
|
7067
7279
|
try {
|
|
7068
|
-
const raw = JSON.parse(
|
|
7280
|
+
const raw = JSON.parse(import_fs29.default.readFileSync(configPath, "utf-8"));
|
|
7069
7281
|
const settings = raw.settings ?? {};
|
|
7070
7282
|
return settings.approvers ?? {};
|
|
7071
7283
|
} catch {
|
|
@@ -7081,15 +7293,15 @@ function approverStatusLine() {
|
|
|
7081
7293
|
return `${fmt("native", "native")} ${fmt("browser", "browser")} ${fmt("cloud", "cloud")} ${fmt("terminal", "terminal")}`;
|
|
7082
7294
|
}
|
|
7083
7295
|
function toggleApprover(channel) {
|
|
7084
|
-
const configPath =
|
|
7296
|
+
const configPath = import_path32.default.join(import_os25.default.homedir(), ".node9", "config.json");
|
|
7085
7297
|
try {
|
|
7086
|
-
const raw = JSON.parse(
|
|
7298
|
+
const raw = JSON.parse(import_fs29.default.readFileSync(configPath, "utf-8"));
|
|
7087
7299
|
const settings = raw.settings ?? {};
|
|
7088
7300
|
const approvers = settings.approvers ?? {};
|
|
7089
7301
|
approvers[channel] = approvers[channel] === false;
|
|
7090
7302
|
settings.approvers = approvers;
|
|
7091
7303
|
raw.settings = settings;
|
|
7092
|
-
|
|
7304
|
+
import_fs29.default.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
|
|
7093
7305
|
} catch (err2) {
|
|
7094
7306
|
process.stderr.write(`[node9] toggleApprover failed: ${String(err2)}
|
|
7095
7307
|
`);
|
|
@@ -7259,8 +7471,8 @@ async function startTail(options = {}) {
|
|
|
7259
7471
|
}
|
|
7260
7472
|
postDecisionHttp(req2.id, httpDecision, csrfToken, port, httpOpts).catch((err2) => {
|
|
7261
7473
|
try {
|
|
7262
|
-
|
|
7263
|
-
|
|
7474
|
+
import_fs29.default.appendFileSync(
|
|
7475
|
+
import_path32.default.join(import_os25.default.homedir(), ".node9", "hook-debug.log"),
|
|
7264
7476
|
`[tail] POST /decision failed: ${String(err2)}
|
|
7265
7477
|
`
|
|
7266
7478
|
);
|
|
@@ -7513,21 +7725,21 @@ async function startTail(options = {}) {
|
|
|
7513
7725
|
process.exit(1);
|
|
7514
7726
|
});
|
|
7515
7727
|
}
|
|
7516
|
-
var import_http2, import_chalk19,
|
|
7728
|
+
var import_http2, import_chalk19, import_fs29, import_os25, import_path32, import_readline5, import_child_process14, PID_FILE, ICONS, RESET2, BOLD2, RED, YELLOW, CYAN, GRAY, GREEN, HIDE_CURSOR, SHOW_CURSOR, ERASE_DOWN, pendingShownForId, pendingWrappedLines, DIVIDER;
|
|
7517
7729
|
var init_tail = __esm({
|
|
7518
7730
|
"src/tui/tail.ts"() {
|
|
7519
7731
|
"use strict";
|
|
7520
7732
|
import_http2 = __toESM(require("http"));
|
|
7521
7733
|
import_chalk19 = __toESM(require("chalk"));
|
|
7522
|
-
|
|
7523
|
-
|
|
7524
|
-
|
|
7734
|
+
import_fs29 = __toESM(require("fs"));
|
|
7735
|
+
import_os25 = __toESM(require("os"));
|
|
7736
|
+
import_path32 = __toESM(require("path"));
|
|
7525
7737
|
import_readline5 = __toESM(require("readline"));
|
|
7526
7738
|
import_child_process14 = require("child_process");
|
|
7527
7739
|
init_daemon2();
|
|
7528
7740
|
init_daemon();
|
|
7529
7741
|
init_core();
|
|
7530
|
-
PID_FILE =
|
|
7742
|
+
PID_FILE = import_path32.default.join(import_os25.default.homedir(), ".node9", "daemon.pid");
|
|
7531
7743
|
ICONS = {
|
|
7532
7744
|
bash: "\u{1F4BB}",
|
|
7533
7745
|
shell: "\u{1F4BB}",
|
|
@@ -7642,9 +7854,9 @@ function formatTimeLeft(resetsAt) {
|
|
|
7642
7854
|
return ` (${m}m left)`;
|
|
7643
7855
|
}
|
|
7644
7856
|
function safeReadJson(filePath) {
|
|
7645
|
-
if (!
|
|
7857
|
+
if (!import_fs30.default.existsSync(filePath)) return null;
|
|
7646
7858
|
try {
|
|
7647
|
-
return JSON.parse(
|
|
7859
|
+
return JSON.parse(import_fs30.default.readFileSync(filePath, "utf-8"));
|
|
7648
7860
|
} catch {
|
|
7649
7861
|
return null;
|
|
7650
7862
|
}
|
|
@@ -7665,12 +7877,12 @@ function countHooksInFile(filePath) {
|
|
|
7665
7877
|
return Object.keys(cfg.hooks).length;
|
|
7666
7878
|
}
|
|
7667
7879
|
function countRulesInDir(rulesDir) {
|
|
7668
|
-
if (!
|
|
7880
|
+
if (!import_fs30.default.existsSync(rulesDir)) return 0;
|
|
7669
7881
|
let count = 0;
|
|
7670
7882
|
try {
|
|
7671
|
-
for (const entry of
|
|
7883
|
+
for (const entry of import_fs30.default.readdirSync(rulesDir, { withFileTypes: true })) {
|
|
7672
7884
|
if (entry.isDirectory()) {
|
|
7673
|
-
count += countRulesInDir(
|
|
7885
|
+
count += countRulesInDir(import_path33.default.join(rulesDir, entry.name));
|
|
7674
7886
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
7675
7887
|
count++;
|
|
7676
7888
|
}
|
|
@@ -7681,46 +7893,46 @@ function countRulesInDir(rulesDir) {
|
|
|
7681
7893
|
}
|
|
7682
7894
|
function isSamePath(a, b) {
|
|
7683
7895
|
try {
|
|
7684
|
-
return
|
|
7896
|
+
return import_path33.default.resolve(a) === import_path33.default.resolve(b);
|
|
7685
7897
|
} catch {
|
|
7686
7898
|
return false;
|
|
7687
7899
|
}
|
|
7688
7900
|
}
|
|
7689
7901
|
function countConfigs(cwd) {
|
|
7690
|
-
const homeDir2 =
|
|
7691
|
-
const claudeDir =
|
|
7902
|
+
const homeDir2 = import_os26.default.homedir();
|
|
7903
|
+
const claudeDir = import_path33.default.join(homeDir2, ".claude");
|
|
7692
7904
|
let claudeMdCount = 0;
|
|
7693
7905
|
let rulesCount = 0;
|
|
7694
7906
|
let hooksCount = 0;
|
|
7695
7907
|
const userMcpServers = /* @__PURE__ */ new Set();
|
|
7696
7908
|
const projectMcpServers = /* @__PURE__ */ new Set();
|
|
7697
|
-
if (
|
|
7698
|
-
rulesCount += countRulesInDir(
|
|
7699
|
-
const userSettings =
|
|
7909
|
+
if (import_fs30.default.existsSync(import_path33.default.join(claudeDir, "CLAUDE.md"))) claudeMdCount++;
|
|
7910
|
+
rulesCount += countRulesInDir(import_path33.default.join(claudeDir, "rules"));
|
|
7911
|
+
const userSettings = import_path33.default.join(claudeDir, "settings.json");
|
|
7700
7912
|
for (const name of getMcpServerNames(userSettings)) userMcpServers.add(name);
|
|
7701
7913
|
hooksCount += countHooksInFile(userSettings);
|
|
7702
|
-
const userClaudeJson =
|
|
7914
|
+
const userClaudeJson = import_path33.default.join(homeDir2, ".claude.json");
|
|
7703
7915
|
for (const name of getMcpServerNames(userClaudeJson)) userMcpServers.add(name);
|
|
7704
7916
|
for (const name of getDisabledMcpServers(userClaudeJson, "disabledMcpServers")) {
|
|
7705
7917
|
userMcpServers.delete(name);
|
|
7706
7918
|
}
|
|
7707
7919
|
if (cwd) {
|
|
7708
|
-
if (
|
|
7709
|
-
if (
|
|
7710
|
-
const projectClaudeDir =
|
|
7920
|
+
if (import_fs30.default.existsSync(import_path33.default.join(cwd, "CLAUDE.md"))) claudeMdCount++;
|
|
7921
|
+
if (import_fs30.default.existsSync(import_path33.default.join(cwd, "CLAUDE.local.md"))) claudeMdCount++;
|
|
7922
|
+
const projectClaudeDir = import_path33.default.join(cwd, ".claude");
|
|
7711
7923
|
const overlapsUserScope = isSamePath(projectClaudeDir, claudeDir);
|
|
7712
7924
|
if (!overlapsUserScope) {
|
|
7713
|
-
if (
|
|
7714
|
-
rulesCount += countRulesInDir(
|
|
7715
|
-
const projSettings =
|
|
7925
|
+
if (import_fs30.default.existsSync(import_path33.default.join(projectClaudeDir, "CLAUDE.md"))) claudeMdCount++;
|
|
7926
|
+
rulesCount += countRulesInDir(import_path33.default.join(projectClaudeDir, "rules"));
|
|
7927
|
+
const projSettings = import_path33.default.join(projectClaudeDir, "settings.json");
|
|
7716
7928
|
for (const name of getMcpServerNames(projSettings)) projectMcpServers.add(name);
|
|
7717
7929
|
hooksCount += countHooksInFile(projSettings);
|
|
7718
7930
|
}
|
|
7719
|
-
if (
|
|
7720
|
-
const localSettings =
|
|
7931
|
+
if (import_fs30.default.existsSync(import_path33.default.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
|
|
7932
|
+
const localSettings = import_path33.default.join(projectClaudeDir, "settings.local.json");
|
|
7721
7933
|
for (const name of getMcpServerNames(localSettings)) projectMcpServers.add(name);
|
|
7722
7934
|
hooksCount += countHooksInFile(localSettings);
|
|
7723
|
-
const mcpJsonServers = getMcpServerNames(
|
|
7935
|
+
const mcpJsonServers = getMcpServerNames(import_path33.default.join(cwd, ".mcp.json"));
|
|
7724
7936
|
const disabledMcpJson = getDisabledMcpServers(localSettings, "disabledMcpjsonServers");
|
|
7725
7937
|
for (const name of disabledMcpJson) mcpJsonServers.delete(name);
|
|
7726
7938
|
for (const name of mcpJsonServers) projectMcpServers.add(name);
|
|
@@ -7753,12 +7965,12 @@ function readActiveShieldsHud() {
|
|
|
7753
7965
|
return shieldsCache.value;
|
|
7754
7966
|
}
|
|
7755
7967
|
try {
|
|
7756
|
-
const shieldsPath =
|
|
7757
|
-
if (!
|
|
7968
|
+
const shieldsPath = import_path33.default.join(import_os26.default.homedir(), ".node9", "shields.json");
|
|
7969
|
+
if (!import_fs30.default.existsSync(shieldsPath)) {
|
|
7758
7970
|
shieldsCache = { value: [], ts: now };
|
|
7759
7971
|
return [];
|
|
7760
7972
|
}
|
|
7761
|
-
const parsed = JSON.parse(
|
|
7973
|
+
const parsed = JSON.parse(import_fs30.default.readFileSync(shieldsPath, "utf-8"));
|
|
7762
7974
|
if (!Array.isArray(parsed.active)) {
|
|
7763
7975
|
shieldsCache = { value: [], ts: now };
|
|
7764
7976
|
return [];
|
|
@@ -7860,17 +8072,17 @@ function renderContextLine(stdin) {
|
|
|
7860
8072
|
async function main() {
|
|
7861
8073
|
try {
|
|
7862
8074
|
const [stdin, daemonStatus2] = await Promise.all([readStdin(), queryDaemon()]);
|
|
7863
|
-
if (
|
|
8075
|
+
if (import_fs30.default.existsSync(import_path33.default.join(import_os26.default.homedir(), ".node9", "hud-debug"))) {
|
|
7864
8076
|
try {
|
|
7865
|
-
const logPath =
|
|
8077
|
+
const logPath = import_path33.default.join(import_os26.default.homedir(), ".node9", "hud-debug.log");
|
|
7866
8078
|
const MAX_LOG_SIZE = 10 * 1024 * 1024;
|
|
7867
8079
|
let size = 0;
|
|
7868
8080
|
try {
|
|
7869
|
-
size =
|
|
8081
|
+
size = import_fs30.default.statSync(logPath).size;
|
|
7870
8082
|
} catch {
|
|
7871
8083
|
}
|
|
7872
8084
|
if (size < MAX_LOG_SIZE) {
|
|
7873
|
-
|
|
8085
|
+
import_fs30.default.appendFileSync(
|
|
7874
8086
|
logPath,
|
|
7875
8087
|
JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), stdin }) + "\n"
|
|
7876
8088
|
);
|
|
@@ -7891,11 +8103,11 @@ async function main() {
|
|
|
7891
8103
|
try {
|
|
7892
8104
|
const cwd = stdin.cwd ?? process.cwd();
|
|
7893
8105
|
for (const configPath of [
|
|
7894
|
-
|
|
7895
|
-
|
|
8106
|
+
import_path33.default.join(cwd, "node9.config.json"),
|
|
8107
|
+
import_path33.default.join(import_os26.default.homedir(), ".node9", "config.json")
|
|
7896
8108
|
]) {
|
|
7897
|
-
if (!
|
|
7898
|
-
const cfg = JSON.parse(
|
|
8109
|
+
if (!import_fs30.default.existsSync(configPath)) continue;
|
|
8110
|
+
const cfg = JSON.parse(import_fs30.default.readFileSync(configPath, "utf-8"));
|
|
7899
8111
|
const hud = cfg.settings?.hud;
|
|
7900
8112
|
if (hud && "showEnvironmentCounts" in hud) return hud.showEnvironmentCounts !== false;
|
|
7901
8113
|
}
|
|
@@ -7913,13 +8125,13 @@ async function main() {
|
|
|
7913
8125
|
renderOffline();
|
|
7914
8126
|
}
|
|
7915
8127
|
}
|
|
7916
|
-
var
|
|
8128
|
+
var import_fs30, import_path33, import_os26, import_http3, RESET3, BOLD3, DIM, RED2, GREEN2, YELLOW2, BLUE, MAGENTA, CYAN2, WHITE, BAR_FILLED, BAR_EMPTY, BAR_WIDTH, shieldsCache, SHIELDS_CACHE_TTL_MS;
|
|
7917
8129
|
var init_hud = __esm({
|
|
7918
8130
|
"src/cli/hud.ts"() {
|
|
7919
8131
|
"use strict";
|
|
7920
|
-
|
|
7921
|
-
|
|
7922
|
-
|
|
8132
|
+
import_fs30 = __toESM(require("fs"));
|
|
8133
|
+
import_path33 = __toESM(require("path"));
|
|
8134
|
+
import_os26 = __toESM(require("os"));
|
|
7923
8135
|
import_http3 = __toESM(require("http"));
|
|
7924
8136
|
init_daemon();
|
|
7925
8137
|
RESET3 = "\x1B[0m";
|
|
@@ -8529,9 +8741,9 @@ function teardownHud() {
|
|
|
8529
8741
|
// src/cli.ts
|
|
8530
8742
|
init_daemon2();
|
|
8531
8743
|
var import_chalk20 = __toESM(require("chalk"));
|
|
8532
|
-
var
|
|
8533
|
-
var
|
|
8534
|
-
var
|
|
8744
|
+
var import_fs31 = __toESM(require("fs"));
|
|
8745
|
+
var import_path34 = __toESM(require("path"));
|
|
8746
|
+
var import_os27 = __toESM(require("os"));
|
|
8535
8747
|
var import_prompts2 = require("@inquirer/prompts");
|
|
8536
8748
|
|
|
8537
8749
|
// src/utils/duration.ts
|
|
@@ -8756,10 +8968,10 @@ async function autoStartDaemonAndWait() {
|
|
|
8756
8968
|
|
|
8757
8969
|
// src/cli/commands/check.ts
|
|
8758
8970
|
var import_chalk5 = __toESM(require("chalk"));
|
|
8759
|
-
var
|
|
8971
|
+
var import_fs20 = __toESM(require("fs"));
|
|
8760
8972
|
var import_child_process9 = require("child_process");
|
|
8761
|
-
var
|
|
8762
|
-
var
|
|
8973
|
+
var import_path22 = __toESM(require("path"));
|
|
8974
|
+
var import_os16 = __toESM(require("os"));
|
|
8763
8975
|
init_orchestrator();
|
|
8764
8976
|
init_daemon();
|
|
8765
8977
|
init_config();
|
|
@@ -8768,11 +8980,11 @@ init_policy();
|
|
|
8768
8980
|
// src/undo.ts
|
|
8769
8981
|
var import_child_process8 = require("child_process");
|
|
8770
8982
|
var import_crypto8 = __toESM(require("crypto"));
|
|
8771
|
-
var
|
|
8983
|
+
var import_fs19 = __toESM(require("fs"));
|
|
8772
8984
|
var import_net3 = __toESM(require("net"));
|
|
8773
|
-
var
|
|
8774
|
-
var
|
|
8775
|
-
var ACTIVITY_SOCKET_PATH3 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" :
|
|
8985
|
+
var import_path21 = __toESM(require("path"));
|
|
8986
|
+
var import_os15 = __toESM(require("os"));
|
|
8987
|
+
var ACTIVITY_SOCKET_PATH3 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : import_path21.default.join(import_os15.default.tmpdir(), "node9-activity.sock");
|
|
8776
8988
|
function notifySnapshotTaken(hash, tool, argsSummary, fileCount) {
|
|
8777
8989
|
try {
|
|
8778
8990
|
const payload = JSON.stringify({
|
|
@@ -8792,22 +9004,22 @@ function notifySnapshotTaken(hash, tool, argsSummary, fileCount) {
|
|
|
8792
9004
|
} catch {
|
|
8793
9005
|
}
|
|
8794
9006
|
}
|
|
8795
|
-
var SNAPSHOT_STACK_PATH =
|
|
8796
|
-
var UNDO_LATEST_PATH =
|
|
9007
|
+
var SNAPSHOT_STACK_PATH = import_path21.default.join(import_os15.default.homedir(), ".node9", "snapshots.json");
|
|
9008
|
+
var UNDO_LATEST_PATH = import_path21.default.join(import_os15.default.homedir(), ".node9", "undo_latest.txt");
|
|
8797
9009
|
var MAX_SNAPSHOTS = 10;
|
|
8798
9010
|
var GIT_TIMEOUT = 15e3;
|
|
8799
9011
|
function readStack() {
|
|
8800
9012
|
try {
|
|
8801
|
-
if (
|
|
8802
|
-
return JSON.parse(
|
|
9013
|
+
if (import_fs19.default.existsSync(SNAPSHOT_STACK_PATH))
|
|
9014
|
+
return JSON.parse(import_fs19.default.readFileSync(SNAPSHOT_STACK_PATH, "utf-8"));
|
|
8803
9015
|
} catch {
|
|
8804
9016
|
}
|
|
8805
9017
|
return [];
|
|
8806
9018
|
}
|
|
8807
9019
|
function writeStack(stack) {
|
|
8808
|
-
const dir =
|
|
8809
|
-
if (!
|
|
8810
|
-
|
|
9020
|
+
const dir = import_path21.default.dirname(SNAPSHOT_STACK_PATH);
|
|
9021
|
+
if (!import_fs19.default.existsSync(dir)) import_fs19.default.mkdirSync(dir, { recursive: true });
|
|
9022
|
+
import_fs19.default.writeFileSync(SNAPSHOT_STACK_PATH, JSON.stringify(stack, null, 2));
|
|
8811
9023
|
}
|
|
8812
9024
|
function extractFilePath(args) {
|
|
8813
9025
|
if (!args || typeof args !== "object") return null;
|
|
@@ -8827,12 +9039,12 @@ function buildArgsSummary(tool, args) {
|
|
|
8827
9039
|
return "";
|
|
8828
9040
|
}
|
|
8829
9041
|
function findProjectRoot(filePath) {
|
|
8830
|
-
let dir =
|
|
9042
|
+
let dir = import_path21.default.dirname(filePath);
|
|
8831
9043
|
while (true) {
|
|
8832
|
-
if (
|
|
9044
|
+
if (import_fs19.default.existsSync(import_path21.default.join(dir, ".git")) || import_fs19.default.existsSync(import_path21.default.join(dir, "package.json"))) {
|
|
8833
9045
|
return dir;
|
|
8834
9046
|
}
|
|
8835
|
-
const parent =
|
|
9047
|
+
const parent = import_path21.default.dirname(dir);
|
|
8836
9048
|
if (parent === dir) return process.cwd();
|
|
8837
9049
|
dir = parent;
|
|
8838
9050
|
}
|
|
@@ -8840,7 +9052,7 @@ function findProjectRoot(filePath) {
|
|
|
8840
9052
|
function normalizeCwdForHash(cwd) {
|
|
8841
9053
|
let normalized;
|
|
8842
9054
|
try {
|
|
8843
|
-
normalized =
|
|
9055
|
+
normalized = import_fs19.default.realpathSync(cwd);
|
|
8844
9056
|
} catch {
|
|
8845
9057
|
normalized = cwd;
|
|
8846
9058
|
}
|
|
@@ -8850,16 +9062,16 @@ function normalizeCwdForHash(cwd) {
|
|
|
8850
9062
|
}
|
|
8851
9063
|
function getShadowRepoDir(cwd) {
|
|
8852
9064
|
const hash = import_crypto8.default.createHash("sha256").update(normalizeCwdForHash(cwd)).digest("hex").slice(0, 16);
|
|
8853
|
-
return
|
|
9065
|
+
return import_path21.default.join(import_os15.default.homedir(), ".node9", "snapshots", hash);
|
|
8854
9066
|
}
|
|
8855
9067
|
function cleanOrphanedIndexFiles(shadowDir) {
|
|
8856
9068
|
try {
|
|
8857
9069
|
const cutoff = Date.now() - 6e4;
|
|
8858
|
-
for (const f of
|
|
9070
|
+
for (const f of import_fs19.default.readdirSync(shadowDir)) {
|
|
8859
9071
|
if (f.startsWith("index_")) {
|
|
8860
|
-
const fp =
|
|
9072
|
+
const fp = import_path21.default.join(shadowDir, f);
|
|
8861
9073
|
try {
|
|
8862
|
-
if (
|
|
9074
|
+
if (import_fs19.default.statSync(fp).mtimeMs < cutoff) import_fs19.default.unlinkSync(fp);
|
|
8863
9075
|
} catch {
|
|
8864
9076
|
}
|
|
8865
9077
|
}
|
|
@@ -8871,7 +9083,7 @@ function writeShadowExcludes(shadowDir, ignorePaths) {
|
|
|
8871
9083
|
const hardcoded = [".git", ".node9"];
|
|
8872
9084
|
const lines = [...hardcoded, ...ignorePaths].join("\n");
|
|
8873
9085
|
try {
|
|
8874
|
-
|
|
9086
|
+
import_fs19.default.writeFileSync(import_path21.default.join(shadowDir, "info", "exclude"), lines + "\n", "utf8");
|
|
8875
9087
|
} catch {
|
|
8876
9088
|
}
|
|
8877
9089
|
}
|
|
@@ -8884,25 +9096,25 @@ function ensureShadowRepo(shadowDir, cwd) {
|
|
|
8884
9096
|
timeout: 3e3
|
|
8885
9097
|
});
|
|
8886
9098
|
if (check.status === 0) {
|
|
8887
|
-
const ptPath =
|
|
9099
|
+
const ptPath = import_path21.default.join(shadowDir, "project-path.txt");
|
|
8888
9100
|
try {
|
|
8889
|
-
const stored =
|
|
9101
|
+
const stored = import_fs19.default.readFileSync(ptPath, "utf8").trim();
|
|
8890
9102
|
if (stored === normalizedCwd) return true;
|
|
8891
9103
|
if (process.env.NODE9_DEBUG === "1")
|
|
8892
9104
|
console.error(
|
|
8893
9105
|
`[Node9] Shadow repo path mismatch: stored="${stored}" expected="${normalizedCwd}" \u2014 reinitializing`
|
|
8894
9106
|
);
|
|
8895
|
-
|
|
9107
|
+
import_fs19.default.rmSync(shadowDir, { recursive: true, force: true });
|
|
8896
9108
|
} catch {
|
|
8897
9109
|
try {
|
|
8898
|
-
|
|
9110
|
+
import_fs19.default.writeFileSync(ptPath, normalizedCwd, "utf8");
|
|
8899
9111
|
} catch {
|
|
8900
9112
|
}
|
|
8901
9113
|
return true;
|
|
8902
9114
|
}
|
|
8903
9115
|
}
|
|
8904
9116
|
try {
|
|
8905
|
-
|
|
9117
|
+
import_fs19.default.mkdirSync(shadowDir, { recursive: true });
|
|
8906
9118
|
} catch {
|
|
8907
9119
|
}
|
|
8908
9120
|
const init = (0, import_child_process8.spawnSync)("git", ["init", "--bare", shadowDir], { timeout: 5e3 });
|
|
@@ -8911,7 +9123,7 @@ function ensureShadowRepo(shadowDir, cwd) {
|
|
|
8911
9123
|
if (process.env.NODE9_DEBUG === "1") console.error("[Node9] git init --bare failed:", reason);
|
|
8912
9124
|
return false;
|
|
8913
9125
|
}
|
|
8914
|
-
const configFile =
|
|
9126
|
+
const configFile = import_path21.default.join(shadowDir, "config");
|
|
8915
9127
|
(0, import_child_process8.spawnSync)("git", ["config", "--file", configFile, "core.untrackedCache", "true"], {
|
|
8916
9128
|
timeout: 3e3
|
|
8917
9129
|
});
|
|
@@ -8919,7 +9131,7 @@ function ensureShadowRepo(shadowDir, cwd) {
|
|
|
8919
9131
|
timeout: 3e3
|
|
8920
9132
|
});
|
|
8921
9133
|
try {
|
|
8922
|
-
|
|
9134
|
+
import_fs19.default.writeFileSync(import_path21.default.join(shadowDir, "project-path.txt"), normalizedCwd, "utf8");
|
|
8923
9135
|
} catch {
|
|
8924
9136
|
}
|
|
8925
9137
|
return true;
|
|
@@ -8939,12 +9151,12 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
|
|
|
8939
9151
|
let indexFile = null;
|
|
8940
9152
|
try {
|
|
8941
9153
|
const rawFilePath = extractFilePath(args);
|
|
8942
|
-
const absFilePath = rawFilePath &&
|
|
9154
|
+
const absFilePath = rawFilePath && import_path21.default.isAbsolute(rawFilePath) ? rawFilePath : null;
|
|
8943
9155
|
const cwd = absFilePath ? findProjectRoot(absFilePath) : process.cwd();
|
|
8944
9156
|
const shadowDir = getShadowRepoDir(cwd);
|
|
8945
9157
|
if (!ensureShadowRepo(shadowDir, cwd)) return null;
|
|
8946
9158
|
writeShadowExcludes(shadowDir, ignorePaths);
|
|
8947
|
-
indexFile =
|
|
9159
|
+
indexFile = import_path21.default.join(shadowDir, `index_${process.pid}_${Date.now()}`);
|
|
8948
9160
|
const shadowEnv = {
|
|
8949
9161
|
...process.env,
|
|
8950
9162
|
GIT_DIR: shadowDir,
|
|
@@ -9016,7 +9228,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
|
|
|
9016
9228
|
writeStack(stack);
|
|
9017
9229
|
const entry = stack[stack.length - 1];
|
|
9018
9230
|
notifySnapshotTaken(commitHash.slice(0, 7), tool, entry.argsSummary, capturedFiles.length);
|
|
9019
|
-
|
|
9231
|
+
import_fs19.default.writeFileSync(UNDO_LATEST_PATH, commitHash);
|
|
9020
9232
|
if (shouldGc) {
|
|
9021
9233
|
(0, import_child_process8.spawn)("git", ["gc", "--auto"], { env: shadowEnv, detached: true, stdio: "ignore" }).unref();
|
|
9022
9234
|
}
|
|
@@ -9027,7 +9239,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
|
|
|
9027
9239
|
} finally {
|
|
9028
9240
|
if (indexFile) {
|
|
9029
9241
|
try {
|
|
9030
|
-
|
|
9242
|
+
import_fs19.default.unlinkSync(indexFile);
|
|
9031
9243
|
} catch {
|
|
9032
9244
|
}
|
|
9033
9245
|
}
|
|
@@ -9103,9 +9315,9 @@ function applyUndo(hash, cwd) {
|
|
|
9103
9315
|
timeout: GIT_TIMEOUT
|
|
9104
9316
|
}).stdout?.toString().trim().split("\n").filter(Boolean) ?? [];
|
|
9105
9317
|
for (const file of [...tracked, ...untracked]) {
|
|
9106
|
-
const fullPath =
|
|
9107
|
-
if (!snapshotFiles.has(file) &&
|
|
9108
|
-
|
|
9318
|
+
const fullPath = import_path21.default.join(dir, file);
|
|
9319
|
+
if (!snapshotFiles.has(file) && import_fs19.default.existsSync(fullPath)) {
|
|
9320
|
+
import_fs19.default.unlinkSync(fullPath);
|
|
9109
9321
|
}
|
|
9110
9322
|
}
|
|
9111
9323
|
return true;
|
|
@@ -9129,9 +9341,9 @@ function registerCheckCommand(program2) {
|
|
|
9129
9341
|
} catch (err2) {
|
|
9130
9342
|
const tempConfig = getConfig();
|
|
9131
9343
|
if (process.env.NODE9_DEBUG === "1" || tempConfig.settings.enableHookLogDebug) {
|
|
9132
|
-
const logPath =
|
|
9344
|
+
const logPath = import_path22.default.join(import_os16.default.homedir(), ".node9", "hook-debug.log");
|
|
9133
9345
|
const errMsg = err2 instanceof Error ? err2.message : String(err2);
|
|
9134
|
-
|
|
9346
|
+
import_fs20.default.appendFileSync(
|
|
9135
9347
|
logPath,
|
|
9136
9348
|
`[${(/* @__PURE__ */ new Date()).toISOString()}] JSON_PARSE_ERROR: ${errMsg}
|
|
9137
9349
|
RAW: ${raw}
|
|
@@ -9144,13 +9356,13 @@ RAW: ${raw}
|
|
|
9144
9356
|
if (config.settings.autoStartDaemon && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON) {
|
|
9145
9357
|
try {
|
|
9146
9358
|
const scriptPath = process.argv[1];
|
|
9147
|
-
if (typeof scriptPath !== "string" || !
|
|
9359
|
+
if (typeof scriptPath !== "string" || !import_path22.default.isAbsolute(scriptPath))
|
|
9148
9360
|
throw new Error("node9: argv[1] is not an absolute path");
|
|
9149
|
-
const resolvedScript =
|
|
9150
|
-
const
|
|
9151
|
-
if (resolvedScript !==
|
|
9361
|
+
const resolvedScript = import_fs20.default.realpathSync(scriptPath);
|
|
9362
|
+
const packageDist = import_fs20.default.realpathSync(import_path22.default.resolve(__dirname, "../.."));
|
|
9363
|
+
if (!resolvedScript.startsWith(packageDist + import_path22.default.sep) && resolvedScript !== packageDist)
|
|
9152
9364
|
throw new Error(
|
|
9153
|
-
|
|
9365
|
+
`node9: daemon spawn aborted \u2014 argv[1] (${resolvedScript}) is outside package dist (${packageDist})`
|
|
9154
9366
|
);
|
|
9155
9367
|
const safeEnv = { ...process.env };
|
|
9156
9368
|
for (const key of [
|
|
@@ -9169,14 +9381,24 @@ RAW: ${raw}
|
|
|
9169
9381
|
env: { ...safeEnv, NODE9_AUTO_STARTED: "1", NODE9_BROWSER_OPENED: "1" }
|
|
9170
9382
|
});
|
|
9171
9383
|
d.unref();
|
|
9172
|
-
} catch {
|
|
9384
|
+
} catch (spawnErr) {
|
|
9385
|
+
const logPath = import_path22.default.join(import_os16.default.homedir(), ".node9", "hook-debug.log");
|
|
9386
|
+
const msg = spawnErr instanceof Error ? spawnErr.message : String(spawnErr);
|
|
9387
|
+
try {
|
|
9388
|
+
import_fs20.default.appendFileSync(
|
|
9389
|
+
logPath,
|
|
9390
|
+
`[${(/* @__PURE__ */ new Date()).toISOString()}] daemon-autostart-failed: ${msg}
|
|
9391
|
+
`
|
|
9392
|
+
);
|
|
9393
|
+
} catch {
|
|
9394
|
+
}
|
|
9173
9395
|
}
|
|
9174
9396
|
}
|
|
9175
9397
|
if (process.env.NODE9_DEBUG === "1" || config.settings.enableHookLogDebug) {
|
|
9176
|
-
const logPath =
|
|
9177
|
-
if (!
|
|
9178
|
-
|
|
9179
|
-
|
|
9398
|
+
const logPath = import_path22.default.join(import_os16.default.homedir(), ".node9", "hook-debug.log");
|
|
9399
|
+
if (!import_fs20.default.existsSync(import_path22.default.dirname(logPath)))
|
|
9400
|
+
import_fs20.default.mkdirSync(import_path22.default.dirname(logPath), { recursive: true });
|
|
9401
|
+
import_fs20.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${raw}
|
|
9180
9402
|
`);
|
|
9181
9403
|
}
|
|
9182
9404
|
const toolName = sanitize2(payload.tool_name ?? payload.name ?? "");
|
|
@@ -9189,8 +9411,8 @@ RAW: ${raw}
|
|
|
9189
9411
|
const isHumanDecision = blockedByContext.toLowerCase().includes("user") || blockedByContext.toLowerCase().includes("daemon") || blockedByContext.toLowerCase().includes("decision");
|
|
9190
9412
|
let ttyFd = null;
|
|
9191
9413
|
try {
|
|
9192
|
-
ttyFd =
|
|
9193
|
-
const writeTty = (line) =>
|
|
9414
|
+
ttyFd = import_fs20.default.openSync("/dev/tty", "w");
|
|
9415
|
+
const writeTty = (line) => import_fs20.default.writeSync(ttyFd, line + "\n");
|
|
9194
9416
|
if (blockedByContext.includes("DLP") || blockedByContext.includes("Secret Detected") || blockedByContext.includes("Credential Review")) {
|
|
9195
9417
|
writeTty(import_chalk5.default.bgRed.white.bold(`
|
|
9196
9418
|
\u{1F6A8} NODE9 DLP ALERT \u2014 CREDENTIAL DETECTED `));
|
|
@@ -9209,7 +9431,7 @@ RAW: ${raw}
|
|
|
9209
9431
|
} finally {
|
|
9210
9432
|
if (ttyFd !== null)
|
|
9211
9433
|
try {
|
|
9212
|
-
|
|
9434
|
+
import_fs20.default.closeSync(ttyFd);
|
|
9213
9435
|
} catch {
|
|
9214
9436
|
}
|
|
9215
9437
|
}
|
|
@@ -9241,7 +9463,7 @@ RAW: ${raw}
|
|
|
9241
9463
|
if (shouldSnapshot(toolName, toolInput, config)) {
|
|
9242
9464
|
await createShadowSnapshot(toolName, toolInput, config.policy.snapshot.ignorePaths);
|
|
9243
9465
|
}
|
|
9244
|
-
const safeCwdForAuth = typeof payload.cwd === "string" &&
|
|
9466
|
+
const safeCwdForAuth = typeof payload.cwd === "string" && import_path22.default.isAbsolute(payload.cwd) ? payload.cwd : void 0;
|
|
9245
9467
|
const result = await authorizeHeadless(toolName, toolInput, meta, {
|
|
9246
9468
|
cwd: safeCwdForAuth
|
|
9247
9469
|
});
|
|
@@ -9253,12 +9475,12 @@ RAW: ${raw}
|
|
|
9253
9475
|
}
|
|
9254
9476
|
if (result.noApprovalMechanism && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON && !process.stdout.isTTY && config.settings.autoStartDaemon) {
|
|
9255
9477
|
try {
|
|
9256
|
-
const tty =
|
|
9257
|
-
|
|
9478
|
+
const tty = import_fs20.default.openSync("/dev/tty", "w");
|
|
9479
|
+
import_fs20.default.writeSync(
|
|
9258
9480
|
tty,
|
|
9259
9481
|
import_chalk5.default.cyan("\n\u{1F6E1}\uFE0F Node9: Starting approval daemon automatically...\n")
|
|
9260
9482
|
);
|
|
9261
|
-
|
|
9483
|
+
import_fs20.default.closeSync(tty);
|
|
9262
9484
|
} catch {
|
|
9263
9485
|
}
|
|
9264
9486
|
const daemonReady = await autoStartDaemonAndWait();
|
|
@@ -9285,9 +9507,9 @@ RAW: ${raw}
|
|
|
9285
9507
|
});
|
|
9286
9508
|
} catch (err2) {
|
|
9287
9509
|
if (process.env.NODE9_DEBUG === "1") {
|
|
9288
|
-
const logPath =
|
|
9510
|
+
const logPath = import_path22.default.join(import_os16.default.homedir(), ".node9", "hook-debug.log");
|
|
9289
9511
|
const errMsg = err2 instanceof Error ? err2.message : String(err2);
|
|
9290
|
-
|
|
9512
|
+
import_fs20.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] ERROR: ${errMsg}
|
|
9291
9513
|
`);
|
|
9292
9514
|
}
|
|
9293
9515
|
process.exit(0);
|
|
@@ -9321,9 +9543,9 @@ RAW: ${raw}
|
|
|
9321
9543
|
}
|
|
9322
9544
|
|
|
9323
9545
|
// src/cli/commands/log.ts
|
|
9324
|
-
var
|
|
9325
|
-
var
|
|
9326
|
-
var
|
|
9546
|
+
var import_fs21 = __toESM(require("fs"));
|
|
9547
|
+
var import_path23 = __toESM(require("path"));
|
|
9548
|
+
var import_os17 = __toESM(require("os"));
|
|
9327
9549
|
init_audit();
|
|
9328
9550
|
init_config();
|
|
9329
9551
|
init_policy();
|
|
@@ -9399,10 +9621,10 @@ function registerLogCommand(program2) {
|
|
|
9399
9621
|
decision: "allowed",
|
|
9400
9622
|
source: "post-hook"
|
|
9401
9623
|
};
|
|
9402
|
-
const logPath =
|
|
9403
|
-
if (!
|
|
9404
|
-
|
|
9405
|
-
|
|
9624
|
+
const logPath = import_path23.default.join(import_os17.default.homedir(), ".node9", "audit.log");
|
|
9625
|
+
if (!import_fs21.default.existsSync(import_path23.default.dirname(logPath)))
|
|
9626
|
+
import_fs21.default.mkdirSync(import_path23.default.dirname(logPath), { recursive: true });
|
|
9627
|
+
import_fs21.default.appendFileSync(logPath, JSON.stringify(entry) + "\n");
|
|
9406
9628
|
if ((tool === "Bash" || tool === "bash") && isDaemonRunning()) {
|
|
9407
9629
|
const command = typeof rawInput === "object" && rawInput !== null && "command" in rawInput && typeof rawInput.command === "string" ? rawInput.command : null;
|
|
9408
9630
|
if (command) {
|
|
@@ -9435,7 +9657,7 @@ function registerLogCommand(program2) {
|
|
|
9435
9657
|
}
|
|
9436
9658
|
}
|
|
9437
9659
|
}
|
|
9438
|
-
const safeCwd = typeof payload.cwd === "string" &&
|
|
9660
|
+
const safeCwd = typeof payload.cwd === "string" && import_path23.default.isAbsolute(payload.cwd) ? payload.cwd : void 0;
|
|
9439
9661
|
const config = getConfig(safeCwd);
|
|
9440
9662
|
if (shouldSnapshot(tool, {}, config)) {
|
|
9441
9663
|
await createShadowSnapshot("unknown", {}, config.policy.snapshot.ignorePaths);
|
|
@@ -9444,9 +9666,9 @@ function registerLogCommand(program2) {
|
|
|
9444
9666
|
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
9445
9667
|
process.stderr.write(`[Node9] audit log error: ${msg}
|
|
9446
9668
|
`);
|
|
9447
|
-
const debugPath =
|
|
9669
|
+
const debugPath = import_path23.default.join(import_os17.default.homedir(), ".node9", "hook-debug.log");
|
|
9448
9670
|
try {
|
|
9449
|
-
|
|
9671
|
+
import_fs21.default.appendFileSync(debugPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] LOG_ERROR: ${msg}
|
|
9450
9672
|
`);
|
|
9451
9673
|
} catch {
|
|
9452
9674
|
}
|
|
@@ -9846,14 +10068,14 @@ function registerConfigShowCommand(program2) {
|
|
|
9846
10068
|
|
|
9847
10069
|
// src/cli/commands/doctor.ts
|
|
9848
10070
|
var import_chalk7 = __toESM(require("chalk"));
|
|
9849
|
-
var
|
|
9850
|
-
var
|
|
9851
|
-
var
|
|
10071
|
+
var import_fs22 = __toESM(require("fs"));
|
|
10072
|
+
var import_path24 = __toESM(require("path"));
|
|
10073
|
+
var import_os18 = __toESM(require("os"));
|
|
9852
10074
|
var import_child_process10 = require("child_process");
|
|
9853
10075
|
init_daemon();
|
|
9854
10076
|
function registerDoctorCommand(program2, version2) {
|
|
9855
10077
|
program2.command("doctor").description("Check that Node9 is installed and configured correctly").action(() => {
|
|
9856
|
-
const homeDir2 =
|
|
10078
|
+
const homeDir2 = import_os18.default.homedir();
|
|
9857
10079
|
let failures = 0;
|
|
9858
10080
|
function pass(msg) {
|
|
9859
10081
|
console.log(import_chalk7.default.green(" \u2705 ") + msg);
|
|
@@ -9902,10 +10124,10 @@ function registerDoctorCommand(program2, version2) {
|
|
|
9902
10124
|
);
|
|
9903
10125
|
}
|
|
9904
10126
|
section("Configuration");
|
|
9905
|
-
const globalConfigPath =
|
|
9906
|
-
if (
|
|
10127
|
+
const globalConfigPath = import_path24.default.join(homeDir2, ".node9", "config.json");
|
|
10128
|
+
if (import_fs22.default.existsSync(globalConfigPath)) {
|
|
9907
10129
|
try {
|
|
9908
|
-
JSON.parse(
|
|
10130
|
+
JSON.parse(import_fs22.default.readFileSync(globalConfigPath, "utf-8"));
|
|
9909
10131
|
pass("~/.node9/config.json found and valid");
|
|
9910
10132
|
} catch {
|
|
9911
10133
|
fail("~/.node9/config.json is invalid JSON", "Run: node9 init --force");
|
|
@@ -9913,10 +10135,10 @@ function registerDoctorCommand(program2, version2) {
|
|
|
9913
10135
|
} else {
|
|
9914
10136
|
warn("~/.node9/config.json not found (using defaults)", "Run: node9 init");
|
|
9915
10137
|
}
|
|
9916
|
-
const projectConfigPath =
|
|
9917
|
-
if (
|
|
10138
|
+
const projectConfigPath = import_path24.default.join(process.cwd(), "node9.config.json");
|
|
10139
|
+
if (import_fs22.default.existsSync(projectConfigPath)) {
|
|
9918
10140
|
try {
|
|
9919
|
-
JSON.parse(
|
|
10141
|
+
JSON.parse(import_fs22.default.readFileSync(projectConfigPath, "utf-8"));
|
|
9920
10142
|
pass("node9.config.json found and valid (project)");
|
|
9921
10143
|
} catch {
|
|
9922
10144
|
fail(
|
|
@@ -9925,8 +10147,8 @@ function registerDoctorCommand(program2, version2) {
|
|
|
9925
10147
|
);
|
|
9926
10148
|
}
|
|
9927
10149
|
}
|
|
9928
|
-
const credsPath =
|
|
9929
|
-
if (
|
|
10150
|
+
const credsPath = import_path24.default.join(homeDir2, ".node9", "credentials.json");
|
|
10151
|
+
if (import_fs22.default.existsSync(credsPath)) {
|
|
9930
10152
|
pass("Cloud credentials found (~/.node9/credentials.json)");
|
|
9931
10153
|
} else {
|
|
9932
10154
|
warn(
|
|
@@ -9935,10 +10157,10 @@ function registerDoctorCommand(program2, version2) {
|
|
|
9935
10157
|
);
|
|
9936
10158
|
}
|
|
9937
10159
|
section("Agent Hooks");
|
|
9938
|
-
const claudeSettingsPath =
|
|
9939
|
-
if (
|
|
10160
|
+
const claudeSettingsPath = import_path24.default.join(homeDir2, ".claude", "settings.json");
|
|
10161
|
+
if (import_fs22.default.existsSync(claudeSettingsPath)) {
|
|
9940
10162
|
try {
|
|
9941
|
-
const cs = JSON.parse(
|
|
10163
|
+
const cs = JSON.parse(import_fs22.default.readFileSync(claudeSettingsPath, "utf-8"));
|
|
9942
10164
|
const hasHook = cs.hooks?.PreToolUse?.some(
|
|
9943
10165
|
(m) => m.hooks.some((h) => h.command?.includes("node9") || h.command?.includes("cli.js"))
|
|
9944
10166
|
);
|
|
@@ -9954,10 +10176,10 @@ function registerDoctorCommand(program2, version2) {
|
|
|
9954
10176
|
} else {
|
|
9955
10177
|
warn("Claude Code \u2014 not configured", "Run: node9 setup claude");
|
|
9956
10178
|
}
|
|
9957
|
-
const geminiSettingsPath =
|
|
9958
|
-
if (
|
|
10179
|
+
const geminiSettingsPath = import_path24.default.join(homeDir2, ".gemini", "settings.json");
|
|
10180
|
+
if (import_fs22.default.existsSync(geminiSettingsPath)) {
|
|
9959
10181
|
try {
|
|
9960
|
-
const gs = JSON.parse(
|
|
10182
|
+
const gs = JSON.parse(import_fs22.default.readFileSync(geminiSettingsPath, "utf-8"));
|
|
9961
10183
|
const hasHook = gs.hooks?.BeforeTool?.some(
|
|
9962
10184
|
(m) => m.hooks.some((h) => h.command?.includes("node9") || h.command?.includes("cli.js"))
|
|
9963
10185
|
);
|
|
@@ -9973,10 +10195,10 @@ function registerDoctorCommand(program2, version2) {
|
|
|
9973
10195
|
} else {
|
|
9974
10196
|
warn("Gemini CLI \u2014 not configured", "Run: node9 setup gemini (skip if not using Gemini)");
|
|
9975
10197
|
}
|
|
9976
|
-
const cursorHooksPath =
|
|
9977
|
-
if (
|
|
10198
|
+
const cursorHooksPath = import_path24.default.join(homeDir2, ".cursor", "hooks.json");
|
|
10199
|
+
if (import_fs22.default.existsSync(cursorHooksPath)) {
|
|
9978
10200
|
try {
|
|
9979
|
-
const cur = JSON.parse(
|
|
10201
|
+
const cur = JSON.parse(import_fs22.default.readFileSync(cursorHooksPath, "utf-8"));
|
|
9980
10202
|
const hasHook = cur.hooks?.preToolUse?.some(
|
|
9981
10203
|
(h) => h.command?.includes("node9") || h.command?.includes("cli.js")
|
|
9982
10204
|
);
|
|
@@ -10014,9 +10236,9 @@ function registerDoctorCommand(program2, version2) {
|
|
|
10014
10236
|
|
|
10015
10237
|
// src/cli/commands/audit.ts
|
|
10016
10238
|
var import_chalk8 = __toESM(require("chalk"));
|
|
10017
|
-
var
|
|
10018
|
-
var
|
|
10019
|
-
var
|
|
10239
|
+
var import_fs23 = __toESM(require("fs"));
|
|
10240
|
+
var import_path25 = __toESM(require("path"));
|
|
10241
|
+
var import_os19 = __toESM(require("os"));
|
|
10020
10242
|
function formatRelativeTime(timestamp) {
|
|
10021
10243
|
const diff = Date.now() - new Date(timestamp).getTime();
|
|
10022
10244
|
const sec = Math.floor(diff / 1e3);
|
|
@@ -10029,14 +10251,14 @@ function formatRelativeTime(timestamp) {
|
|
|
10029
10251
|
}
|
|
10030
10252
|
function registerAuditCommand(program2) {
|
|
10031
10253
|
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) => {
|
|
10032
|
-
const logPath =
|
|
10033
|
-
if (!
|
|
10254
|
+
const logPath = import_path25.default.join(import_os19.default.homedir(), ".node9", "audit.log");
|
|
10255
|
+
if (!import_fs23.default.existsSync(logPath)) {
|
|
10034
10256
|
console.log(
|
|
10035
10257
|
import_chalk8.default.yellow("No audit logs found. Run node9 with an agent to generate entries.")
|
|
10036
10258
|
);
|
|
10037
10259
|
return;
|
|
10038
10260
|
}
|
|
10039
|
-
const raw =
|
|
10261
|
+
const raw = import_fs23.default.readFileSync(logPath, "utf-8");
|
|
10040
10262
|
const lines = raw.split("\n").filter((l) => l.trim() !== "");
|
|
10041
10263
|
let entries = lines.flatMap((line) => {
|
|
10042
10264
|
try {
|
|
@@ -10090,9 +10312,9 @@ function registerAuditCommand(program2) {
|
|
|
10090
10312
|
|
|
10091
10313
|
// src/cli/commands/report.ts
|
|
10092
10314
|
var import_chalk9 = __toESM(require("chalk"));
|
|
10093
|
-
var
|
|
10094
|
-
var
|
|
10095
|
-
var
|
|
10315
|
+
var import_fs24 = __toESM(require("fs"));
|
|
10316
|
+
var import_path26 = __toESM(require("path"));
|
|
10317
|
+
var import_os20 = __toESM(require("os"));
|
|
10096
10318
|
var TEST_COMMAND_RE3 = /(?:^|\s)(npm\s+(?:run\s+)?test|npx\s+(?:vitest|jest|mocha)|yarn\s+(?:run\s+)?test|pnpm\s+(?:run\s+)?test|vitest|jest|mocha|pytest|py\.test|cargo\s+test|go\s+test|bundle\s+exec\s+rspec|rspec|phpunit|dotnet\s+test)\b/i;
|
|
10097
10319
|
function buildTestTimestamps(allEntries) {
|
|
10098
10320
|
const testTs = /* @__PURE__ */ new Set();
|
|
@@ -10139,8 +10361,8 @@ function getDateRange(period) {
|
|
|
10139
10361
|
}
|
|
10140
10362
|
}
|
|
10141
10363
|
function parseAuditLog(logPath) {
|
|
10142
|
-
if (!
|
|
10143
|
-
const raw =
|
|
10364
|
+
if (!import_fs24.default.existsSync(logPath)) return [];
|
|
10365
|
+
const raw = import_fs24.default.readFileSync(logPath, "utf-8");
|
|
10144
10366
|
return raw.split("\n").flatMap((line) => {
|
|
10145
10367
|
if (!line.trim()) return [];
|
|
10146
10368
|
try {
|
|
@@ -10202,29 +10424,39 @@ function claudeModelPrice(model) {
|
|
|
10202
10424
|
return null;
|
|
10203
10425
|
}
|
|
10204
10426
|
function loadClaudeCost(start, end) {
|
|
10205
|
-
const
|
|
10206
|
-
|
|
10427
|
+
const empty = {
|
|
10428
|
+
total: 0,
|
|
10429
|
+
byDay: /* @__PURE__ */ new Map(),
|
|
10430
|
+
byModel: /* @__PURE__ */ new Map(),
|
|
10431
|
+
inputTokens: 0,
|
|
10432
|
+
cacheReadTokens: 0
|
|
10433
|
+
};
|
|
10434
|
+
const projectsDir = import_path26.default.join(import_os20.default.homedir(), ".claude", "projects");
|
|
10435
|
+
if (!import_fs24.default.existsSync(projectsDir)) return empty;
|
|
10207
10436
|
let dirs;
|
|
10208
10437
|
try {
|
|
10209
|
-
dirs =
|
|
10438
|
+
dirs = import_fs24.default.readdirSync(projectsDir);
|
|
10210
10439
|
} catch {
|
|
10211
|
-
return
|
|
10440
|
+
return empty;
|
|
10212
10441
|
}
|
|
10213
10442
|
let total = 0;
|
|
10443
|
+
let inputTokens = 0;
|
|
10444
|
+
let cacheReadTokens = 0;
|
|
10214
10445
|
const byDay = /* @__PURE__ */ new Map();
|
|
10446
|
+
const byModel = /* @__PURE__ */ new Map();
|
|
10215
10447
|
for (const proj of dirs) {
|
|
10216
|
-
const projPath =
|
|
10448
|
+
const projPath = import_path26.default.join(projectsDir, proj);
|
|
10217
10449
|
let files;
|
|
10218
10450
|
try {
|
|
10219
|
-
const stat =
|
|
10451
|
+
const stat = import_fs24.default.statSync(projPath);
|
|
10220
10452
|
if (!stat.isDirectory()) continue;
|
|
10221
|
-
files =
|
|
10453
|
+
files = import_fs24.default.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
|
|
10222
10454
|
} catch {
|
|
10223
10455
|
continue;
|
|
10224
10456
|
}
|
|
10225
10457
|
for (const file of files) {
|
|
10226
10458
|
try {
|
|
10227
|
-
const raw =
|
|
10459
|
+
const raw = import_fs24.default.readFileSync(import_path26.default.join(projPath, file), "utf-8");
|
|
10228
10460
|
for (const line of raw.split("\n")) {
|
|
10229
10461
|
if (!line.trim()) continue;
|
|
10230
10462
|
let entry;
|
|
@@ -10242,24 +10474,32 @@ function loadClaudeCost(start, end) {
|
|
|
10242
10474
|
if (!usage || !model) continue;
|
|
10243
10475
|
const p = claudeModelPrice(model);
|
|
10244
10476
|
if (!p) continue;
|
|
10245
|
-
const
|
|
10477
|
+
const inp = usage.input_tokens ?? 0;
|
|
10478
|
+
const out = usage.output_tokens ?? 0;
|
|
10479
|
+
const cw = usage.cache_creation_input_tokens ?? 0;
|
|
10480
|
+
const cr = usage.cache_read_input_tokens ?? 0;
|
|
10481
|
+
const cost = inp * p.i + out * p.o + cw * p.cw + cr * p.cr;
|
|
10246
10482
|
total += cost;
|
|
10483
|
+
inputTokens += inp;
|
|
10484
|
+
cacheReadTokens += cr;
|
|
10247
10485
|
const dateKey = entry.timestamp.slice(0, 10);
|
|
10248
10486
|
byDay.set(dateKey, (byDay.get(dateKey) ?? 0) + cost);
|
|
10487
|
+
const normModel = model.replace(/@.*$/, "").replace(/-\d{8}$/, "");
|
|
10488
|
+
byModel.set(normModel, (byModel.get(normModel) ?? 0) + cost);
|
|
10249
10489
|
}
|
|
10250
10490
|
} catch {
|
|
10251
10491
|
continue;
|
|
10252
10492
|
}
|
|
10253
10493
|
}
|
|
10254
10494
|
}
|
|
10255
|
-
return { total, byDay };
|
|
10495
|
+
return { total, byDay, byModel, inputTokens, cacheReadTokens };
|
|
10256
10496
|
}
|
|
10257
10497
|
function registerReportCommand(program2) {
|
|
10258
10498
|
program2.command("report").description("Activity and security report \u2014 what Claude did, what was blocked").option("--period <period>", "today | 7d | 30d | month", "7d").option("--no-tests", "exclude test runner calls (npm test, vitest, pytest\u2026) from stats").action((options) => {
|
|
10259
10499
|
const period = ["today", "7d", "30d", "month"].includes(
|
|
10260
10500
|
options.period
|
|
10261
10501
|
) ? options.period : "7d";
|
|
10262
|
-
const logPath =
|
|
10502
|
+
const logPath = import_path26.default.join(import_os20.default.homedir(), ".node9", "audit.log");
|
|
10263
10503
|
const allEntries = parseAuditLog(logPath);
|
|
10264
10504
|
if (allEntries.length === 0) {
|
|
10265
10505
|
console.log(
|
|
@@ -10268,7 +10508,13 @@ function registerReportCommand(program2) {
|
|
|
10268
10508
|
return;
|
|
10269
10509
|
}
|
|
10270
10510
|
const { start, end } = getDateRange(period);
|
|
10271
|
-
const {
|
|
10511
|
+
const {
|
|
10512
|
+
total: costUSD,
|
|
10513
|
+
byDay: costByDay,
|
|
10514
|
+
byModel: costByModel,
|
|
10515
|
+
inputTokens: costInputTokens,
|
|
10516
|
+
cacheReadTokens: costCacheRead
|
|
10517
|
+
} = loadClaudeCost(start, end);
|
|
10272
10518
|
const periodMs = end.getTime() - start.getTime();
|
|
10273
10519
|
const priorEnd = new Date(start.getTime() - 1);
|
|
10274
10520
|
const priorStart = new Date(start.getTime() - periodMs);
|
|
@@ -10370,7 +10616,6 @@ function registerReportCommand(program2) {
|
|
|
10370
10616
|
const blockLabel = blocked > 0 ? import_chalk9.default.red(`\u{1F6D1} ${num(blocked)} blocked`) : import_chalk9.default.dim("\u{1F6D1} 0 blocked");
|
|
10371
10617
|
const dlpLabel = dlpHits > 0 ? import_chalk9.default.yellow(`\u{1F6A8} ${dlpHits} DLP hits`) : import_chalk9.default.dim("\u{1F6A8} 0 DLP hits");
|
|
10372
10618
|
const loopLabel = loopHits > 0 ? import_chalk9.default.yellow(`\u{1F504} ${loopHits} loops`) : import_chalk9.default.dim("\u{1F504} 0 loops");
|
|
10373
|
-
const costLabel = costUSD > 0 ? import_chalk9.default.magenta(`\u{1F4B0} ${fmtCost(costUSD)}`) : import_chalk9.default.dim("\u{1F4B0} \u2013");
|
|
10374
10619
|
const currentRate = total > 0 ? blocked / total : 0;
|
|
10375
10620
|
const trendLabel = (() => {
|
|
10376
10621
|
if (priorBlockRate === null) return import_chalk9.default.dim(`${pct(blocked, total)} block rate`);
|
|
@@ -10383,7 +10628,7 @@ function registerReportCommand(program2) {
|
|
|
10383
10628
|
const ratioLabel = reads > 0 ? import_chalk9.default.dim(`edit/read ${(edits / reads).toFixed(1)}`) : import_chalk9.default.dim("edit/read \u2013");
|
|
10384
10629
|
const testLabel = testPasses + testFails > 0 ? import_chalk9.default.dim("tests ") + import_chalk9.default.green(`${testPasses}\u2713`) + (testFails > 0 ? " " + import_chalk9.default.red(`${testFails}\u2717`) : "") : import_chalk9.default.dim("tests \u2013");
|
|
10385
10630
|
console.log(
|
|
10386
|
-
" " + import_chalk9.default.green(`\u2705 ${num(allowed)} allowed`) + " " + blockLabel + " " + dlpLabel + " " + loopLabel + " " + trendLabel
|
|
10631
|
+
" " + import_chalk9.default.green(`\u2705 ${num(allowed)} allowed`) + " " + blockLabel + " " + dlpLabel + " " + loopLabel + " " + trendLabel
|
|
10387
10632
|
);
|
|
10388
10633
|
console.log(" " + ratioLabel + " " + testLabel);
|
|
10389
10634
|
console.log("");
|
|
@@ -10468,6 +10713,30 @@ function registerReportCommand(program2) {
|
|
|
10468
10713
|
);
|
|
10469
10714
|
}
|
|
10470
10715
|
}
|
|
10716
|
+
if (costUSD > 0) {
|
|
10717
|
+
const periodDays = Math.max(1, Math.ceil((end.getTime() - start.getTime()) / 864e5));
|
|
10718
|
+
const avgPerDay = costUSD / periodDays;
|
|
10719
|
+
const cacheHitPct = costInputTokens + costCacheRead > 0 ? Math.round(costCacheRead / (costInputTokens + costCacheRead) * 100) : 0;
|
|
10720
|
+
const costHeaderRight = [
|
|
10721
|
+
import_chalk9.default.yellow(fmtCost(costUSD)),
|
|
10722
|
+
import_chalk9.default.dim(`avg ${fmtCost(avgPerDay)}/day`),
|
|
10723
|
+
cacheHitPct > 0 ? import_chalk9.default.dim(`${cacheHitPct}% cache hit`) : null
|
|
10724
|
+
].filter(Boolean).join(import_chalk9.default.dim(" \xB7 "));
|
|
10725
|
+
console.log("");
|
|
10726
|
+
console.log(" " + import_chalk9.default.bold("Cost") + " " + costHeaderRight);
|
|
10727
|
+
console.log(" " + import_chalk9.default.dim("\u2500".repeat(Math.min(50, W - 4))));
|
|
10728
|
+
const modelList = [...costByModel.entries()].sort((a, b) => b[1] - a[1]);
|
|
10729
|
+
const maxModelCost = Math.max(...modelList.map(([, v]) => v), 1e-9);
|
|
10730
|
+
const MODEL_LABEL = 22;
|
|
10731
|
+
const MODEL_BAR = Math.max(6, Math.min(20, W - MODEL_LABEL - 12));
|
|
10732
|
+
for (const [model, cost] of modelList) {
|
|
10733
|
+
const label = model.length > MODEL_LABEL - 1 ? model.slice(0, MODEL_LABEL - 2) + "\u2026" : model;
|
|
10734
|
+
const b = colorBar(cost, maxModelCost, MODEL_BAR);
|
|
10735
|
+
console.log(
|
|
10736
|
+
" " + import_chalk9.default.white(label.padEnd(MODEL_LABEL)) + b + " " + import_chalk9.default.yellow(fmtCost(cost))
|
|
10737
|
+
);
|
|
10738
|
+
}
|
|
10739
|
+
}
|
|
10471
10740
|
console.log("");
|
|
10472
10741
|
console.log(
|
|
10473
10742
|
" " + import_chalk9.default.dim("node9 audit --deny") + import_chalk9.default.dim(" \xB7 ") + import_chalk9.default.dim("node9 report --period today|7d|30d|month --no-tests")
|
|
@@ -10542,14 +10811,14 @@ function registerDaemonCommand(program2) {
|
|
|
10542
10811
|
|
|
10543
10812
|
// src/cli/commands/status.ts
|
|
10544
10813
|
var import_chalk11 = __toESM(require("chalk"));
|
|
10545
|
-
var
|
|
10546
|
-
var
|
|
10547
|
-
var
|
|
10814
|
+
var import_fs25 = __toESM(require("fs"));
|
|
10815
|
+
var import_path27 = __toESM(require("path"));
|
|
10816
|
+
var import_os21 = __toESM(require("os"));
|
|
10548
10817
|
init_core();
|
|
10549
10818
|
init_daemon();
|
|
10550
10819
|
function readJson2(filePath) {
|
|
10551
10820
|
try {
|
|
10552
|
-
if (
|
|
10821
|
+
if (import_fs25.default.existsSync(filePath)) return JSON.parse(import_fs25.default.readFileSync(filePath, "utf-8"));
|
|
10553
10822
|
} catch {
|
|
10554
10823
|
}
|
|
10555
10824
|
return null;
|
|
@@ -10614,28 +10883,28 @@ function registerStatusCommand(program2) {
|
|
|
10614
10883
|
console.log("");
|
|
10615
10884
|
const modeLabel = settings.mode === "audit" ? import_chalk11.default.blue("audit") : settings.mode === "strict" ? import_chalk11.default.red("strict") : import_chalk11.default.white("standard");
|
|
10616
10885
|
console.log(` Mode: ${modeLabel}`);
|
|
10617
|
-
const projectConfig =
|
|
10618
|
-
const globalConfig =
|
|
10886
|
+
const projectConfig = import_path27.default.join(process.cwd(), "node9.config.json");
|
|
10887
|
+
const globalConfig = import_path27.default.join(import_os21.default.homedir(), ".node9", "config.json");
|
|
10619
10888
|
console.log(
|
|
10620
|
-
` Local: ${
|
|
10889
|
+
` Local: ${import_fs25.default.existsSync(projectConfig) ? import_chalk11.default.green("Active (node9.config.json)") : import_chalk11.default.gray("Not present")}`
|
|
10621
10890
|
);
|
|
10622
10891
|
console.log(
|
|
10623
|
-
` Global: ${
|
|
10892
|
+
` Global: ${import_fs25.default.existsSync(globalConfig) ? import_chalk11.default.green("Active (~/.node9/config.json)") : import_chalk11.default.gray("Not present")}`
|
|
10624
10893
|
);
|
|
10625
10894
|
if (mergedConfig.policy.sandboxPaths.length > 0) {
|
|
10626
10895
|
console.log(
|
|
10627
10896
|
` Sandbox: ${import_chalk11.default.green(`${mergedConfig.policy.sandboxPaths.length} safe zones active`)}`
|
|
10628
10897
|
);
|
|
10629
10898
|
}
|
|
10630
|
-
const homeDir2 =
|
|
10899
|
+
const homeDir2 = import_os21.default.homedir();
|
|
10631
10900
|
const claudeSettings = readJson2(
|
|
10632
|
-
|
|
10901
|
+
import_path27.default.join(homeDir2, ".claude", "settings.json")
|
|
10633
10902
|
);
|
|
10634
|
-
const claudeConfig = readJson2(
|
|
10903
|
+
const claudeConfig = readJson2(import_path27.default.join(homeDir2, ".claude.json"));
|
|
10635
10904
|
const geminiSettings = readJson2(
|
|
10636
|
-
|
|
10905
|
+
import_path27.default.join(homeDir2, ".gemini", "settings.json")
|
|
10637
10906
|
);
|
|
10638
|
-
const cursorConfig = readJson2(
|
|
10907
|
+
const cursorConfig = readJson2(import_path27.default.join(homeDir2, ".cursor", "mcp.json"));
|
|
10639
10908
|
const agentFound = claudeSettings || claudeConfig || geminiSettings || cursorConfig;
|
|
10640
10909
|
if (agentFound) {
|
|
10641
10910
|
console.log("");
|
|
@@ -10694,9 +10963,9 @@ function registerStatusCommand(program2) {
|
|
|
10694
10963
|
|
|
10695
10964
|
// src/cli/commands/init.ts
|
|
10696
10965
|
var import_chalk12 = __toESM(require("chalk"));
|
|
10697
|
-
var
|
|
10698
|
-
var
|
|
10699
|
-
var
|
|
10966
|
+
var import_fs26 = __toESM(require("fs"));
|
|
10967
|
+
var import_path28 = __toESM(require("path"));
|
|
10968
|
+
var import_os22 = __toESM(require("os"));
|
|
10700
10969
|
var import_https2 = __toESM(require("https"));
|
|
10701
10970
|
init_core();
|
|
10702
10971
|
init_shields();
|
|
@@ -10756,15 +11025,15 @@ function registerInitCommand(program2) {
|
|
|
10756
11025
|
}
|
|
10757
11026
|
console.log("");
|
|
10758
11027
|
}
|
|
10759
|
-
const configPath =
|
|
10760
|
-
if (
|
|
11028
|
+
const configPath = import_path28.default.join(import_os22.default.homedir(), ".node9", "config.json");
|
|
11029
|
+
if (import_fs26.default.existsSync(configPath) && !options.force) {
|
|
10761
11030
|
try {
|
|
10762
|
-
const existing = JSON.parse(
|
|
11031
|
+
const existing = JSON.parse(import_fs26.default.readFileSync(configPath, "utf-8"));
|
|
10763
11032
|
const settings = existing.settings ?? {};
|
|
10764
11033
|
if (settings.mode !== chosenMode) {
|
|
10765
11034
|
settings.mode = chosenMode;
|
|
10766
11035
|
existing.settings = settings;
|
|
10767
|
-
|
|
11036
|
+
import_fs26.default.writeFileSync(configPath, JSON.stringify(existing, null, 2) + "\n");
|
|
10768
11037
|
console.log(import_chalk12.default.green(`\u2705 Mode updated: ${chosenMode}`));
|
|
10769
11038
|
} else {
|
|
10770
11039
|
console.log(import_chalk12.default.blue(`\u2139\uFE0F Config already exists: ${configPath}`));
|
|
@@ -10777,9 +11046,9 @@ function registerInitCommand(program2) {
|
|
|
10777
11046
|
...DEFAULT_CONFIG,
|
|
10778
11047
|
settings: { ...DEFAULT_CONFIG.settings, mode: chosenMode }
|
|
10779
11048
|
};
|
|
10780
|
-
const dir =
|
|
10781
|
-
if (!
|
|
10782
|
-
|
|
11049
|
+
const dir = import_path28.default.dirname(configPath);
|
|
11050
|
+
if (!import_fs26.default.existsSync(dir)) import_fs26.default.mkdirSync(dir, { recursive: true });
|
|
11051
|
+
import_fs26.default.writeFileSync(configPath, JSON.stringify(configToSave, null, 2) + "\n");
|
|
10783
11052
|
console.log(import_chalk12.default.green(`\u2705 Config created: ${configPath}`));
|
|
10784
11053
|
console.log(import_chalk12.default.gray(` Mode: ${chosenMode}`));
|
|
10785
11054
|
}
|
|
@@ -10833,7 +11102,7 @@ function registerInitCommand(program2) {
|
|
|
10833
11102
|
}
|
|
10834
11103
|
|
|
10835
11104
|
// src/cli/commands/undo.ts
|
|
10836
|
-
var
|
|
11105
|
+
var import_path29 = __toESM(require("path"));
|
|
10837
11106
|
var import_chalk14 = __toESM(require("chalk"));
|
|
10838
11107
|
|
|
10839
11108
|
// src/tui/undo-navigator.ts
|
|
@@ -10992,7 +11261,7 @@ function findMatchingCwd(startDir, history) {
|
|
|
10992
11261
|
let dir = startDir;
|
|
10993
11262
|
while (true) {
|
|
10994
11263
|
if (cwds.has(dir)) return dir;
|
|
10995
|
-
const parent =
|
|
11264
|
+
const parent = import_path29.default.dirname(dir);
|
|
10996
11265
|
if (parent === dir) return null;
|
|
10997
11266
|
dir = parent;
|
|
10998
11267
|
}
|
|
@@ -11188,12 +11457,12 @@ init_orchestrator();
|
|
|
11188
11457
|
init_provenance();
|
|
11189
11458
|
|
|
11190
11459
|
// src/mcp-pin.ts
|
|
11191
|
-
var
|
|
11192
|
-
var
|
|
11193
|
-
var
|
|
11460
|
+
var import_fs27 = __toESM(require("fs"));
|
|
11461
|
+
var import_path30 = __toESM(require("path"));
|
|
11462
|
+
var import_os23 = __toESM(require("os"));
|
|
11194
11463
|
var import_crypto9 = __toESM(require("crypto"));
|
|
11195
11464
|
function getPinsFilePath() {
|
|
11196
|
-
return
|
|
11465
|
+
return import_path30.default.join(import_os23.default.homedir(), ".node9", "mcp-pins.json");
|
|
11197
11466
|
}
|
|
11198
11467
|
function hashToolDefinitions(tools) {
|
|
11199
11468
|
const sorted = [...tools].sort((a, b) => {
|
|
@@ -11210,7 +11479,7 @@ function getServerKey(upstreamCommand) {
|
|
|
11210
11479
|
function readMcpPinsSafe() {
|
|
11211
11480
|
const filePath = getPinsFilePath();
|
|
11212
11481
|
try {
|
|
11213
|
-
const raw =
|
|
11482
|
+
const raw = import_fs27.default.readFileSync(filePath, "utf-8");
|
|
11214
11483
|
if (!raw.trim()) {
|
|
11215
11484
|
return { ok: false, reason: "corrupt", detail: "empty file" };
|
|
11216
11485
|
}
|
|
@@ -11234,10 +11503,10 @@ function readMcpPins() {
|
|
|
11234
11503
|
}
|
|
11235
11504
|
function writeMcpPins(data) {
|
|
11236
11505
|
const filePath = getPinsFilePath();
|
|
11237
|
-
|
|
11506
|
+
import_fs27.default.mkdirSync(import_path30.default.dirname(filePath), { recursive: true });
|
|
11238
11507
|
const tmp = `${filePath}.${import_crypto9.default.randomBytes(6).toString("hex")}.tmp`;
|
|
11239
|
-
|
|
11240
|
-
|
|
11508
|
+
import_fs27.default.writeFileSync(tmp, JSON.stringify(data, null, 2), { mode: 384 });
|
|
11509
|
+
import_fs27.default.renameSync(tmp, filePath);
|
|
11241
11510
|
}
|
|
11242
11511
|
function checkPin(serverKey, currentHash) {
|
|
11243
11512
|
const result = readMcpPinsSafe();
|
|
@@ -11609,9 +11878,9 @@ function registerMcpGatewayCommand(program2) {
|
|
|
11609
11878
|
|
|
11610
11879
|
// src/mcp-server/index.ts
|
|
11611
11880
|
var import_readline4 = __toESM(require("readline"));
|
|
11612
|
-
var
|
|
11613
|
-
var
|
|
11614
|
-
var
|
|
11881
|
+
var import_fs28 = __toESM(require("fs"));
|
|
11882
|
+
var import_os24 = __toESM(require("os"));
|
|
11883
|
+
var import_path31 = __toESM(require("path"));
|
|
11615
11884
|
init_core();
|
|
11616
11885
|
init_daemon();
|
|
11617
11886
|
init_shields();
|
|
@@ -11786,13 +12055,13 @@ function handleStatus() {
|
|
|
11786
12055
|
lines.push(`Active shields: ${activeShields.length > 0 ? activeShields.join(", ") : "none"}`);
|
|
11787
12056
|
lines.push(`Smart rules: ${config.policy.smartRules.length} loaded`);
|
|
11788
12057
|
lines.push(`DLP: ${config.policy.dlp?.enabled !== false ? "enabled" : "disabled"}`);
|
|
11789
|
-
const projectConfig =
|
|
11790
|
-
const globalConfig =
|
|
12058
|
+
const projectConfig = import_path31.default.join(process.cwd(), "node9.config.json");
|
|
12059
|
+
const globalConfig = import_path31.default.join(import_os24.default.homedir(), ".node9", "config.json");
|
|
11791
12060
|
lines.push(
|
|
11792
|
-
`Project config (node9.config.json): ${
|
|
12061
|
+
`Project config (node9.config.json): ${import_fs28.default.existsSync(projectConfig) ? "present" : "not found"}`
|
|
11793
12062
|
);
|
|
11794
12063
|
lines.push(
|
|
11795
|
-
`Global config (~/.node9/config.json): ${
|
|
12064
|
+
`Global config (~/.node9/config.json): ${import_fs28.default.existsSync(globalConfig) ? "present" : "not found"}`
|
|
11796
12065
|
);
|
|
11797
12066
|
return lines.join("\n");
|
|
11798
12067
|
}
|
|
@@ -11866,21 +12135,21 @@ function handleShieldDisable(args) {
|
|
|
11866
12135
|
writeActiveShields(active.filter((s) => s !== name));
|
|
11867
12136
|
return `Shield "${name}" disabled.`;
|
|
11868
12137
|
}
|
|
11869
|
-
var GLOBAL_CONFIG_PATH2 =
|
|
12138
|
+
var GLOBAL_CONFIG_PATH2 = import_path31.default.join(import_os24.default.homedir(), ".node9", "config.json");
|
|
11870
12139
|
var APPROVER_CHANNELS = ["native", "browser", "cloud", "terminal"];
|
|
11871
12140
|
function readGlobalConfigRaw() {
|
|
11872
12141
|
try {
|
|
11873
|
-
if (
|
|
11874
|
-
return JSON.parse(
|
|
12142
|
+
if (import_fs28.default.existsSync(GLOBAL_CONFIG_PATH2)) {
|
|
12143
|
+
return JSON.parse(import_fs28.default.readFileSync(GLOBAL_CONFIG_PATH2, "utf-8"));
|
|
11875
12144
|
}
|
|
11876
12145
|
} catch {
|
|
11877
12146
|
}
|
|
11878
12147
|
return {};
|
|
11879
12148
|
}
|
|
11880
12149
|
function writeGlobalConfigRaw(data) {
|
|
11881
|
-
const dir =
|
|
11882
|
-
if (!
|
|
11883
|
-
|
|
12150
|
+
const dir = import_path31.default.dirname(GLOBAL_CONFIG_PATH2);
|
|
12151
|
+
if (!import_fs28.default.existsSync(dir)) import_fs28.default.mkdirSync(dir, { recursive: true });
|
|
12152
|
+
import_fs28.default.writeFileSync(GLOBAL_CONFIG_PATH2, JSON.stringify(data, null, 2) + "\n");
|
|
11884
12153
|
}
|
|
11885
12154
|
function handleApproverList() {
|
|
11886
12155
|
const config = getConfig();
|
|
@@ -11923,9 +12192,9 @@ function handleApproverSet(args) {
|
|
|
11923
12192
|
}
|
|
11924
12193
|
function handleAuditGet(args) {
|
|
11925
12194
|
const limit = Math.min(typeof args.limit === "number" ? args.limit : 20, 100);
|
|
11926
|
-
const auditPath =
|
|
11927
|
-
if (!
|
|
11928
|
-
const lines =
|
|
12195
|
+
const auditPath = import_path31.default.join(import_os24.default.homedir(), ".node9", "audit.log");
|
|
12196
|
+
if (!import_fs28.default.existsSync(auditPath)) return "No audit log found.";
|
|
12197
|
+
const lines = import_fs28.default.readFileSync(auditPath, "utf-8").trim().split("\n").filter(Boolean);
|
|
11929
12198
|
const recent = lines.slice(-limit);
|
|
11930
12199
|
const entries = recent.map((line) => {
|
|
11931
12200
|
try {
|
|
@@ -12245,20 +12514,20 @@ function registerMcpPinCommand(program2) {
|
|
|
12245
12514
|
|
|
12246
12515
|
// src/cli.ts
|
|
12247
12516
|
var { version } = JSON.parse(
|
|
12248
|
-
|
|
12517
|
+
import_fs31.default.readFileSync(import_path34.default.join(__dirname, "../package.json"), "utf-8")
|
|
12249
12518
|
);
|
|
12250
12519
|
var program = new import_commander.Command();
|
|
12251
12520
|
program.name("node9").description("The Sudo Command for AI Agents").version(version);
|
|
12252
12521
|
program.command("login").argument("<apiKey>").option("--local", "Save key for audit/logging only \u2014 local config still controls all decisions").option("--profile <name>", 'Save as a named profile (default: "default")').action((apiKey, options) => {
|
|
12253
12522
|
const DEFAULT_API_URL = "https://api.node9.ai/api/v1/intercept";
|
|
12254
|
-
const credPath =
|
|
12255
|
-
if (!
|
|
12256
|
-
|
|
12523
|
+
const credPath = import_path34.default.join(import_os27.default.homedir(), ".node9", "credentials.json");
|
|
12524
|
+
if (!import_fs31.default.existsSync(import_path34.default.dirname(credPath)))
|
|
12525
|
+
import_fs31.default.mkdirSync(import_path34.default.dirname(credPath), { recursive: true });
|
|
12257
12526
|
const profileName = options.profile || "default";
|
|
12258
12527
|
let existingCreds = {};
|
|
12259
12528
|
try {
|
|
12260
|
-
if (
|
|
12261
|
-
const raw = JSON.parse(
|
|
12529
|
+
if (import_fs31.default.existsSync(credPath)) {
|
|
12530
|
+
const raw = JSON.parse(import_fs31.default.readFileSync(credPath, "utf-8"));
|
|
12262
12531
|
if (raw.apiKey) {
|
|
12263
12532
|
existingCreds = {
|
|
12264
12533
|
default: { apiKey: raw.apiKey, apiUrl: raw.apiUrl || DEFAULT_API_URL }
|
|
@@ -12270,13 +12539,13 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
|
|
|
12270
12539
|
} catch {
|
|
12271
12540
|
}
|
|
12272
12541
|
existingCreds[profileName] = { apiKey, apiUrl: DEFAULT_API_URL };
|
|
12273
|
-
|
|
12542
|
+
import_fs31.default.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), { mode: 384 });
|
|
12274
12543
|
if (profileName === "default") {
|
|
12275
|
-
const configPath =
|
|
12544
|
+
const configPath = import_path34.default.join(import_os27.default.homedir(), ".node9", "config.json");
|
|
12276
12545
|
let config = {};
|
|
12277
12546
|
try {
|
|
12278
|
-
if (
|
|
12279
|
-
config = JSON.parse(
|
|
12547
|
+
if (import_fs31.default.existsSync(configPath))
|
|
12548
|
+
config = JSON.parse(import_fs31.default.readFileSync(configPath, "utf-8"));
|
|
12280
12549
|
} catch {
|
|
12281
12550
|
}
|
|
12282
12551
|
if (!config.settings || typeof config.settings !== "object") config.settings = {};
|
|
@@ -12291,9 +12560,9 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
|
|
|
12291
12560
|
approvers.cloud = false;
|
|
12292
12561
|
}
|
|
12293
12562
|
s.approvers = approvers;
|
|
12294
|
-
if (!
|
|
12295
|
-
|
|
12296
|
-
|
|
12563
|
+
if (!import_fs31.default.existsSync(import_path34.default.dirname(configPath)))
|
|
12564
|
+
import_fs31.default.mkdirSync(import_path34.default.dirname(configPath), { recursive: true });
|
|
12565
|
+
import_fs31.default.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
|
|
12297
12566
|
}
|
|
12298
12567
|
if (options.profile && profileName !== "default") {
|
|
12299
12568
|
console.log(import_chalk20.default.green(`\u2705 Profile "${profileName}" saved`));
|
|
@@ -12387,15 +12656,15 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
|
|
|
12387
12656
|
}
|
|
12388
12657
|
}
|
|
12389
12658
|
if (options.purge) {
|
|
12390
|
-
const node9Dir =
|
|
12391
|
-
if (
|
|
12659
|
+
const node9Dir = import_path34.default.join(import_os27.default.homedir(), ".node9");
|
|
12660
|
+
if (import_fs31.default.existsSync(node9Dir)) {
|
|
12392
12661
|
const confirmed = await (0, import_prompts2.confirm)({
|
|
12393
12662
|
message: `Permanently delete ${node9Dir} (config, audit log, credentials)?`,
|
|
12394
12663
|
default: false
|
|
12395
12664
|
});
|
|
12396
12665
|
if (confirmed) {
|
|
12397
|
-
|
|
12398
|
-
if (
|
|
12666
|
+
import_fs31.default.rmSync(node9Dir, { recursive: true });
|
|
12667
|
+
if (import_fs31.default.existsSync(node9Dir)) {
|
|
12399
12668
|
console.error(
|
|
12400
12669
|
import_chalk20.default.red("\n \u26A0\uFE0F ~/.node9/ could not be fully deleted \u2014 remove it manually.")
|
|
12401
12670
|
);
|
|
@@ -12536,14 +12805,14 @@ Claude Code spawns this command every ~300ms and writes a JSON payload to stdin.
|
|
|
12536
12805
|
Run "node9 addto claude" to register it as the statusLine.`
|
|
12537
12806
|
).argument("[subcommand]", 'Optional: "debug on" / "debug off" to toggle stdin logging').argument("[state]", 'on|off \u2014 used with "debug" subcommand').action(async (subcommand, state) => {
|
|
12538
12807
|
if (subcommand === "debug") {
|
|
12539
|
-
const flagFile =
|
|
12808
|
+
const flagFile = import_path34.default.join(import_os27.default.homedir(), ".node9", "hud-debug");
|
|
12540
12809
|
if (state === "on") {
|
|
12541
|
-
|
|
12542
|
-
|
|
12810
|
+
import_fs31.default.mkdirSync(import_path34.default.dirname(flagFile), { recursive: true });
|
|
12811
|
+
import_fs31.default.writeFileSync(flagFile, "");
|
|
12543
12812
|
console.log("HUD debug logging enabled \u2192 ~/.node9/hud-debug.log");
|
|
12544
12813
|
console.log("Tail it with: tail -f ~/.node9/hud-debug.log");
|
|
12545
12814
|
} else if (state === "off") {
|
|
12546
|
-
if (
|
|
12815
|
+
if (import_fs31.default.existsSync(flagFile)) import_fs31.default.unlinkSync(flagFile);
|
|
12547
12816
|
console.log("HUD debug logging disabled.");
|
|
12548
12817
|
} else {
|
|
12549
12818
|
console.error("Usage: node9 hud debug on|off");
|
|
@@ -12650,9 +12919,9 @@ if (process.argv[2] !== "daemon") {
|
|
|
12650
12919
|
const isCheckHook = process.argv[2] === "check";
|
|
12651
12920
|
if (isCheckHook) {
|
|
12652
12921
|
if (process.env.NODE9_DEBUG === "1" || getConfig().settings.enableHookLogDebug) {
|
|
12653
|
-
const logPath =
|
|
12922
|
+
const logPath = import_path34.default.join(import_os27.default.homedir(), ".node9", "hook-debug.log");
|
|
12654
12923
|
const msg = reason instanceof Error ? reason.message : String(reason);
|
|
12655
|
-
|
|
12924
|
+
import_fs31.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
|
|
12656
12925
|
`);
|
|
12657
12926
|
}
|
|
12658
12927
|
process.exit(0);
|