@node9/proxy 1.5.4 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +70 -4
- package/dist/cli.js +493 -140
- package/dist/cli.mjs +491 -138
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -160,8 +160,8 @@ function sanitizeConfig(raw) {
|
|
|
160
160
|
}
|
|
161
161
|
}
|
|
162
162
|
const lines = result.error.issues.map((issue) => {
|
|
163
|
-
const
|
|
164
|
-
return ` \u2022 ${
|
|
163
|
+
const path31 = issue.path.length > 0 ? issue.path.join(".") : "root";
|
|
164
|
+
return ` \u2022 ${path31}: ${issue.message}`;
|
|
165
165
|
});
|
|
166
166
|
return {
|
|
167
167
|
sanitized,
|
|
@@ -315,9 +315,9 @@ function readShieldsFile() {
|
|
|
315
315
|
(e) => typeof e === "string" && e.length > 0 && e in SHIELDS
|
|
316
316
|
) : [];
|
|
317
317
|
return { active, overrides: validateOverrides(parsed.overrides) };
|
|
318
|
-
} catch (
|
|
319
|
-
if (
|
|
320
|
-
process.stderr.write(`[node9] Warning: could not read shields state: ${String(
|
|
318
|
+
} catch (err2) {
|
|
319
|
+
if (err2.code !== "ENOENT") {
|
|
320
|
+
process.stderr.write(`[node9] Warning: could not read shields state: ${String(err2)}
|
|
321
321
|
`);
|
|
322
322
|
}
|
|
323
323
|
return { active: [] };
|
|
@@ -733,8 +733,8 @@ function tryLoadConfig(filePath) {
|
|
|
733
733
|
let raw;
|
|
734
734
|
try {
|
|
735
735
|
raw = JSON.parse(import_fs3.default.readFileSync(filePath, "utf-8"));
|
|
736
|
-
} catch (
|
|
737
|
-
const msg =
|
|
736
|
+
} catch (err2) {
|
|
737
|
+
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
738
738
|
process.stderr.write(
|
|
739
739
|
`
|
|
740
740
|
\u26A0\uFE0F Node9: Failed to parse ${filePath}
|
|
@@ -1064,10 +1064,10 @@ function getCompiledRegex(pattern, flags = "") {
|
|
|
1064
1064
|
regexCache.set(key, cached);
|
|
1065
1065
|
return cached;
|
|
1066
1066
|
}
|
|
1067
|
-
const
|
|
1068
|
-
if (
|
|
1067
|
+
const err2 = validateRegex(pattern);
|
|
1068
|
+
if (err2) {
|
|
1069
1069
|
if (process.env.NODE9_DEBUG === "1")
|
|
1070
|
-
console.error(`[Node9] Regex blocked: ${
|
|
1070
|
+
console.error(`[Node9] Regex blocked: ${err2} \u2014 pattern: "${pattern}"`);
|
|
1071
1071
|
return null;
|
|
1072
1072
|
}
|
|
1073
1073
|
try {
|
|
@@ -1101,8 +1101,8 @@ function scanFilePath(filePath, cwd = process.cwd()) {
|
|
|
1101
1101
|
try {
|
|
1102
1102
|
const absolute = import_path4.default.resolve(cwd, filePath);
|
|
1103
1103
|
resolved = import_fs4.default.realpathSync.native(absolute);
|
|
1104
|
-
} catch (
|
|
1105
|
-
const code =
|
|
1104
|
+
} catch (err2) {
|
|
1105
|
+
const code = err2.code;
|
|
1106
1106
|
if (code === "ENOENT" || code === "ENOTDIR") {
|
|
1107
1107
|
resolved = import_path4.default.resolve(cwd, filePath);
|
|
1108
1108
|
} else {
|
|
@@ -1774,9 +1774,9 @@ function matchesPattern(text, patterns) {
|
|
|
1774
1774
|
const withoutDotSlash = text.replace(/^\.\//, "");
|
|
1775
1775
|
return isMatch(withoutDotSlash) || isMatch(`./${withoutDotSlash}`);
|
|
1776
1776
|
}
|
|
1777
|
-
function getNestedValue(obj,
|
|
1777
|
+
function getNestedValue(obj, path31) {
|
|
1778
1778
|
if (!obj || typeof obj !== "object") return null;
|
|
1779
|
-
return
|
|
1779
|
+
return path31.split(".").reduce((prev, curr) => prev?.[curr], obj);
|
|
1780
1780
|
}
|
|
1781
1781
|
function shouldSnapshot(toolName, args, config) {
|
|
1782
1782
|
if (!config.settings.enableUndo) return false;
|
|
@@ -2428,9 +2428,9 @@ function writeTrustSession(toolName, durationMs) {
|
|
|
2428
2428
|
trust.entries = trust.entries.filter((e) => e.tool !== toolName && e.expiry > now);
|
|
2429
2429
|
trust.entries.push({ tool: toolName, expiry: now + durationMs });
|
|
2430
2430
|
atomicWriteSync(TRUST_FILE, JSON.stringify(trust, null, 2));
|
|
2431
|
-
} catch (
|
|
2431
|
+
} catch (err2) {
|
|
2432
2432
|
if (process.env.NODE9_DEBUG === "1") {
|
|
2433
|
-
console.error("[Node9 Trust Error]:",
|
|
2433
|
+
console.error("[Node9 Trust Error]:", err2);
|
|
2434
2434
|
}
|
|
2435
2435
|
}
|
|
2436
2436
|
}
|
|
@@ -2627,13 +2627,13 @@ async function checkTaint(paths) {
|
|
|
2627
2627
|
signal: AbortSignal.timeout(2e3)
|
|
2628
2628
|
});
|
|
2629
2629
|
return await res.json();
|
|
2630
|
-
} catch (
|
|
2630
|
+
} catch (err2) {
|
|
2631
2631
|
try {
|
|
2632
2632
|
const { appendToLog: appendToLog2, HOOK_DEBUG_LOG: HOOK_DEBUG_LOG2 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
|
|
2633
2633
|
appendToLog2(HOOK_DEBUG_LOG2, {
|
|
2634
2634
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2635
2635
|
event: "checkTaint-error",
|
|
2636
|
-
error: String(
|
|
2636
|
+
error: String(err2),
|
|
2637
2637
|
paths
|
|
2638
2638
|
});
|
|
2639
2639
|
} catch {
|
|
@@ -3128,10 +3128,10 @@ async function resolveNode9SaaS(requestId, creds, approved, decidedBy) {
|
|
|
3128
3128
|
`
|
|
3129
3129
|
);
|
|
3130
3130
|
}
|
|
3131
|
-
} catch (
|
|
3131
|
+
} catch (err2) {
|
|
3132
3132
|
import_fs10.default.appendFileSync(
|
|
3133
3133
|
HOOK_DEBUG_LOG,
|
|
3134
|
-
`[resolve-cloud] PATCH failed for ${requestId}: ${
|
|
3134
|
+
`[resolve-cloud] PATCH failed for ${requestId}: ${err2.message}
|
|
3135
3135
|
`
|
|
3136
3136
|
);
|
|
3137
3137
|
}
|
|
@@ -3501,10 +3501,10 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
3501
3501
|
blockedBy: cloudResult.approved ? void 0 : "team-policy",
|
|
3502
3502
|
blockedByLabel: "Organization Policy (SaaS)"
|
|
3503
3503
|
};
|
|
3504
|
-
} catch (
|
|
3505
|
-
const error =
|
|
3506
|
-
if (error.name === "AbortError" || error.message?.includes("Aborted")) throw
|
|
3507
|
-
throw
|
|
3504
|
+
} catch (err2) {
|
|
3505
|
+
const error = err2;
|
|
3506
|
+
if (error.name === "AbortError" || error.message?.includes("Aborted")) throw err2;
|
|
3507
|
+
throw err2;
|
|
3508
3508
|
}
|
|
3509
3509
|
})()
|
|
3510
3510
|
);
|
|
@@ -3597,10 +3597,10 @@ REASON: Action blocked because no approval channels are available. (Native/Brows
|
|
|
3597
3597
|
}
|
|
3598
3598
|
};
|
|
3599
3599
|
for (const p of racePromises) {
|
|
3600
|
-
p.then(finish).catch((
|
|
3601
|
-
if (
|
|
3600
|
+
p.then(finish).catch((err2) => {
|
|
3601
|
+
if (err2.name === "AbortError" || err2.message?.includes("canceled") || err2.message?.includes("Aborted"))
|
|
3602
3602
|
return;
|
|
3603
|
-
if (
|
|
3603
|
+
if (err2.message === "Abandoned") {
|
|
3604
3604
|
finish({
|
|
3605
3605
|
approved: false,
|
|
3606
3606
|
reason: "Browser dashboard closed without making a decision.",
|
|
@@ -5615,21 +5615,21 @@ function atomicWriteSync2(filePath, data, options) {
|
|
|
5615
5615
|
const tmpPath = `${filePath}.${(0, import_crypto5.randomUUID)()}.tmp`;
|
|
5616
5616
|
try {
|
|
5617
5617
|
import_fs13.default.writeFileSync(tmpPath, data, options);
|
|
5618
|
-
} catch (
|
|
5618
|
+
} catch (err2) {
|
|
5619
5619
|
try {
|
|
5620
5620
|
import_fs13.default.unlinkSync(tmpPath);
|
|
5621
5621
|
} catch {
|
|
5622
5622
|
}
|
|
5623
|
-
throw
|
|
5623
|
+
throw err2;
|
|
5624
5624
|
}
|
|
5625
5625
|
try {
|
|
5626
5626
|
import_fs13.default.renameSync(tmpPath, filePath);
|
|
5627
|
-
} catch (
|
|
5627
|
+
} catch (err2) {
|
|
5628
5628
|
try {
|
|
5629
5629
|
import_fs13.default.unlinkSync(tmpPath);
|
|
5630
5630
|
} catch {
|
|
5631
5631
|
}
|
|
5632
|
-
throw
|
|
5632
|
+
throw err2;
|
|
5633
5633
|
}
|
|
5634
5634
|
}
|
|
5635
5635
|
function redactArgs(value) {
|
|
@@ -5808,6 +5808,16 @@ function startActivitySocket() {
|
|
|
5808
5808
|
sessionHistory.recordTestFail(data.ts);
|
|
5809
5809
|
return;
|
|
5810
5810
|
}
|
|
5811
|
+
if (data.status === "snapshot") {
|
|
5812
|
+
broadcast("snapshot", {
|
|
5813
|
+
hash: data.hash,
|
|
5814
|
+
tool: data.tool,
|
|
5815
|
+
argsSummary: data.argsSummary,
|
|
5816
|
+
fileCount: data.fileCount,
|
|
5817
|
+
ts: data.ts
|
|
5818
|
+
});
|
|
5819
|
+
return;
|
|
5820
|
+
}
|
|
5811
5821
|
if (data.status === "pending") {
|
|
5812
5822
|
broadcast("activity", {
|
|
5813
5823
|
id: data.id,
|
|
@@ -5942,21 +5952,21 @@ function patchConfig(configPath, patch) {
|
|
|
5942
5952
|
const tmp = configPath + ".node9-tmp";
|
|
5943
5953
|
try {
|
|
5944
5954
|
import_fs14.default.writeFileSync(tmp, JSON.stringify(config, null, 2), { mode: 384 });
|
|
5945
|
-
} catch (
|
|
5955
|
+
} catch (err2) {
|
|
5946
5956
|
try {
|
|
5947
5957
|
import_fs14.default.unlinkSync(tmp);
|
|
5948
5958
|
} catch {
|
|
5949
5959
|
}
|
|
5950
|
-
throw
|
|
5960
|
+
throw err2;
|
|
5951
5961
|
}
|
|
5952
5962
|
try {
|
|
5953
5963
|
import_fs14.default.renameSync(tmp, configPath);
|
|
5954
|
-
} catch (
|
|
5964
|
+
} catch (err2) {
|
|
5955
5965
|
try {
|
|
5956
5966
|
import_fs14.default.unlinkSync(tmp);
|
|
5957
5967
|
} catch {
|
|
5958
5968
|
}
|
|
5959
|
-
throw
|
|
5969
|
+
throw err2;
|
|
5960
5970
|
}
|
|
5961
5971
|
}
|
|
5962
5972
|
var import_fs14, import_path17, import_os12, GLOBAL_CONFIG_PATH;
|
|
@@ -6208,11 +6218,11 @@ data: ${JSON.stringify(item.data)}
|
|
|
6208
6218
|
e.earlyDecision = decision;
|
|
6209
6219
|
e.earlyReason = result.reason;
|
|
6210
6220
|
}
|
|
6211
|
-
}).catch((
|
|
6221
|
+
}).catch((err2) => {
|
|
6212
6222
|
const e = pending.get(id);
|
|
6213
6223
|
if (!e) return;
|
|
6214
6224
|
clearTimeout(e.timer);
|
|
6215
|
-
const reason =
|
|
6225
|
+
const reason = err2?.reason || "No response \u2014 request timed out";
|
|
6216
6226
|
if (e.waiter) e.waiter("deny", reason);
|
|
6217
6227
|
else {
|
|
6218
6228
|
e.earlyDecision = "deny";
|
|
@@ -6339,8 +6349,8 @@ data: ${JSON.stringify(item.data)}
|
|
|
6339
6349
|
const s = getGlobalSettings();
|
|
6340
6350
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
6341
6351
|
return res.end(JSON.stringify({ ...s, autoStarted }));
|
|
6342
|
-
} catch (
|
|
6343
|
-
console.error(import_chalk2.default.red("[node9 daemon] GET /settings failed:"),
|
|
6352
|
+
} catch (err2) {
|
|
6353
|
+
console.error(import_chalk2.default.red("[node9 daemon] GET /settings failed:"), err2);
|
|
6344
6354
|
res.writeHead(500, { "Content-Type": "application/json" });
|
|
6345
6355
|
return res.end(JSON.stringify({ error: "internal" }));
|
|
6346
6356
|
}
|
|
@@ -6364,8 +6374,8 @@ data: ${JSON.stringify(item.data)}
|
|
|
6364
6374
|
};
|
|
6365
6375
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
6366
6376
|
return res.end(JSON.stringify(status));
|
|
6367
|
-
} catch (
|
|
6368
|
-
console.error(import_chalk2.default.red("[node9 daemon] GET /status failed:"),
|
|
6377
|
+
} catch (err2) {
|
|
6378
|
+
console.error(import_chalk2.default.red("[node9 daemon] GET /status failed:"), err2);
|
|
6369
6379
|
res.writeHead(500, { "Content-Type": "application/json" });
|
|
6370
6380
|
return res.end(JSON.stringify({ error: "internal" }));
|
|
6371
6381
|
}
|
|
@@ -6405,8 +6415,8 @@ data: ${JSON.stringify(item.data)}
|
|
|
6405
6415
|
const s = getGlobalSettings();
|
|
6406
6416
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
6407
6417
|
return res.end(JSON.stringify({ hasKey: hasStoredSlackKey(), enabled: s.slackEnabled }));
|
|
6408
|
-
} catch (
|
|
6409
|
-
console.error(import_chalk2.default.red("[node9 daemon] GET /slack-status failed:"),
|
|
6418
|
+
} catch (err2) {
|
|
6419
|
+
console.error(import_chalk2.default.red("[node9 daemon] GET /slack-status failed:"), err2);
|
|
6410
6420
|
res.writeHead(500, { "Content-Type": "application/json" });
|
|
6411
6421
|
return res.end(JSON.stringify({ error: "internal" }));
|
|
6412
6422
|
}
|
|
@@ -6566,10 +6576,10 @@ data: ${JSON.stringify(item.data)}
|
|
|
6566
6576
|
broadcast("suggestion:resolved", { id, status: "applied" });
|
|
6567
6577
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
6568
6578
|
return res.end(JSON.stringify({ ok: true }));
|
|
6569
|
-
} catch (
|
|
6570
|
-
console.error(import_chalk2.default.red("[node9 daemon] POST /suggestions/:id/apply failed:"),
|
|
6579
|
+
} catch (err2) {
|
|
6580
|
+
console.error(import_chalk2.default.red("[node9 daemon] POST /suggestions/:id/apply failed:"), err2);
|
|
6571
6581
|
res.writeHead(500, { "Content-Type": "application/json" });
|
|
6572
|
-
return res.end(JSON.stringify({ error: String(
|
|
6582
|
+
return res.end(JSON.stringify({ error: String(err2) }));
|
|
6573
6583
|
}
|
|
6574
6584
|
}
|
|
6575
6585
|
if (req.method === "POST" && pathname.startsWith("/suggestions/") && pathname.endsWith("/dismiss")) {
|
|
@@ -6816,8 +6826,8 @@ function renderResult(activity, result) {
|
|
|
6816
6826
|
status = import_chalk17.default.red("\u2717 BLOCK");
|
|
6817
6827
|
}
|
|
6818
6828
|
if (process.stdout.isTTY) {
|
|
6819
|
-
|
|
6820
|
-
|
|
6829
|
+
import_readline5.default.clearLine(process.stdout, 0);
|
|
6830
|
+
import_readline5.default.cursorTo(process.stdout, 0);
|
|
6821
6831
|
}
|
|
6822
6832
|
console.log(`${base} ${status}`);
|
|
6823
6833
|
}
|
|
@@ -6827,9 +6837,9 @@ function renderPending(activity) {
|
|
|
6827
6837
|
}
|
|
6828
6838
|
async function ensureDaemon() {
|
|
6829
6839
|
let pidPort = null;
|
|
6830
|
-
if (
|
|
6840
|
+
if (import_fs25.default.existsSync(PID_FILE)) {
|
|
6831
6841
|
try {
|
|
6832
|
-
const { port } = JSON.parse(
|
|
6842
|
+
const { port } = JSON.parse(import_fs25.default.readFileSync(PID_FILE, "utf-8"));
|
|
6833
6843
|
pidPort = port;
|
|
6834
6844
|
} catch {
|
|
6835
6845
|
console.error(import_chalk17.default.dim("\u26A0\uFE0F Could not read PID file; falling back to default port."));
|
|
@@ -6966,7 +6976,7 @@ async function startTail(options = {}) {
|
|
|
6966
6976
|
res.resume();
|
|
6967
6977
|
}
|
|
6968
6978
|
);
|
|
6969
|
-
req2.once("error", (
|
|
6979
|
+
req2.once("error", (err2) => resolve({ ok: false, code: err2.code }));
|
|
6970
6980
|
req2.setTimeout(2e3, () => {
|
|
6971
6981
|
resolve({ ok: false, code: "ETIMEDOUT" });
|
|
6972
6982
|
req2.destroy();
|
|
@@ -6993,10 +7003,10 @@ async function startTail(options = {}) {
|
|
|
6993
7003
|
let cancelActiveCard = null;
|
|
6994
7004
|
const localAllowCounts = /* @__PURE__ */ new Map();
|
|
6995
7005
|
const canApprove = process.stdout.isTTY && process.stdin.isTTY;
|
|
6996
|
-
if (canApprove)
|
|
7006
|
+
if (canApprove) import_readline5.default.emitKeypressEvents(process.stdin);
|
|
6997
7007
|
function clearCard() {
|
|
6998
7008
|
if (cardLineCount > 0) {
|
|
6999
|
-
|
|
7009
|
+
import_readline5.default.moveCursor(process.stdout, 0, -cardLineCount);
|
|
7000
7010
|
process.stdout.write(ERASE_DOWN);
|
|
7001
7011
|
cardLineCount = 0;
|
|
7002
7012
|
}
|
|
@@ -7072,11 +7082,11 @@ async function startTail(options = {}) {
|
|
|
7072
7082
|
} else {
|
|
7073
7083
|
httpDecision = action;
|
|
7074
7084
|
}
|
|
7075
|
-
postDecisionHttp(req2.id, httpDecision, csrfToken, port, httpOpts).catch((
|
|
7085
|
+
postDecisionHttp(req2.id, httpDecision, csrfToken, port, httpOpts).catch((err2) => {
|
|
7076
7086
|
try {
|
|
7077
|
-
|
|
7078
|
-
|
|
7079
|
-
`[tail] POST /decision failed: ${String(
|
|
7087
|
+
import_fs25.default.appendFileSync(
|
|
7088
|
+
import_path28.default.join(import_os21.default.homedir(), ".node9", "hook-debug.log"),
|
|
7089
|
+
`[tail] POST /decision failed: ${String(err2)}
|
|
7080
7090
|
`
|
|
7081
7091
|
);
|
|
7082
7092
|
} catch {
|
|
@@ -7174,8 +7184,8 @@ async function startTail(options = {}) {
|
|
|
7174
7184
|
clearCard();
|
|
7175
7185
|
process.stdout.write(SHOW_CURSOR);
|
|
7176
7186
|
if (process.stdout.isTTY) {
|
|
7177
|
-
|
|
7178
|
-
|
|
7187
|
+
import_readline5.default.clearLine(process.stdout, 0);
|
|
7188
|
+
import_readline5.default.cursorTo(process.stdout, 0);
|
|
7179
7189
|
}
|
|
7180
7190
|
console.log(import_chalk17.default.dim("\n\u{1F6F0}\uFE0F Disconnected."));
|
|
7181
7191
|
process.exit(0);
|
|
@@ -7190,7 +7200,7 @@ async function startTail(options = {}) {
|
|
|
7190
7200
|
let currentData = "";
|
|
7191
7201
|
res.on("error", () => {
|
|
7192
7202
|
});
|
|
7193
|
-
const rl =
|
|
7203
|
+
const rl = import_readline5.default.createInterface({ input: res, crlfDelay: Infinity });
|
|
7194
7204
|
rl.on("error", () => {
|
|
7195
7205
|
});
|
|
7196
7206
|
rl.on("line", (line) => {
|
|
@@ -7210,8 +7220,8 @@ async function startTail(options = {}) {
|
|
|
7210
7220
|
clearCard();
|
|
7211
7221
|
process.stdout.write(SHOW_CURSOR);
|
|
7212
7222
|
if (process.stdout.isTTY) {
|
|
7213
|
-
|
|
7214
|
-
|
|
7223
|
+
import_readline5.default.clearLine(process.stdout, 0);
|
|
7224
|
+
import_readline5.default.cursorTo(process.stdout, 0);
|
|
7215
7225
|
}
|
|
7216
7226
|
console.log(import_chalk17.default.red("\n\u274C Daemon disconnected."));
|
|
7217
7227
|
process.exit(1);
|
|
@@ -7289,6 +7299,18 @@ async function startTail(options = {}) {
|
|
|
7289
7299
|
const slowTool = /bash|shell|query|sql|agent/i.test(data.tool);
|
|
7290
7300
|
if (slowTool) renderPending(data);
|
|
7291
7301
|
}
|
|
7302
|
+
if (event === "snapshot") {
|
|
7303
|
+
const time = new Date(data.ts).toLocaleTimeString([], { hour12: false });
|
|
7304
|
+
const hash = data.hash ?? "";
|
|
7305
|
+
const summary = data.argsSummary ?? data.tool;
|
|
7306
|
+
const fileCount = data.fileCount ?? 0;
|
|
7307
|
+
const files = fileCount > 0 ? import_chalk17.default.dim(` \xB7 ${fileCount} file${fileCount === 1 ? "" : "s"}`) : "";
|
|
7308
|
+
process.stdout.write(
|
|
7309
|
+
`${import_chalk17.default.dim(time)} ${import_chalk17.default.cyan("\u{1F4F8} snapshot")} ${import_chalk17.default.dim(hash)} ${summary}${files}
|
|
7310
|
+
`
|
|
7311
|
+
);
|
|
7312
|
+
return;
|
|
7313
|
+
}
|
|
7292
7314
|
if (event === "activity-result") {
|
|
7293
7315
|
const original = activityPending.get(data.id);
|
|
7294
7316
|
if (original) {
|
|
@@ -7297,28 +7319,28 @@ async function startTail(options = {}) {
|
|
|
7297
7319
|
}
|
|
7298
7320
|
}
|
|
7299
7321
|
}
|
|
7300
|
-
req.on("error", (
|
|
7301
|
-
const msg =
|
|
7322
|
+
req.on("error", (err2) => {
|
|
7323
|
+
const msg = err2.code === "ECONNREFUSED" ? "Daemon is not running. Start it with: node9 daemon start" : err2.message;
|
|
7302
7324
|
console.error(import_chalk17.default.red(`
|
|
7303
7325
|
\u274C ${msg}`));
|
|
7304
7326
|
process.exit(1);
|
|
7305
7327
|
});
|
|
7306
7328
|
}
|
|
7307
|
-
var import_http2, import_chalk17,
|
|
7329
|
+
var import_http2, import_chalk17, import_fs25, import_os21, import_path28, import_readline5, import_child_process13, PID_FILE, ICONS, RESET2, BOLD2, RED, YELLOW, CYAN, GRAY, GREEN, HIDE_CURSOR, SHOW_CURSOR, ERASE_DOWN, DIVIDER;
|
|
7308
7330
|
var init_tail = __esm({
|
|
7309
7331
|
"src/tui/tail.ts"() {
|
|
7310
7332
|
"use strict";
|
|
7311
7333
|
import_http2 = __toESM(require("http"));
|
|
7312
7334
|
import_chalk17 = __toESM(require("chalk"));
|
|
7313
|
-
|
|
7314
|
-
|
|
7315
|
-
|
|
7316
|
-
|
|
7335
|
+
import_fs25 = __toESM(require("fs"));
|
|
7336
|
+
import_os21 = __toESM(require("os"));
|
|
7337
|
+
import_path28 = __toESM(require("path"));
|
|
7338
|
+
import_readline5 = __toESM(require("readline"));
|
|
7317
7339
|
import_child_process13 = require("child_process");
|
|
7318
7340
|
init_daemon2();
|
|
7319
7341
|
init_daemon();
|
|
7320
7342
|
init_core();
|
|
7321
|
-
PID_FILE =
|
|
7343
|
+
PID_FILE = import_path28.default.join(import_os21.default.homedir(), ".node9", "daemon.pid");
|
|
7322
7344
|
ICONS = {
|
|
7323
7345
|
bash: "\u{1F4BB}",
|
|
7324
7346
|
shell: "\u{1F4BB}",
|
|
@@ -7431,9 +7453,9 @@ function formatTimeLeft(resetsAt) {
|
|
|
7431
7453
|
return ` (${m}m left)`;
|
|
7432
7454
|
}
|
|
7433
7455
|
function safeReadJson(filePath) {
|
|
7434
|
-
if (!
|
|
7456
|
+
if (!import_fs26.default.existsSync(filePath)) return null;
|
|
7435
7457
|
try {
|
|
7436
|
-
return JSON.parse(
|
|
7458
|
+
return JSON.parse(import_fs26.default.readFileSync(filePath, "utf-8"));
|
|
7437
7459
|
} catch {
|
|
7438
7460
|
return null;
|
|
7439
7461
|
}
|
|
@@ -7454,12 +7476,12 @@ function countHooksInFile(filePath) {
|
|
|
7454
7476
|
return Object.keys(cfg.hooks).length;
|
|
7455
7477
|
}
|
|
7456
7478
|
function countRulesInDir(rulesDir) {
|
|
7457
|
-
if (!
|
|
7479
|
+
if (!import_fs26.default.existsSync(rulesDir)) return 0;
|
|
7458
7480
|
let count = 0;
|
|
7459
7481
|
try {
|
|
7460
|
-
for (const entry of
|
|
7482
|
+
for (const entry of import_fs26.default.readdirSync(rulesDir, { withFileTypes: true })) {
|
|
7461
7483
|
if (entry.isDirectory()) {
|
|
7462
|
-
count += countRulesInDir(
|
|
7484
|
+
count += countRulesInDir(import_path29.default.join(rulesDir, entry.name));
|
|
7463
7485
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
7464
7486
|
count++;
|
|
7465
7487
|
}
|
|
@@ -7470,46 +7492,46 @@ function countRulesInDir(rulesDir) {
|
|
|
7470
7492
|
}
|
|
7471
7493
|
function isSamePath(a, b) {
|
|
7472
7494
|
try {
|
|
7473
|
-
return
|
|
7495
|
+
return import_path29.default.resolve(a) === import_path29.default.resolve(b);
|
|
7474
7496
|
} catch {
|
|
7475
7497
|
return false;
|
|
7476
7498
|
}
|
|
7477
7499
|
}
|
|
7478
7500
|
function countConfigs(cwd) {
|
|
7479
|
-
const homeDir2 =
|
|
7480
|
-
const claudeDir =
|
|
7501
|
+
const homeDir2 = import_os22.default.homedir();
|
|
7502
|
+
const claudeDir = import_path29.default.join(homeDir2, ".claude");
|
|
7481
7503
|
let claudeMdCount = 0;
|
|
7482
7504
|
let rulesCount = 0;
|
|
7483
7505
|
let hooksCount = 0;
|
|
7484
7506
|
const userMcpServers = /* @__PURE__ */ new Set();
|
|
7485
7507
|
const projectMcpServers = /* @__PURE__ */ new Set();
|
|
7486
|
-
if (
|
|
7487
|
-
rulesCount += countRulesInDir(
|
|
7488
|
-
const userSettings =
|
|
7508
|
+
if (import_fs26.default.existsSync(import_path29.default.join(claudeDir, "CLAUDE.md"))) claudeMdCount++;
|
|
7509
|
+
rulesCount += countRulesInDir(import_path29.default.join(claudeDir, "rules"));
|
|
7510
|
+
const userSettings = import_path29.default.join(claudeDir, "settings.json");
|
|
7489
7511
|
for (const name of getMcpServerNames(userSettings)) userMcpServers.add(name);
|
|
7490
7512
|
hooksCount += countHooksInFile(userSettings);
|
|
7491
|
-
const userClaudeJson =
|
|
7513
|
+
const userClaudeJson = import_path29.default.join(homeDir2, ".claude.json");
|
|
7492
7514
|
for (const name of getMcpServerNames(userClaudeJson)) userMcpServers.add(name);
|
|
7493
7515
|
for (const name of getDisabledMcpServers(userClaudeJson, "disabledMcpServers")) {
|
|
7494
7516
|
userMcpServers.delete(name);
|
|
7495
7517
|
}
|
|
7496
7518
|
if (cwd) {
|
|
7497
|
-
if (
|
|
7498
|
-
if (
|
|
7499
|
-
const projectClaudeDir =
|
|
7519
|
+
if (import_fs26.default.existsSync(import_path29.default.join(cwd, "CLAUDE.md"))) claudeMdCount++;
|
|
7520
|
+
if (import_fs26.default.existsSync(import_path29.default.join(cwd, "CLAUDE.local.md"))) claudeMdCount++;
|
|
7521
|
+
const projectClaudeDir = import_path29.default.join(cwd, ".claude");
|
|
7500
7522
|
const overlapsUserScope = isSamePath(projectClaudeDir, claudeDir);
|
|
7501
7523
|
if (!overlapsUserScope) {
|
|
7502
|
-
if (
|
|
7503
|
-
rulesCount += countRulesInDir(
|
|
7504
|
-
const projSettings =
|
|
7524
|
+
if (import_fs26.default.existsSync(import_path29.default.join(projectClaudeDir, "CLAUDE.md"))) claudeMdCount++;
|
|
7525
|
+
rulesCount += countRulesInDir(import_path29.default.join(projectClaudeDir, "rules"));
|
|
7526
|
+
const projSettings = import_path29.default.join(projectClaudeDir, "settings.json");
|
|
7505
7527
|
for (const name of getMcpServerNames(projSettings)) projectMcpServers.add(name);
|
|
7506
7528
|
hooksCount += countHooksInFile(projSettings);
|
|
7507
7529
|
}
|
|
7508
|
-
if (
|
|
7509
|
-
const localSettings =
|
|
7530
|
+
if (import_fs26.default.existsSync(import_path29.default.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
|
|
7531
|
+
const localSettings = import_path29.default.join(projectClaudeDir, "settings.local.json");
|
|
7510
7532
|
for (const name of getMcpServerNames(localSettings)) projectMcpServers.add(name);
|
|
7511
7533
|
hooksCount += countHooksInFile(localSettings);
|
|
7512
|
-
const mcpJsonServers = getMcpServerNames(
|
|
7534
|
+
const mcpJsonServers = getMcpServerNames(import_path29.default.join(cwd, ".mcp.json"));
|
|
7513
7535
|
const disabledMcpJson = getDisabledMcpServers(localSettings, "disabledMcpjsonServers");
|
|
7514
7536
|
for (const name of disabledMcpJson) mcpJsonServers.delete(name);
|
|
7515
7537
|
for (const name of mcpJsonServers) projectMcpServers.add(name);
|
|
@@ -7622,11 +7644,11 @@ async function main() {
|
|
|
7622
7644
|
try {
|
|
7623
7645
|
const cwd = stdin.cwd ?? process.cwd();
|
|
7624
7646
|
for (const configPath of [
|
|
7625
|
-
|
|
7626
|
-
|
|
7647
|
+
import_path29.default.join(cwd, "node9.config.json"),
|
|
7648
|
+
import_path29.default.join(import_os22.default.homedir(), ".node9", "config.json")
|
|
7627
7649
|
]) {
|
|
7628
|
-
if (!
|
|
7629
|
-
const cfg = JSON.parse(
|
|
7650
|
+
if (!import_fs26.default.existsSync(configPath)) continue;
|
|
7651
|
+
const cfg = JSON.parse(import_fs26.default.readFileSync(configPath, "utf-8"));
|
|
7630
7652
|
const hud = cfg.settings?.hud;
|
|
7631
7653
|
if (hud && "showEnvironmentCounts" in hud) return hud.showEnvironmentCounts !== false;
|
|
7632
7654
|
}
|
|
@@ -7644,13 +7666,13 @@ async function main() {
|
|
|
7644
7666
|
renderOffline();
|
|
7645
7667
|
}
|
|
7646
7668
|
}
|
|
7647
|
-
var
|
|
7669
|
+
var import_fs26, import_path29, import_os22, import_http3, RESET3, BOLD3, DIM, RED2, GREEN2, YELLOW2, BLUE, MAGENTA, CYAN2, WHITE, BAR_FILLED, BAR_EMPTY, BAR_WIDTH;
|
|
7648
7670
|
var init_hud = __esm({
|
|
7649
7671
|
"src/cli/hud.ts"() {
|
|
7650
7672
|
"use strict";
|
|
7651
|
-
|
|
7652
|
-
|
|
7653
|
-
|
|
7673
|
+
import_fs26 = __toESM(require("fs"));
|
|
7674
|
+
import_path29 = __toESM(require("path"));
|
|
7675
|
+
import_os22 = __toESM(require("os"));
|
|
7654
7676
|
import_http3 = __toESM(require("http"));
|
|
7655
7677
|
init_daemon();
|
|
7656
7678
|
RESET3 = "\x1B[0m";
|
|
@@ -7679,6 +7701,16 @@ var import_path14 = __toESM(require("path"));
|
|
|
7679
7701
|
var import_os10 = __toESM(require("os"));
|
|
7680
7702
|
var import_chalk = __toESM(require("chalk"));
|
|
7681
7703
|
var import_prompts = require("@inquirer/prompts");
|
|
7704
|
+
var NODE9_MCP_SERVER_ENTRY = { command: "node9", args: ["mcp-server"] };
|
|
7705
|
+
function hasNode9McpServer(servers) {
|
|
7706
|
+
const entry = servers["node9"];
|
|
7707
|
+
return !!entry && entry.command === "node9" && Array.isArray(entry.args) && entry.args[0] === "mcp-server";
|
|
7708
|
+
}
|
|
7709
|
+
function removeNode9McpServer(servers) {
|
|
7710
|
+
if (!hasNode9McpServer(servers)) return false;
|
|
7711
|
+
delete servers["node9"];
|
|
7712
|
+
return true;
|
|
7713
|
+
}
|
|
7682
7714
|
function printDaemonTip() {
|
|
7683
7715
|
console.log(
|
|
7684
7716
|
import_chalk.default.cyan("\n \u{1F4A1} Node9 will protect you automatically using Native OS popups.") + import_chalk.default.white("\n To view your history or manage persistent rules, run:") + import_chalk.default.green("\n node9 daemon --openui")
|
|
@@ -7736,6 +7768,10 @@ function teardownClaude() {
|
|
|
7736
7768
|
const claudeConfig = readJson(mcpPath);
|
|
7737
7769
|
if (claudeConfig?.mcpServers) {
|
|
7738
7770
|
let mcpChanged = false;
|
|
7771
|
+
if (removeNode9McpServer(claudeConfig.mcpServers)) {
|
|
7772
|
+
mcpChanged = true;
|
|
7773
|
+
console.log(import_chalk.default.green(" \u2705 Removed node9 MCP server entry from ~/.claude.json"));
|
|
7774
|
+
}
|
|
7739
7775
|
for (const [name, server] of Object.entries(claudeConfig.mcpServers)) {
|
|
7740
7776
|
if (server.command === "node9" && Array.isArray(server.args) && server.args.length > 0) {
|
|
7741
7777
|
const [originalCmd, ...originalArgs] = server.args;
|
|
@@ -7779,6 +7815,10 @@ function teardownGemini() {
|
|
|
7779
7815
|
}
|
|
7780
7816
|
}
|
|
7781
7817
|
if (settings.mcpServers) {
|
|
7818
|
+
if (removeNode9McpServer(settings.mcpServers)) {
|
|
7819
|
+
changed = true;
|
|
7820
|
+
console.log(import_chalk.default.green(" \u2705 Removed node9 MCP server entry from ~/.gemini/settings.json"));
|
|
7821
|
+
}
|
|
7782
7822
|
for (const [name, server] of Object.entries(settings.mcpServers)) {
|
|
7783
7823
|
if (server.command === "node9" && Array.isArray(server.args) && server.args.length > 0) {
|
|
7784
7824
|
const [originalCmd, ...originalArgs] = server.args;
|
|
@@ -7807,6 +7847,10 @@ function teardownCursor() {
|
|
|
7807
7847
|
return;
|
|
7808
7848
|
}
|
|
7809
7849
|
let changed = false;
|
|
7850
|
+
if (removeNode9McpServer(mcpConfig.mcpServers)) {
|
|
7851
|
+
changed = true;
|
|
7852
|
+
console.log(import_chalk.default.green(" \u2705 Removed node9 MCP server entry from ~/.cursor/mcp.json"));
|
|
7853
|
+
}
|
|
7810
7854
|
for (const [name, server] of Object.entries(mcpConfig.mcpServers)) {
|
|
7811
7855
|
if (server.command === "node9" && Array.isArray(server.args) && server.args.length > 0) {
|
|
7812
7856
|
const [originalCmd, ...originalArgs] = server.args;
|
|
@@ -7832,6 +7876,7 @@ async function setupClaude() {
|
|
|
7832
7876
|
const claudeConfig = readJson(mcpPath) ?? {};
|
|
7833
7877
|
const settings = readJson(hooksPath) ?? {};
|
|
7834
7878
|
const servers = claudeConfig.mcpServers ?? {};
|
|
7879
|
+
let hooksChanged = false;
|
|
7835
7880
|
let anythingChanged = false;
|
|
7836
7881
|
if (!settings.hooks) settings.hooks = {};
|
|
7837
7882
|
const hasPreHook = settings.hooks.PreToolUse?.some(
|
|
@@ -7844,6 +7889,7 @@ async function setupClaude() {
|
|
|
7844
7889
|
hooks: [{ type: "command", command: fullPathCommand("check"), timeout: 60 }]
|
|
7845
7890
|
});
|
|
7846
7891
|
console.log(import_chalk.default.green(" \u2705 PreToolUse hook added \u2192 node9 check"));
|
|
7892
|
+
hooksChanged = true;
|
|
7847
7893
|
anythingChanged = true;
|
|
7848
7894
|
}
|
|
7849
7895
|
const hasPostHook = settings.hooks.PostToolUse?.some(
|
|
@@ -7856,9 +7902,17 @@ async function setupClaude() {
|
|
|
7856
7902
|
hooks: [{ type: "command", command: fullPathCommand("log"), timeout: 600 }]
|
|
7857
7903
|
});
|
|
7858
7904
|
console.log(import_chalk.default.green(" \u2705 PostToolUse hook added \u2192 node9 log"));
|
|
7905
|
+
hooksChanged = true;
|
|
7859
7906
|
anythingChanged = true;
|
|
7860
7907
|
}
|
|
7861
|
-
if (
|
|
7908
|
+
if (!hasNode9McpServer(servers)) {
|
|
7909
|
+
servers["node9"] = NODE9_MCP_SERVER_ENTRY;
|
|
7910
|
+
claudeConfig.mcpServers = servers;
|
|
7911
|
+
writeJson(mcpPath, claudeConfig);
|
|
7912
|
+
console.log(import_chalk.default.green(" \u2705 node9 MCP server added \u2192 node9 mcp-server"));
|
|
7913
|
+
anythingChanged = true;
|
|
7914
|
+
}
|
|
7915
|
+
if (hooksChanged) {
|
|
7862
7916
|
writeJson(hooksPath, settings);
|
|
7863
7917
|
console.log("");
|
|
7864
7918
|
}
|
|
@@ -7906,6 +7960,7 @@ async function setupGemini() {
|
|
|
7906
7960
|
const settingsPath = import_path14.default.join(homeDir2, ".gemini", "settings.json");
|
|
7907
7961
|
const settings = readJson(settingsPath) ?? {};
|
|
7908
7962
|
const servers = settings.mcpServers ?? {};
|
|
7963
|
+
let hooksChanged = false;
|
|
7909
7964
|
let anythingChanged = false;
|
|
7910
7965
|
if (!settings.hooks) settings.hooks = {};
|
|
7911
7966
|
const hasBeforeHook = Array.isArray(settings.hooks.BeforeTool) && settings.hooks.BeforeTool.some(
|
|
@@ -7926,6 +7981,7 @@ async function setupGemini() {
|
|
|
7926
7981
|
]
|
|
7927
7982
|
});
|
|
7928
7983
|
console.log(import_chalk.default.green(" \u2705 BeforeTool hook added \u2192 node9 check"));
|
|
7984
|
+
hooksChanged = true;
|
|
7929
7985
|
anythingChanged = true;
|
|
7930
7986
|
}
|
|
7931
7987
|
const hasAfterHook = Array.isArray(settings.hooks.AfterTool) && settings.hooks.AfterTool.some(
|
|
@@ -7939,9 +7995,17 @@ async function setupGemini() {
|
|
|
7939
7995
|
hooks: [{ name: "node9-log", type: "command", command: fullPathCommand("log") }]
|
|
7940
7996
|
});
|
|
7941
7997
|
console.log(import_chalk.default.green(" \u2705 AfterTool hook added \u2192 node9 log"));
|
|
7998
|
+
hooksChanged = true;
|
|
7942
7999
|
anythingChanged = true;
|
|
7943
8000
|
}
|
|
7944
|
-
if (
|
|
8001
|
+
if (!hasNode9McpServer(servers)) {
|
|
8002
|
+
servers["node9"] = NODE9_MCP_SERVER_ENTRY;
|
|
8003
|
+
settings.mcpServers = servers;
|
|
8004
|
+
console.log(import_chalk.default.green(" \u2705 node9 MCP server added \u2192 node9 mcp-server"));
|
|
8005
|
+
hooksChanged = true;
|
|
8006
|
+
anythingChanged = true;
|
|
8007
|
+
}
|
|
8008
|
+
if (hooksChanged) {
|
|
7945
8009
|
writeJson(settingsPath, settings);
|
|
7946
8010
|
console.log("");
|
|
7947
8011
|
}
|
|
@@ -7988,10 +8052,10 @@ function detectAgents(homeDir2 = import_os10.default.homedir()) {
|
|
|
7988
8052
|
const exists = (p) => {
|
|
7989
8053
|
try {
|
|
7990
8054
|
return import_fs11.default.existsSync(p);
|
|
7991
|
-
} catch (
|
|
7992
|
-
const code =
|
|
8055
|
+
} catch (err2) {
|
|
8056
|
+
const code = err2.code;
|
|
7993
8057
|
if (code !== "ENOENT") {
|
|
7994
|
-
process.stderr.write(`[node9] detectAgents: cannot access ${p}: ${code ?? String(
|
|
8058
|
+
process.stderr.write(`[node9] detectAgents: cannot access ${p}: ${code ?? String(err2)}
|
|
7995
8059
|
`);
|
|
7996
8060
|
}
|
|
7997
8061
|
return false;
|
|
@@ -8009,6 +8073,13 @@ async function setupCursor() {
|
|
|
8009
8073
|
const mcpConfig = readJson(mcpPath) ?? {};
|
|
8010
8074
|
const servers = mcpConfig.mcpServers ?? {};
|
|
8011
8075
|
let anythingChanged = false;
|
|
8076
|
+
if (!hasNode9McpServer(servers)) {
|
|
8077
|
+
servers["node9"] = NODE9_MCP_SERVER_ENTRY;
|
|
8078
|
+
mcpConfig.mcpServers = servers;
|
|
8079
|
+
writeJson(mcpPath, mcpConfig);
|
|
8080
|
+
console.log(import_chalk.default.green(" \u2705 node9 MCP server added \u2192 node9 mcp-server"));
|
|
8081
|
+
anythingChanged = true;
|
|
8082
|
+
}
|
|
8012
8083
|
const serversToWrap = [];
|
|
8013
8084
|
for (const [name, server] of Object.entries(servers)) {
|
|
8014
8085
|
if (!server.command || server.command === "node9") continue;
|
|
@@ -8108,9 +8179,9 @@ function teardownHud() {
|
|
|
8108
8179
|
// src/cli.ts
|
|
8109
8180
|
init_daemon2();
|
|
8110
8181
|
var import_chalk18 = __toESM(require("chalk"));
|
|
8111
|
-
var
|
|
8112
|
-
var
|
|
8113
|
-
var
|
|
8182
|
+
var import_fs27 = __toESM(require("fs"));
|
|
8183
|
+
var import_path30 = __toESM(require("path"));
|
|
8184
|
+
var import_os23 = __toESM(require("os"));
|
|
8114
8185
|
var import_prompts2 = require("@inquirer/prompts");
|
|
8115
8186
|
|
|
8116
8187
|
// src/utils/duration.ts
|
|
@@ -8345,8 +8416,29 @@ init_policy();
|
|
|
8345
8416
|
var import_child_process8 = require("child_process");
|
|
8346
8417
|
var import_crypto7 = __toESM(require("crypto"));
|
|
8347
8418
|
var import_fs17 = __toESM(require("fs"));
|
|
8419
|
+
var import_net3 = __toESM(require("net"));
|
|
8348
8420
|
var import_path19 = __toESM(require("path"));
|
|
8349
8421
|
var import_os13 = __toESM(require("os"));
|
|
8422
|
+
var ACTIVITY_SOCKET_PATH3 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : import_path19.default.join(import_os13.default.tmpdir(), "node9-activity.sock");
|
|
8423
|
+
function notifySnapshotTaken(hash, tool, argsSummary, fileCount) {
|
|
8424
|
+
try {
|
|
8425
|
+
const payload = JSON.stringify({
|
|
8426
|
+
status: "snapshot",
|
|
8427
|
+
hash,
|
|
8428
|
+
tool,
|
|
8429
|
+
argsSummary,
|
|
8430
|
+
fileCount,
|
|
8431
|
+
ts: Date.now()
|
|
8432
|
+
});
|
|
8433
|
+
const sock = import_net3.default.createConnection(ACTIVITY_SOCKET_PATH3);
|
|
8434
|
+
sock.on("connect", () => {
|
|
8435
|
+
sock.end(payload);
|
|
8436
|
+
});
|
|
8437
|
+
sock.on("error", () => {
|
|
8438
|
+
});
|
|
8439
|
+
} catch {
|
|
8440
|
+
}
|
|
8441
|
+
}
|
|
8350
8442
|
var SNAPSHOT_STACK_PATH = import_path19.default.join(import_os13.default.homedir(), ".node9", "snapshots.json");
|
|
8351
8443
|
var UNDO_LATEST_PATH = import_path19.default.join(import_os13.default.homedir(), ".node9", "undo_latest.txt");
|
|
8352
8444
|
var MAX_SNAPSHOTS = 10;
|
|
@@ -8566,13 +8658,15 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
|
|
|
8566
8658
|
}
|
|
8567
8659
|
if (cwdCount > MAX_SNAPSHOTS) stack.splice(oldestCwdIdx, 1);
|
|
8568
8660
|
writeStack(stack);
|
|
8661
|
+
const entry = stack[stack.length - 1];
|
|
8662
|
+
notifySnapshotTaken(commitHash.slice(0, 7), tool, entry.argsSummary, capturedFiles.length);
|
|
8569
8663
|
import_fs17.default.writeFileSync(UNDO_LATEST_PATH, commitHash);
|
|
8570
8664
|
if (shouldGc) {
|
|
8571
8665
|
(0, import_child_process8.spawn)("git", ["gc", "--auto"], { env: shadowEnv, detached: true, stdio: "ignore" }).unref();
|
|
8572
8666
|
}
|
|
8573
8667
|
return commitHash;
|
|
8574
|
-
} catch (
|
|
8575
|
-
if (process.env.NODE9_DEBUG === "1") console.error("[Node9 Undo Engine Error]:",
|
|
8668
|
+
} catch (err2) {
|
|
8669
|
+
if (process.env.NODE9_DEBUG === "1") console.error("[Node9 Undo Engine Error]:", err2);
|
|
8576
8670
|
return null;
|
|
8577
8671
|
} finally {
|
|
8578
8672
|
if (indexFile) {
|
|
@@ -8676,11 +8770,11 @@ function registerCheckCommand(program2) {
|
|
|
8676
8770
|
let payload = JSON.parse(raw);
|
|
8677
8771
|
try {
|
|
8678
8772
|
payload = JSON.parse(raw);
|
|
8679
|
-
} catch (
|
|
8773
|
+
} catch (err2) {
|
|
8680
8774
|
const tempConfig = getConfig();
|
|
8681
8775
|
if (process.env.NODE9_DEBUG === "1" || tempConfig.settings.enableHookLogDebug) {
|
|
8682
8776
|
const logPath = import_path20.default.join(import_os14.default.homedir(), ".node9", "hook-debug.log");
|
|
8683
|
-
const errMsg =
|
|
8777
|
+
const errMsg = err2 instanceof Error ? err2.message : String(err2);
|
|
8684
8778
|
import_fs18.default.appendFileSync(
|
|
8685
8779
|
logPath,
|
|
8686
8780
|
`[${(/* @__PURE__ */ new Date()).toISOString()}] JSON_PARSE_ERROR: ${errMsg}
|
|
@@ -8801,10 +8895,10 @@ RAW: ${raw}
|
|
|
8801
8895
|
...result,
|
|
8802
8896
|
blockedByLabel: result.blockedByLabel
|
|
8803
8897
|
});
|
|
8804
|
-
} catch (
|
|
8898
|
+
} catch (err2) {
|
|
8805
8899
|
if (process.env.NODE9_DEBUG === "1") {
|
|
8806
8900
|
const logPath = import_path20.default.join(import_os14.default.homedir(), ".node9", "hook-debug.log");
|
|
8807
|
-
const errMsg =
|
|
8901
|
+
const errMsg = err2 instanceof Error ? err2.message : String(err2);
|
|
8808
8902
|
import_fs18.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] ERROR: ${errMsg}
|
|
8809
8903
|
`);
|
|
8810
8904
|
}
|
|
@@ -8950,8 +9044,8 @@ function registerLogCommand(program2) {
|
|
|
8950
9044
|
if (shouldSnapshot(tool, {}, config)) {
|
|
8951
9045
|
await createShadowSnapshot("unknown", {}, config.policy.snapshot.ignorePaths);
|
|
8952
9046
|
}
|
|
8953
|
-
} catch (
|
|
8954
|
-
const msg =
|
|
9047
|
+
} catch (err2) {
|
|
9048
|
+
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
8955
9049
|
process.stderr.write(`[Node9] audit log error: ${msg}
|
|
8956
9050
|
`);
|
|
8957
9051
|
const debugPath = import_path21.default.join(import_os15.default.homedir(), ".node9", "hook-debug.log");
|
|
@@ -10337,6 +10431,264 @@ function registerMcpGatewayCommand(program2) {
|
|
|
10337
10431
|
});
|
|
10338
10432
|
}
|
|
10339
10433
|
|
|
10434
|
+
// src/mcp-server/index.ts
|
|
10435
|
+
var import_readline4 = __toESM(require("readline"));
|
|
10436
|
+
var import_fs24 = __toESM(require("fs"));
|
|
10437
|
+
var import_os20 = __toESM(require("os"));
|
|
10438
|
+
var import_path27 = __toESM(require("path"));
|
|
10439
|
+
init_core();
|
|
10440
|
+
init_daemon();
|
|
10441
|
+
init_shields();
|
|
10442
|
+
function ok(id, result) {
|
|
10443
|
+
return JSON.stringify({ jsonrpc: "2.0", id: id ?? null, result });
|
|
10444
|
+
}
|
|
10445
|
+
function err(id, code, message) {
|
|
10446
|
+
return JSON.stringify({ jsonrpc: "2.0", id: id ?? null, error: { code, message } });
|
|
10447
|
+
}
|
|
10448
|
+
var TOOLS = [
|
|
10449
|
+
{
|
|
10450
|
+
name: "node9_status",
|
|
10451
|
+
description: "Show the current node9 protection status: mode, daemon state, undo engine, pause state, active shields, and whether agent hooks are wired. Use this to understand what protection is active before doing risky work.",
|
|
10452
|
+
inputSchema: { type: "object", properties: {}, required: [] }
|
|
10453
|
+
},
|
|
10454
|
+
{
|
|
10455
|
+
name: "node9_config_get",
|
|
10456
|
+
description: "Read the current node9 configuration: security mode, approver channels, timeouts, DLP settings, and the number of active smart rules. Returns the merged config (env > cloud > project > global > defaults).",
|
|
10457
|
+
inputSchema: { type: "object", properties: {}, required: [] }
|
|
10458
|
+
},
|
|
10459
|
+
{
|
|
10460
|
+
name: "node9_shield_list",
|
|
10461
|
+
description: "List all available node9 shields and which ones are currently active. Shields are pre-packaged rule sets for specific services (postgres, aws, github, filesystem).",
|
|
10462
|
+
inputSchema: { type: "object", properties: {}, required: [] }
|
|
10463
|
+
},
|
|
10464
|
+
{
|
|
10465
|
+
name: "node9_shield_enable",
|
|
10466
|
+
description: "Enable a node9 shield for a specific service. Shields only add protection \u2014 they cannot be used to weaken or bypass node9. Use node9_shield_list to see available shield names.",
|
|
10467
|
+
inputSchema: {
|
|
10468
|
+
type: "object",
|
|
10469
|
+
properties: {
|
|
10470
|
+
service: {
|
|
10471
|
+
type: "string",
|
|
10472
|
+
description: 'Shield name to enable (e.g. "postgres", "aws", "github", "filesystem").'
|
|
10473
|
+
}
|
|
10474
|
+
},
|
|
10475
|
+
required: ["service"]
|
|
10476
|
+
}
|
|
10477
|
+
},
|
|
10478
|
+
{
|
|
10479
|
+
name: "node9_undo_list",
|
|
10480
|
+
description: "List the node9 snapshot history. Each entry shows the git hash, tool that triggered it, a short summary, affected files, working directory, and timestamp. Use this to find a hash before calling node9_undo_revert.",
|
|
10481
|
+
inputSchema: { type: "object", properties: {}, required: [] }
|
|
10482
|
+
},
|
|
10483
|
+
{
|
|
10484
|
+
name: "node9_undo_revert",
|
|
10485
|
+
description: "Revert the working directory to a specific node9 snapshot. Call node9_undo_list first to find the hash you want to restore. WARNING: this overwrites current files \u2014 any unsaved work will be lost.",
|
|
10486
|
+
inputSchema: {
|
|
10487
|
+
type: "object",
|
|
10488
|
+
properties: {
|
|
10489
|
+
hash: {
|
|
10490
|
+
type: "string",
|
|
10491
|
+
description: "The full git commit hash from node9_undo_list."
|
|
10492
|
+
},
|
|
10493
|
+
cwd: {
|
|
10494
|
+
type: "string",
|
|
10495
|
+
description: "Absolute path to the project directory. Defaults to process.cwd()."
|
|
10496
|
+
}
|
|
10497
|
+
},
|
|
10498
|
+
required: ["hash"]
|
|
10499
|
+
}
|
|
10500
|
+
}
|
|
10501
|
+
];
|
|
10502
|
+
function handleStatus() {
|
|
10503
|
+
const config = getConfig();
|
|
10504
|
+
const settings = config.settings;
|
|
10505
|
+
const paused = checkPause();
|
|
10506
|
+
const daemonUp = isDaemonRunning();
|
|
10507
|
+
const activeShields = readActiveShields();
|
|
10508
|
+
const lines = [];
|
|
10509
|
+
lines.push(`Mode: ${settings.mode}`);
|
|
10510
|
+
lines.push(`Daemon: ${daemonUp ? "running" : "stopped"}`);
|
|
10511
|
+
lines.push(`Undo engine: ${settings.enableUndo ? "enabled" : "disabled"}`);
|
|
10512
|
+
if (paused.paused) {
|
|
10513
|
+
const until = paused.expiresAt ? new Date(paused.expiresAt).toLocaleTimeString() : "indefinitely";
|
|
10514
|
+
lines.push(`PAUSED until ${until} \u2014 all tool calls currently allowed`);
|
|
10515
|
+
} else {
|
|
10516
|
+
lines.push(`Pause state: not paused`);
|
|
10517
|
+
}
|
|
10518
|
+
lines.push(`Active shields: ${activeShields.length > 0 ? activeShields.join(", ") : "none"}`);
|
|
10519
|
+
lines.push(`Smart rules: ${config.policy.smartRules.length} loaded`);
|
|
10520
|
+
lines.push(`DLP: ${config.policy.dlp?.enabled !== false ? "enabled" : "disabled"}`);
|
|
10521
|
+
const projectConfig = import_path27.default.join(process.cwd(), "node9.config.json");
|
|
10522
|
+
const globalConfig = import_path27.default.join(import_os20.default.homedir(), ".node9", "config.json");
|
|
10523
|
+
lines.push(
|
|
10524
|
+
`Project config (node9.config.json): ${import_fs24.default.existsSync(projectConfig) ? "present" : "not found"}`
|
|
10525
|
+
);
|
|
10526
|
+
lines.push(
|
|
10527
|
+
`Global config (~/.node9/config.json): ${import_fs24.default.existsSync(globalConfig) ? "present" : "not found"}`
|
|
10528
|
+
);
|
|
10529
|
+
return lines.join("\n");
|
|
10530
|
+
}
|
|
10531
|
+
function handleConfigGet() {
|
|
10532
|
+
const config = getConfig();
|
|
10533
|
+
const s = config.settings;
|
|
10534
|
+
const lines = [
|
|
10535
|
+
`mode: ${s.mode}`,
|
|
10536
|
+
`enableUndo: ${s.enableUndo}`,
|
|
10537
|
+
`flightRecorder: ${s.flightRecorder}`,
|
|
10538
|
+
`approvalTimeoutMs: ${s.approvalTimeoutMs}`,
|
|
10539
|
+
`approvers:`,
|
|
10540
|
+
` native: ${s.approvers.native}`,
|
|
10541
|
+
` browser: ${s.approvers.browser}`,
|
|
10542
|
+
` cloud: ${s.approvers.cloud}`,
|
|
10543
|
+
` terminal: ${s.approvers.terminal}`,
|
|
10544
|
+
`dlp.enabled: ${config.policy.dlp?.enabled !== false}`,
|
|
10545
|
+
`dlp.scanIgnoredTools: ${config.policy.dlp?.scanIgnoredTools !== false}`,
|
|
10546
|
+
`smartRules: ${config.policy.smartRules.length} active`,
|
|
10547
|
+
`sandboxPaths: ${config.policy.sandboxPaths.length > 0 ? config.policy.sandboxPaths.join(", ") : "none"}`
|
|
10548
|
+
];
|
|
10549
|
+
return lines.join("\n");
|
|
10550
|
+
}
|
|
10551
|
+
function handleShieldList() {
|
|
10552
|
+
const all = listShields();
|
|
10553
|
+
const active = new Set(readActiveShields());
|
|
10554
|
+
if (all.length === 0) return "No shields available.";
|
|
10555
|
+
const lines = all.map((shield) => {
|
|
10556
|
+
const on = active.has(shield.name);
|
|
10557
|
+
const ruleCount = shield.smartRules.length;
|
|
10558
|
+
return `${on ? "[active]" : "[off] "} ${shield.name.padEnd(12)} \u2014 ${shield.description ?? ""} (${ruleCount} rule${ruleCount === 1 ? "" : "s"})`;
|
|
10559
|
+
});
|
|
10560
|
+
lines.unshift(`${active.size} of ${all.length} shields active:
|
|
10561
|
+
`);
|
|
10562
|
+
return lines.join("\n");
|
|
10563
|
+
}
|
|
10564
|
+
function handleShieldEnable(args) {
|
|
10565
|
+
const service = args.service;
|
|
10566
|
+
if (typeof service !== "string" || !service) {
|
|
10567
|
+
throw new Error("service is required");
|
|
10568
|
+
}
|
|
10569
|
+
const name = resolveShieldName(service);
|
|
10570
|
+
if (!name) {
|
|
10571
|
+
throw new Error(
|
|
10572
|
+
`Unknown shield: "${service}". Run node9_shield_list to see available shields.`
|
|
10573
|
+
);
|
|
10574
|
+
}
|
|
10575
|
+
const active = readActiveShields();
|
|
10576
|
+
if (active.includes(name)) {
|
|
10577
|
+
return `Shield "${name}" is already active.`;
|
|
10578
|
+
}
|
|
10579
|
+
writeActiveShields([...active, name]);
|
|
10580
|
+
const shield = getShield(name);
|
|
10581
|
+
return `Shield "${name}" enabled \u2014 ${shield.smartRules.length} smart rule${shield.smartRules.length === 1 ? "" : "s"} now active.`;
|
|
10582
|
+
}
|
|
10583
|
+
function handleUndoList() {
|
|
10584
|
+
const history = getSnapshotHistory();
|
|
10585
|
+
if (history.length === 0) {
|
|
10586
|
+
return "No snapshots found. Node9 captures snapshots automatically before file edits.";
|
|
10587
|
+
}
|
|
10588
|
+
const lines = history.slice().reverse().map((entry, i) => {
|
|
10589
|
+
const date = new Date(entry.timestamp).toLocaleString();
|
|
10590
|
+
const files = entry.files?.length ? `${entry.files.length} file(s)` : "unknown files";
|
|
10591
|
+
const summary = entry.argsSummary ? ` \u2014 ${entry.argsSummary}` : "";
|
|
10592
|
+
return `[${i + 1}] ${entry.hash.slice(0, 7)} ${date} ${entry.tool}${summary} (${files}) cwd: ${entry.cwd}
|
|
10593
|
+
full hash: ${entry.hash}`;
|
|
10594
|
+
});
|
|
10595
|
+
return lines.join("\n\n");
|
|
10596
|
+
}
|
|
10597
|
+
function handleUndoRevert(args) {
|
|
10598
|
+
const hash = args.hash;
|
|
10599
|
+
if (typeof hash !== "string" || !hash) {
|
|
10600
|
+
throw new Error("hash is required and must be a non-empty string");
|
|
10601
|
+
}
|
|
10602
|
+
if (!/^[0-9a-f]{7,40}$/i.test(hash)) {
|
|
10603
|
+
throw new Error(`Invalid hash format: ${hash}`);
|
|
10604
|
+
}
|
|
10605
|
+
const cwd = typeof args.cwd === "string" && args.cwd ? args.cwd : process.cwd();
|
|
10606
|
+
const success = applyUndo(hash, cwd);
|
|
10607
|
+
if (!success) {
|
|
10608
|
+
throw new Error(
|
|
10609
|
+
`Revert failed for hash ${hash}. The snapshot may not exist for this directory, or git encountered an error.`
|
|
10610
|
+
);
|
|
10611
|
+
}
|
|
10612
|
+
return `Successfully reverted to snapshot ${hash.slice(0, 7)} in ${cwd}.`;
|
|
10613
|
+
}
|
|
10614
|
+
function runMcpServer() {
|
|
10615
|
+
const rl = import_readline4.default.createInterface({ input: process.stdin, terminal: false });
|
|
10616
|
+
rl.on("line", (line) => {
|
|
10617
|
+
let msg;
|
|
10618
|
+
try {
|
|
10619
|
+
msg = JSON.parse(line);
|
|
10620
|
+
} catch {
|
|
10621
|
+
process.stdout.write(err(null, -32700, "Parse error") + "\n");
|
|
10622
|
+
return;
|
|
10623
|
+
}
|
|
10624
|
+
const { method, id, params } = msg;
|
|
10625
|
+
if (method === "initialize") {
|
|
10626
|
+
process.stdout.write(
|
|
10627
|
+
ok(id, {
|
|
10628
|
+
protocolVersion: "2024-11-05",
|
|
10629
|
+
serverInfo: { name: "node9", version: "1.0.0" },
|
|
10630
|
+
capabilities: { tools: {} }
|
|
10631
|
+
}) + "\n"
|
|
10632
|
+
);
|
|
10633
|
+
return;
|
|
10634
|
+
}
|
|
10635
|
+
if (id === void 0 || id === null) {
|
|
10636
|
+
return;
|
|
10637
|
+
}
|
|
10638
|
+
if (method === "tools/list") {
|
|
10639
|
+
process.stdout.write(ok(id, { tools: TOOLS }) + "\n");
|
|
10640
|
+
return;
|
|
10641
|
+
}
|
|
10642
|
+
if (method === "tools/call") {
|
|
10643
|
+
const p = params ?? {};
|
|
10644
|
+
const toolName = p.name;
|
|
10645
|
+
const toolArgs = p.arguments ?? {};
|
|
10646
|
+
try {
|
|
10647
|
+
let text;
|
|
10648
|
+
if (toolName === "node9_status") {
|
|
10649
|
+
text = handleStatus();
|
|
10650
|
+
} else if (toolName === "node9_config_get") {
|
|
10651
|
+
text = handleConfigGet();
|
|
10652
|
+
} else if (toolName === "node9_shield_list") {
|
|
10653
|
+
text = handleShieldList();
|
|
10654
|
+
} else if (toolName === "node9_shield_enable") {
|
|
10655
|
+
text = handleShieldEnable(toolArgs);
|
|
10656
|
+
} else if (toolName === "node9_undo_list") {
|
|
10657
|
+
text = handleUndoList();
|
|
10658
|
+
} else if (toolName === "node9_undo_revert") {
|
|
10659
|
+
text = handleUndoRevert(toolArgs);
|
|
10660
|
+
} else {
|
|
10661
|
+
process.stdout.write(err(id, -32601, `Unknown tool: ${toolName}`) + "\n");
|
|
10662
|
+
return;
|
|
10663
|
+
}
|
|
10664
|
+
process.stdout.write(ok(id, { content: [{ type: "text", text }] }) + "\n");
|
|
10665
|
+
} catch (e) {
|
|
10666
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
10667
|
+
process.stdout.write(
|
|
10668
|
+
ok(id, {
|
|
10669
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
10670
|
+
isError: true
|
|
10671
|
+
}) + "\n"
|
|
10672
|
+
);
|
|
10673
|
+
}
|
|
10674
|
+
return;
|
|
10675
|
+
}
|
|
10676
|
+
process.stdout.write(err(id, -32601, `Method not found: ${method}`) + "\n");
|
|
10677
|
+
});
|
|
10678
|
+
rl.on("close", () => {
|
|
10679
|
+
process.exit(0);
|
|
10680
|
+
});
|
|
10681
|
+
}
|
|
10682
|
+
|
|
10683
|
+
// src/cli/commands/mcp-server.ts
|
|
10684
|
+
function registerMcpServerCommand(program2) {
|
|
10685
|
+
program2.command("mcp-server").description(
|
|
10686
|
+
"Run the Node9 MCP server \u2014 exposes node9 tools (undo, rules, \u2026) to Claude, Cursor, and Gemini"
|
|
10687
|
+
).action(() => {
|
|
10688
|
+
runMcpServer();
|
|
10689
|
+
});
|
|
10690
|
+
}
|
|
10691
|
+
|
|
10340
10692
|
// src/cli/commands/trust.ts
|
|
10341
10693
|
var import_chalk16 = __toESM(require("chalk"));
|
|
10342
10694
|
init_trusted_hosts();
|
|
@@ -10394,20 +10746,20 @@ function registerTrustCommand(program2) {
|
|
|
10394
10746
|
|
|
10395
10747
|
// src/cli.ts
|
|
10396
10748
|
var { version } = JSON.parse(
|
|
10397
|
-
|
|
10749
|
+
import_fs27.default.readFileSync(import_path30.default.join(__dirname, "../package.json"), "utf-8")
|
|
10398
10750
|
);
|
|
10399
10751
|
var program = new import_commander.Command();
|
|
10400
10752
|
program.name("node9").description("The Sudo Command for AI Agents").version(version);
|
|
10401
10753
|
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) => {
|
|
10402
10754
|
const DEFAULT_API_URL = "https://api.node9.ai/api/v1/intercept";
|
|
10403
|
-
const credPath =
|
|
10404
|
-
if (!
|
|
10405
|
-
|
|
10755
|
+
const credPath = import_path30.default.join(import_os23.default.homedir(), ".node9", "credentials.json");
|
|
10756
|
+
if (!import_fs27.default.existsSync(import_path30.default.dirname(credPath)))
|
|
10757
|
+
import_fs27.default.mkdirSync(import_path30.default.dirname(credPath), { recursive: true });
|
|
10406
10758
|
const profileName = options.profile || "default";
|
|
10407
10759
|
let existingCreds = {};
|
|
10408
10760
|
try {
|
|
10409
|
-
if (
|
|
10410
|
-
const raw = JSON.parse(
|
|
10761
|
+
if (import_fs27.default.existsSync(credPath)) {
|
|
10762
|
+
const raw = JSON.parse(import_fs27.default.readFileSync(credPath, "utf-8"));
|
|
10411
10763
|
if (raw.apiKey) {
|
|
10412
10764
|
existingCreds = {
|
|
10413
10765
|
default: { apiKey: raw.apiKey, apiUrl: raw.apiUrl || DEFAULT_API_URL }
|
|
@@ -10419,13 +10771,13 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
|
|
|
10419
10771
|
} catch {
|
|
10420
10772
|
}
|
|
10421
10773
|
existingCreds[profileName] = { apiKey, apiUrl: DEFAULT_API_URL };
|
|
10422
|
-
|
|
10774
|
+
import_fs27.default.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), { mode: 384 });
|
|
10423
10775
|
if (profileName === "default") {
|
|
10424
|
-
const configPath =
|
|
10776
|
+
const configPath = import_path30.default.join(import_os23.default.homedir(), ".node9", "config.json");
|
|
10425
10777
|
let config = {};
|
|
10426
10778
|
try {
|
|
10427
|
-
if (
|
|
10428
|
-
config = JSON.parse(
|
|
10779
|
+
if (import_fs27.default.existsSync(configPath))
|
|
10780
|
+
config = JSON.parse(import_fs27.default.readFileSync(configPath, "utf-8"));
|
|
10429
10781
|
} catch {
|
|
10430
10782
|
}
|
|
10431
10783
|
if (!config.settings || typeof config.settings !== "object") config.settings = {};
|
|
@@ -10440,9 +10792,9 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
|
|
|
10440
10792
|
approvers.cloud = false;
|
|
10441
10793
|
}
|
|
10442
10794
|
s.approvers = approvers;
|
|
10443
|
-
if (!
|
|
10444
|
-
|
|
10445
|
-
|
|
10795
|
+
if (!import_fs27.default.existsSync(import_path30.default.dirname(configPath)))
|
|
10796
|
+
import_fs27.default.mkdirSync(import_path30.default.dirname(configPath), { recursive: true });
|
|
10797
|
+
import_fs27.default.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
|
|
10446
10798
|
}
|
|
10447
10799
|
if (options.profile && profileName !== "default") {
|
|
10448
10800
|
console.log(import_chalk18.default.green(`\u2705 Profile "${profileName}" saved`));
|
|
@@ -10502,8 +10854,8 @@ program.command("removefrom").description("Remove Node9 hooks from an AI agent c
|
|
|
10502
10854
|
`));
|
|
10503
10855
|
try {
|
|
10504
10856
|
fn();
|
|
10505
|
-
} catch (
|
|
10506
|
-
console.error(import_chalk18.default.red(` \u26A0\uFE0F Failed: ${
|
|
10857
|
+
} catch (err2) {
|
|
10858
|
+
console.error(import_chalk18.default.red(` \u26A0\uFE0F Failed: ${err2 instanceof Error ? err2.message : String(err2)}`));
|
|
10507
10859
|
process.exit(1);
|
|
10508
10860
|
}
|
|
10509
10861
|
console.log(import_chalk18.default.gray("\n Restart the agent for changes to take effect."));
|
|
@@ -10526,25 +10878,25 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
|
|
|
10526
10878
|
]) {
|
|
10527
10879
|
try {
|
|
10528
10880
|
fn();
|
|
10529
|
-
} catch (
|
|
10881
|
+
} catch (err2) {
|
|
10530
10882
|
teardownFailed = true;
|
|
10531
10883
|
console.error(
|
|
10532
10884
|
import_chalk18.default.red(
|
|
10533
|
-
` \u26A0\uFE0F Failed to remove ${label} hooks: ${
|
|
10885
|
+
` \u26A0\uFE0F Failed to remove ${label} hooks: ${err2 instanceof Error ? err2.message : String(err2)}`
|
|
10534
10886
|
)
|
|
10535
10887
|
);
|
|
10536
10888
|
}
|
|
10537
10889
|
}
|
|
10538
10890
|
if (options.purge) {
|
|
10539
|
-
const node9Dir =
|
|
10540
|
-
if (
|
|
10891
|
+
const node9Dir = import_path30.default.join(import_os23.default.homedir(), ".node9");
|
|
10892
|
+
if (import_fs27.default.existsSync(node9Dir)) {
|
|
10541
10893
|
const confirmed = await (0, import_prompts2.confirm)({
|
|
10542
10894
|
message: `Permanently delete ${node9Dir} (config, audit log, credentials)?`,
|
|
10543
10895
|
default: false
|
|
10544
10896
|
});
|
|
10545
10897
|
if (confirmed) {
|
|
10546
|
-
|
|
10547
|
-
if (
|
|
10898
|
+
import_fs27.default.rmSync(node9Dir, { recursive: true });
|
|
10899
|
+
if (import_fs27.default.existsSync(node9Dir)) {
|
|
10548
10900
|
console.error(
|
|
10549
10901
|
import_chalk18.default.red("\n \u26A0\uFE0F ~/.node9/ could not be fully deleted \u2014 remove it manually.")
|
|
10550
10902
|
);
|
|
@@ -10651,13 +11003,14 @@ program.command("tail").description("Stream live agent activity to the terminal"
|
|
|
10651
11003
|
const { startTail: startTail2 } = await Promise.resolve().then(() => (init_tail(), tail_exports));
|
|
10652
11004
|
try {
|
|
10653
11005
|
await startTail2(options);
|
|
10654
|
-
} catch (
|
|
10655
|
-
console.error(import_chalk18.default.red(`\u274C ${
|
|
11006
|
+
} catch (err2) {
|
|
11007
|
+
console.error(import_chalk18.default.red(`\u274C ${err2 instanceof Error ? err2.message : String(err2)}`));
|
|
10656
11008
|
process.exit(1);
|
|
10657
11009
|
}
|
|
10658
11010
|
});
|
|
10659
11011
|
registerWatchCommand(program);
|
|
10660
11012
|
registerMcpGatewayCommand(program);
|
|
11013
|
+
registerMcpServerCommand(program);
|
|
10661
11014
|
registerCheckCommand(program);
|
|
10662
11015
|
registerLogCommand(program);
|
|
10663
11016
|
program.command("hud").description("Render node9 security statusline (spawned by Claude Code statusLine)").action(async () => {
|
|
@@ -10760,9 +11113,9 @@ if (process.argv[2] !== "daemon") {
|
|
|
10760
11113
|
const isCheckHook = process.argv[2] === "check";
|
|
10761
11114
|
if (isCheckHook) {
|
|
10762
11115
|
if (process.env.NODE9_DEBUG === "1" || getConfig().settings.enableHookLogDebug) {
|
|
10763
|
-
const logPath =
|
|
11116
|
+
const logPath = import_path30.default.join(import_os23.default.homedir(), ".node9", "hook-debug.log");
|
|
10764
11117
|
const msg = reason instanceof Error ? reason.message : String(reason);
|
|
10765
|
-
|
|
11118
|
+
import_fs27.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
|
|
10766
11119
|
`);
|
|
10767
11120
|
}
|
|
10768
11121
|
process.exit(0);
|