@node9/proxy 1.5.5 → 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.mjs
CHANGED
|
@@ -139,8 +139,8 @@ function sanitizeConfig(raw) {
|
|
|
139
139
|
}
|
|
140
140
|
}
|
|
141
141
|
const lines = result.error.issues.map((issue) => {
|
|
142
|
-
const
|
|
143
|
-
return ` \u2022 ${
|
|
142
|
+
const path31 = issue.path.length > 0 ? issue.path.join(".") : "root";
|
|
143
|
+
return ` \u2022 ${path31}: ${issue.message}`;
|
|
144
144
|
});
|
|
145
145
|
return {
|
|
146
146
|
sanitized,
|
|
@@ -297,9 +297,9 @@ function readShieldsFile() {
|
|
|
297
297
|
(e) => typeof e === "string" && e.length > 0 && e in SHIELDS
|
|
298
298
|
) : [];
|
|
299
299
|
return { active, overrides: validateOverrides(parsed.overrides) };
|
|
300
|
-
} catch (
|
|
301
|
-
if (
|
|
302
|
-
process.stderr.write(`[node9] Warning: could not read shields state: ${String(
|
|
300
|
+
} catch (err2) {
|
|
301
|
+
if (err2.code !== "ENOENT") {
|
|
302
|
+
process.stderr.write(`[node9] Warning: could not read shields state: ${String(err2)}
|
|
303
303
|
`);
|
|
304
304
|
}
|
|
305
305
|
return { active: [] };
|
|
@@ -714,8 +714,8 @@ function tryLoadConfig(filePath) {
|
|
|
714
714
|
let raw;
|
|
715
715
|
try {
|
|
716
716
|
raw = JSON.parse(fs3.readFileSync(filePath, "utf-8"));
|
|
717
|
-
} catch (
|
|
718
|
-
const msg =
|
|
717
|
+
} catch (err2) {
|
|
718
|
+
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
719
719
|
process.stderr.write(
|
|
720
720
|
`
|
|
721
721
|
\u26A0\uFE0F Node9: Failed to parse ${filePath}
|
|
@@ -1043,10 +1043,10 @@ function getCompiledRegex(pattern, flags = "") {
|
|
|
1043
1043
|
regexCache.set(key, cached);
|
|
1044
1044
|
return cached;
|
|
1045
1045
|
}
|
|
1046
|
-
const
|
|
1047
|
-
if (
|
|
1046
|
+
const err2 = validateRegex(pattern);
|
|
1047
|
+
if (err2) {
|
|
1048
1048
|
if (process.env.NODE9_DEBUG === "1")
|
|
1049
|
-
console.error(`[Node9] Regex blocked: ${
|
|
1049
|
+
console.error(`[Node9] Regex blocked: ${err2} \u2014 pattern: "${pattern}"`);
|
|
1050
1050
|
return null;
|
|
1051
1051
|
}
|
|
1052
1052
|
try {
|
|
@@ -1081,8 +1081,8 @@ function scanFilePath(filePath, cwd = process.cwd()) {
|
|
|
1081
1081
|
try {
|
|
1082
1082
|
const absolute = path4.resolve(cwd, filePath);
|
|
1083
1083
|
resolved = fs4.realpathSync.native(absolute);
|
|
1084
|
-
} catch (
|
|
1085
|
-
const code =
|
|
1084
|
+
} catch (err2) {
|
|
1085
|
+
const code = err2.code;
|
|
1086
1086
|
if (code === "ENOENT" || code === "ENOTDIR") {
|
|
1087
1087
|
resolved = path4.resolve(cwd, filePath);
|
|
1088
1088
|
} else {
|
|
@@ -1757,9 +1757,9 @@ function matchesPattern(text, patterns) {
|
|
|
1757
1757
|
const withoutDotSlash = text.replace(/^\.\//, "");
|
|
1758
1758
|
return isMatch(withoutDotSlash) || isMatch(`./${withoutDotSlash}`);
|
|
1759
1759
|
}
|
|
1760
|
-
function getNestedValue(obj,
|
|
1760
|
+
function getNestedValue(obj, path31) {
|
|
1761
1761
|
if (!obj || typeof obj !== "object") return null;
|
|
1762
|
-
return
|
|
1762
|
+
return path31.split(".").reduce((prev, curr) => prev?.[curr], obj);
|
|
1763
1763
|
}
|
|
1764
1764
|
function shouldSnapshot(toolName, args, config) {
|
|
1765
1765
|
if (!config.settings.enableUndo) return false;
|
|
@@ -2409,9 +2409,9 @@ function writeTrustSession(toolName, durationMs) {
|
|
|
2409
2409
|
trust.entries = trust.entries.filter((e) => e.tool !== toolName && e.expiry > now);
|
|
2410
2410
|
trust.entries.push({ tool: toolName, expiry: now + durationMs });
|
|
2411
2411
|
atomicWriteSync(TRUST_FILE, JSON.stringify(trust, null, 2));
|
|
2412
|
-
} catch (
|
|
2412
|
+
} catch (err2) {
|
|
2413
2413
|
if (process.env.NODE9_DEBUG === "1") {
|
|
2414
|
-
console.error("[Node9 Trust Error]:",
|
|
2414
|
+
console.error("[Node9 Trust Error]:", err2);
|
|
2415
2415
|
}
|
|
2416
2416
|
}
|
|
2417
2417
|
}
|
|
@@ -2610,13 +2610,13 @@ async function checkTaint(paths) {
|
|
|
2610
2610
|
signal: AbortSignal.timeout(2e3)
|
|
2611
2611
|
});
|
|
2612
2612
|
return await res.json();
|
|
2613
|
-
} catch (
|
|
2613
|
+
} catch (err2) {
|
|
2614
2614
|
try {
|
|
2615
2615
|
const { appendToLog: appendToLog2, HOOK_DEBUG_LOG: HOOK_DEBUG_LOG2 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
|
|
2616
2616
|
appendToLog2(HOOK_DEBUG_LOG2, {
|
|
2617
2617
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2618
2618
|
event: "checkTaint-error",
|
|
2619
|
-
error: String(
|
|
2619
|
+
error: String(err2),
|
|
2620
2620
|
paths
|
|
2621
2621
|
});
|
|
2622
2622
|
} catch {
|
|
@@ -3109,10 +3109,10 @@ async function resolveNode9SaaS(requestId, creds, approved, decidedBy) {
|
|
|
3109
3109
|
`
|
|
3110
3110
|
);
|
|
3111
3111
|
}
|
|
3112
|
-
} catch (
|
|
3112
|
+
} catch (err2) {
|
|
3113
3113
|
fs10.appendFileSync(
|
|
3114
3114
|
HOOK_DEBUG_LOG,
|
|
3115
|
-
`[resolve-cloud] PATCH failed for ${requestId}: ${
|
|
3115
|
+
`[resolve-cloud] PATCH failed for ${requestId}: ${err2.message}
|
|
3116
3116
|
`
|
|
3117
3117
|
);
|
|
3118
3118
|
}
|
|
@@ -3479,10 +3479,10 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
3479
3479
|
blockedBy: cloudResult.approved ? void 0 : "team-policy",
|
|
3480
3480
|
blockedByLabel: "Organization Policy (SaaS)"
|
|
3481
3481
|
};
|
|
3482
|
-
} catch (
|
|
3483
|
-
const error =
|
|
3484
|
-
if (error.name === "AbortError" || error.message?.includes("Aborted")) throw
|
|
3485
|
-
throw
|
|
3482
|
+
} catch (err2) {
|
|
3483
|
+
const error = err2;
|
|
3484
|
+
if (error.name === "AbortError" || error.message?.includes("Aborted")) throw err2;
|
|
3485
|
+
throw err2;
|
|
3486
3486
|
}
|
|
3487
3487
|
})()
|
|
3488
3488
|
);
|
|
@@ -3575,10 +3575,10 @@ REASON: Action blocked because no approval channels are available. (Native/Brows
|
|
|
3575
3575
|
}
|
|
3576
3576
|
};
|
|
3577
3577
|
for (const p of racePromises) {
|
|
3578
|
-
p.then(finish).catch((
|
|
3579
|
-
if (
|
|
3578
|
+
p.then(finish).catch((err2) => {
|
|
3579
|
+
if (err2.name === "AbortError" || err2.message?.includes("canceled") || err2.message?.includes("Aborted"))
|
|
3580
3580
|
return;
|
|
3581
|
-
if (
|
|
3581
|
+
if (err2.message === "Abandoned") {
|
|
3582
3582
|
finish({
|
|
3583
3583
|
approved: false,
|
|
3584
3584
|
reason: "Browser dashboard closed without making a decision.",
|
|
@@ -5598,21 +5598,21 @@ function atomicWriteSync2(filePath, data, options) {
|
|
|
5598
5598
|
const tmpPath = `${filePath}.${randomUUID3()}.tmp`;
|
|
5599
5599
|
try {
|
|
5600
5600
|
fs13.writeFileSync(tmpPath, data, options);
|
|
5601
|
-
} catch (
|
|
5601
|
+
} catch (err2) {
|
|
5602
5602
|
try {
|
|
5603
5603
|
fs13.unlinkSync(tmpPath);
|
|
5604
5604
|
} catch {
|
|
5605
5605
|
}
|
|
5606
|
-
throw
|
|
5606
|
+
throw err2;
|
|
5607
5607
|
}
|
|
5608
5608
|
try {
|
|
5609
5609
|
fs13.renameSync(tmpPath, filePath);
|
|
5610
|
-
} catch (
|
|
5610
|
+
} catch (err2) {
|
|
5611
5611
|
try {
|
|
5612
5612
|
fs13.unlinkSync(tmpPath);
|
|
5613
5613
|
} catch {
|
|
5614
5614
|
}
|
|
5615
|
-
throw
|
|
5615
|
+
throw err2;
|
|
5616
5616
|
}
|
|
5617
5617
|
}
|
|
5618
5618
|
function redactArgs(value) {
|
|
@@ -5791,6 +5791,16 @@ function startActivitySocket() {
|
|
|
5791
5791
|
sessionHistory.recordTestFail(data.ts);
|
|
5792
5792
|
return;
|
|
5793
5793
|
}
|
|
5794
|
+
if (data.status === "snapshot") {
|
|
5795
|
+
broadcast("snapshot", {
|
|
5796
|
+
hash: data.hash,
|
|
5797
|
+
tool: data.tool,
|
|
5798
|
+
argsSummary: data.argsSummary,
|
|
5799
|
+
fileCount: data.fileCount,
|
|
5800
|
+
ts: data.ts
|
|
5801
|
+
});
|
|
5802
|
+
return;
|
|
5803
|
+
}
|
|
5794
5804
|
if (data.status === "pending") {
|
|
5795
5805
|
broadcast("activity", {
|
|
5796
5806
|
id: data.id,
|
|
@@ -5922,21 +5932,21 @@ function patchConfig(configPath, patch) {
|
|
|
5922
5932
|
const tmp = configPath + ".node9-tmp";
|
|
5923
5933
|
try {
|
|
5924
5934
|
fs14.writeFileSync(tmp, JSON.stringify(config, null, 2), { mode: 384 });
|
|
5925
|
-
} catch (
|
|
5935
|
+
} catch (err2) {
|
|
5926
5936
|
try {
|
|
5927
5937
|
fs14.unlinkSync(tmp);
|
|
5928
5938
|
} catch {
|
|
5929
5939
|
}
|
|
5930
|
-
throw
|
|
5940
|
+
throw err2;
|
|
5931
5941
|
}
|
|
5932
5942
|
try {
|
|
5933
5943
|
fs14.renameSync(tmp, configPath);
|
|
5934
|
-
} catch (
|
|
5944
|
+
} catch (err2) {
|
|
5935
5945
|
try {
|
|
5936
5946
|
fs14.unlinkSync(tmp);
|
|
5937
5947
|
} catch {
|
|
5938
5948
|
}
|
|
5939
|
-
throw
|
|
5949
|
+
throw err2;
|
|
5940
5950
|
}
|
|
5941
5951
|
}
|
|
5942
5952
|
var GLOBAL_CONFIG_PATH;
|
|
@@ -6191,11 +6201,11 @@ data: ${JSON.stringify(item.data)}
|
|
|
6191
6201
|
e.earlyDecision = decision;
|
|
6192
6202
|
e.earlyReason = result.reason;
|
|
6193
6203
|
}
|
|
6194
|
-
}).catch((
|
|
6204
|
+
}).catch((err2) => {
|
|
6195
6205
|
const e = pending.get(id);
|
|
6196
6206
|
if (!e) return;
|
|
6197
6207
|
clearTimeout(e.timer);
|
|
6198
|
-
const reason =
|
|
6208
|
+
const reason = err2?.reason || "No response \u2014 request timed out";
|
|
6199
6209
|
if (e.waiter) e.waiter("deny", reason);
|
|
6200
6210
|
else {
|
|
6201
6211
|
e.earlyDecision = "deny";
|
|
@@ -6322,8 +6332,8 @@ data: ${JSON.stringify(item.data)}
|
|
|
6322
6332
|
const s = getGlobalSettings();
|
|
6323
6333
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
6324
6334
|
return res.end(JSON.stringify({ ...s, autoStarted }));
|
|
6325
|
-
} catch (
|
|
6326
|
-
console.error(chalk2.red("[node9 daemon] GET /settings failed:"),
|
|
6335
|
+
} catch (err2) {
|
|
6336
|
+
console.error(chalk2.red("[node9 daemon] GET /settings failed:"), err2);
|
|
6327
6337
|
res.writeHead(500, { "Content-Type": "application/json" });
|
|
6328
6338
|
return res.end(JSON.stringify({ error: "internal" }));
|
|
6329
6339
|
}
|
|
@@ -6347,8 +6357,8 @@ data: ${JSON.stringify(item.data)}
|
|
|
6347
6357
|
};
|
|
6348
6358
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
6349
6359
|
return res.end(JSON.stringify(status));
|
|
6350
|
-
} catch (
|
|
6351
|
-
console.error(chalk2.red("[node9 daemon] GET /status failed:"),
|
|
6360
|
+
} catch (err2) {
|
|
6361
|
+
console.error(chalk2.red("[node9 daemon] GET /status failed:"), err2);
|
|
6352
6362
|
res.writeHead(500, { "Content-Type": "application/json" });
|
|
6353
6363
|
return res.end(JSON.stringify({ error: "internal" }));
|
|
6354
6364
|
}
|
|
@@ -6388,8 +6398,8 @@ data: ${JSON.stringify(item.data)}
|
|
|
6388
6398
|
const s = getGlobalSettings();
|
|
6389
6399
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
6390
6400
|
return res.end(JSON.stringify({ hasKey: hasStoredSlackKey(), enabled: s.slackEnabled }));
|
|
6391
|
-
} catch (
|
|
6392
|
-
console.error(chalk2.red("[node9 daemon] GET /slack-status failed:"),
|
|
6401
|
+
} catch (err2) {
|
|
6402
|
+
console.error(chalk2.red("[node9 daemon] GET /slack-status failed:"), err2);
|
|
6393
6403
|
res.writeHead(500, { "Content-Type": "application/json" });
|
|
6394
6404
|
return res.end(JSON.stringify({ error: "internal" }));
|
|
6395
6405
|
}
|
|
@@ -6549,10 +6559,10 @@ data: ${JSON.stringify(item.data)}
|
|
|
6549
6559
|
broadcast("suggestion:resolved", { id, status: "applied" });
|
|
6550
6560
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
6551
6561
|
return res.end(JSON.stringify({ ok: true }));
|
|
6552
|
-
} catch (
|
|
6553
|
-
console.error(chalk2.red("[node9 daemon] POST /suggestions/:id/apply failed:"),
|
|
6562
|
+
} catch (err2) {
|
|
6563
|
+
console.error(chalk2.red("[node9 daemon] POST /suggestions/:id/apply failed:"), err2);
|
|
6554
6564
|
res.writeHead(500, { "Content-Type": "application/json" });
|
|
6555
|
-
return res.end(JSON.stringify({ error: String(
|
|
6565
|
+
return res.end(JSON.stringify({ error: String(err2) }));
|
|
6556
6566
|
}
|
|
6557
6567
|
}
|
|
6558
6568
|
if (req.method === "POST" && pathname.startsWith("/suggestions/") && pathname.endsWith("/dismiss")) {
|
|
@@ -6767,10 +6777,10 @@ __export(tail_exports, {
|
|
|
6767
6777
|
});
|
|
6768
6778
|
import http2 from "http";
|
|
6769
6779
|
import chalk17 from "chalk";
|
|
6770
|
-
import
|
|
6771
|
-
import
|
|
6772
|
-
import
|
|
6773
|
-
import
|
|
6780
|
+
import fs25 from "fs";
|
|
6781
|
+
import os21 from "os";
|
|
6782
|
+
import path28 from "path";
|
|
6783
|
+
import readline5 from "readline";
|
|
6774
6784
|
import { spawn as spawn9, execSync as execSync3 } from "child_process";
|
|
6775
6785
|
function getIcon(tool) {
|
|
6776
6786
|
const t = tool.toLowerCase();
|
|
@@ -6798,8 +6808,8 @@ function renderResult(activity, result) {
|
|
|
6798
6808
|
status = chalk17.red("\u2717 BLOCK");
|
|
6799
6809
|
}
|
|
6800
6810
|
if (process.stdout.isTTY) {
|
|
6801
|
-
|
|
6802
|
-
|
|
6811
|
+
readline5.clearLine(process.stdout, 0);
|
|
6812
|
+
readline5.cursorTo(process.stdout, 0);
|
|
6803
6813
|
}
|
|
6804
6814
|
console.log(`${base} ${status}`);
|
|
6805
6815
|
}
|
|
@@ -6809,9 +6819,9 @@ function renderPending(activity) {
|
|
|
6809
6819
|
}
|
|
6810
6820
|
async function ensureDaemon() {
|
|
6811
6821
|
let pidPort = null;
|
|
6812
|
-
if (
|
|
6822
|
+
if (fs25.existsSync(PID_FILE)) {
|
|
6813
6823
|
try {
|
|
6814
|
-
const { port } = JSON.parse(
|
|
6824
|
+
const { port } = JSON.parse(fs25.readFileSync(PID_FILE, "utf-8"));
|
|
6815
6825
|
pidPort = port;
|
|
6816
6826
|
} catch {
|
|
6817
6827
|
console.error(chalk17.dim("\u26A0\uFE0F Could not read PID file; falling back to default port."));
|
|
@@ -6948,7 +6958,7 @@ async function startTail(options = {}) {
|
|
|
6948
6958
|
res.resume();
|
|
6949
6959
|
}
|
|
6950
6960
|
);
|
|
6951
|
-
req2.once("error", (
|
|
6961
|
+
req2.once("error", (err2) => resolve({ ok: false, code: err2.code }));
|
|
6952
6962
|
req2.setTimeout(2e3, () => {
|
|
6953
6963
|
resolve({ ok: false, code: "ETIMEDOUT" });
|
|
6954
6964
|
req2.destroy();
|
|
@@ -6975,10 +6985,10 @@ async function startTail(options = {}) {
|
|
|
6975
6985
|
let cancelActiveCard = null;
|
|
6976
6986
|
const localAllowCounts = /* @__PURE__ */ new Map();
|
|
6977
6987
|
const canApprove = process.stdout.isTTY && process.stdin.isTTY;
|
|
6978
|
-
if (canApprove)
|
|
6988
|
+
if (canApprove) readline5.emitKeypressEvents(process.stdin);
|
|
6979
6989
|
function clearCard() {
|
|
6980
6990
|
if (cardLineCount > 0) {
|
|
6981
|
-
|
|
6991
|
+
readline5.moveCursor(process.stdout, 0, -cardLineCount);
|
|
6982
6992
|
process.stdout.write(ERASE_DOWN);
|
|
6983
6993
|
cardLineCount = 0;
|
|
6984
6994
|
}
|
|
@@ -7054,11 +7064,11 @@ async function startTail(options = {}) {
|
|
|
7054
7064
|
} else {
|
|
7055
7065
|
httpDecision = action;
|
|
7056
7066
|
}
|
|
7057
|
-
postDecisionHttp(req2.id, httpDecision, csrfToken, port, httpOpts).catch((
|
|
7067
|
+
postDecisionHttp(req2.id, httpDecision, csrfToken, port, httpOpts).catch((err2) => {
|
|
7058
7068
|
try {
|
|
7059
|
-
|
|
7060
|
-
|
|
7061
|
-
`[tail] POST /decision failed: ${String(
|
|
7069
|
+
fs25.appendFileSync(
|
|
7070
|
+
path28.join(os21.homedir(), ".node9", "hook-debug.log"),
|
|
7071
|
+
`[tail] POST /decision failed: ${String(err2)}
|
|
7062
7072
|
`
|
|
7063
7073
|
);
|
|
7064
7074
|
} catch {
|
|
@@ -7156,8 +7166,8 @@ async function startTail(options = {}) {
|
|
|
7156
7166
|
clearCard();
|
|
7157
7167
|
process.stdout.write(SHOW_CURSOR);
|
|
7158
7168
|
if (process.stdout.isTTY) {
|
|
7159
|
-
|
|
7160
|
-
|
|
7169
|
+
readline5.clearLine(process.stdout, 0);
|
|
7170
|
+
readline5.cursorTo(process.stdout, 0);
|
|
7161
7171
|
}
|
|
7162
7172
|
console.log(chalk17.dim("\n\u{1F6F0}\uFE0F Disconnected."));
|
|
7163
7173
|
process.exit(0);
|
|
@@ -7172,7 +7182,7 @@ async function startTail(options = {}) {
|
|
|
7172
7182
|
let currentData = "";
|
|
7173
7183
|
res.on("error", () => {
|
|
7174
7184
|
});
|
|
7175
|
-
const rl =
|
|
7185
|
+
const rl = readline5.createInterface({ input: res, crlfDelay: Infinity });
|
|
7176
7186
|
rl.on("error", () => {
|
|
7177
7187
|
});
|
|
7178
7188
|
rl.on("line", (line) => {
|
|
@@ -7192,8 +7202,8 @@ async function startTail(options = {}) {
|
|
|
7192
7202
|
clearCard();
|
|
7193
7203
|
process.stdout.write(SHOW_CURSOR);
|
|
7194
7204
|
if (process.stdout.isTTY) {
|
|
7195
|
-
|
|
7196
|
-
|
|
7205
|
+
readline5.clearLine(process.stdout, 0);
|
|
7206
|
+
readline5.cursorTo(process.stdout, 0);
|
|
7197
7207
|
}
|
|
7198
7208
|
console.log(chalk17.red("\n\u274C Daemon disconnected."));
|
|
7199
7209
|
process.exit(1);
|
|
@@ -7271,6 +7281,18 @@ async function startTail(options = {}) {
|
|
|
7271
7281
|
const slowTool = /bash|shell|query|sql|agent/i.test(data.tool);
|
|
7272
7282
|
if (slowTool) renderPending(data);
|
|
7273
7283
|
}
|
|
7284
|
+
if (event === "snapshot") {
|
|
7285
|
+
const time = new Date(data.ts).toLocaleTimeString([], { hour12: false });
|
|
7286
|
+
const hash = data.hash ?? "";
|
|
7287
|
+
const summary = data.argsSummary ?? data.tool;
|
|
7288
|
+
const fileCount = data.fileCount ?? 0;
|
|
7289
|
+
const files = fileCount > 0 ? chalk17.dim(` \xB7 ${fileCount} file${fileCount === 1 ? "" : "s"}`) : "";
|
|
7290
|
+
process.stdout.write(
|
|
7291
|
+
`${chalk17.dim(time)} ${chalk17.cyan("\u{1F4F8} snapshot")} ${chalk17.dim(hash)} ${summary}${files}
|
|
7292
|
+
`
|
|
7293
|
+
);
|
|
7294
|
+
return;
|
|
7295
|
+
}
|
|
7274
7296
|
if (event === "activity-result") {
|
|
7275
7297
|
const original = activityPending.get(data.id);
|
|
7276
7298
|
if (original) {
|
|
@@ -7279,8 +7301,8 @@ async function startTail(options = {}) {
|
|
|
7279
7301
|
}
|
|
7280
7302
|
}
|
|
7281
7303
|
}
|
|
7282
|
-
req.on("error", (
|
|
7283
|
-
const msg =
|
|
7304
|
+
req.on("error", (err2) => {
|
|
7305
|
+
const msg = err2.code === "ECONNREFUSED" ? "Daemon is not running. Start it with: node9 daemon start" : err2.message;
|
|
7284
7306
|
console.error(chalk17.red(`
|
|
7285
7307
|
\u274C ${msg}`));
|
|
7286
7308
|
process.exit(1);
|
|
@@ -7293,7 +7315,7 @@ var init_tail = __esm({
|
|
|
7293
7315
|
init_daemon2();
|
|
7294
7316
|
init_daemon();
|
|
7295
7317
|
init_core();
|
|
7296
|
-
PID_FILE =
|
|
7318
|
+
PID_FILE = path28.join(os21.homedir(), ".node9", "daemon.pid");
|
|
7297
7319
|
ICONS = {
|
|
7298
7320
|
bash: "\u{1F4BB}",
|
|
7299
7321
|
shell: "\u{1F4BB}",
|
|
@@ -7332,9 +7354,9 @@ __export(hud_exports, {
|
|
|
7332
7354
|
main: () => main,
|
|
7333
7355
|
renderEnvironmentLine: () => renderEnvironmentLine
|
|
7334
7356
|
});
|
|
7335
|
-
import
|
|
7336
|
-
import
|
|
7337
|
-
import
|
|
7357
|
+
import fs26 from "fs";
|
|
7358
|
+
import path29 from "path";
|
|
7359
|
+
import os22 from "os";
|
|
7338
7360
|
import http3 from "http";
|
|
7339
7361
|
async function readStdin() {
|
|
7340
7362
|
const chunks = [];
|
|
@@ -7410,9 +7432,9 @@ function formatTimeLeft(resetsAt) {
|
|
|
7410
7432
|
return ` (${m}m left)`;
|
|
7411
7433
|
}
|
|
7412
7434
|
function safeReadJson(filePath) {
|
|
7413
|
-
if (!
|
|
7435
|
+
if (!fs26.existsSync(filePath)) return null;
|
|
7414
7436
|
try {
|
|
7415
|
-
return JSON.parse(
|
|
7437
|
+
return JSON.parse(fs26.readFileSync(filePath, "utf-8"));
|
|
7416
7438
|
} catch {
|
|
7417
7439
|
return null;
|
|
7418
7440
|
}
|
|
@@ -7433,12 +7455,12 @@ function countHooksInFile(filePath) {
|
|
|
7433
7455
|
return Object.keys(cfg.hooks).length;
|
|
7434
7456
|
}
|
|
7435
7457
|
function countRulesInDir(rulesDir) {
|
|
7436
|
-
if (!
|
|
7458
|
+
if (!fs26.existsSync(rulesDir)) return 0;
|
|
7437
7459
|
let count = 0;
|
|
7438
7460
|
try {
|
|
7439
|
-
for (const entry of
|
|
7461
|
+
for (const entry of fs26.readdirSync(rulesDir, { withFileTypes: true })) {
|
|
7440
7462
|
if (entry.isDirectory()) {
|
|
7441
|
-
count += countRulesInDir(
|
|
7463
|
+
count += countRulesInDir(path29.join(rulesDir, entry.name));
|
|
7442
7464
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
7443
7465
|
count++;
|
|
7444
7466
|
}
|
|
@@ -7449,46 +7471,46 @@ function countRulesInDir(rulesDir) {
|
|
|
7449
7471
|
}
|
|
7450
7472
|
function isSamePath(a, b) {
|
|
7451
7473
|
try {
|
|
7452
|
-
return
|
|
7474
|
+
return path29.resolve(a) === path29.resolve(b);
|
|
7453
7475
|
} catch {
|
|
7454
7476
|
return false;
|
|
7455
7477
|
}
|
|
7456
7478
|
}
|
|
7457
7479
|
function countConfigs(cwd) {
|
|
7458
|
-
const homeDir2 =
|
|
7459
|
-
const claudeDir =
|
|
7480
|
+
const homeDir2 = os22.homedir();
|
|
7481
|
+
const claudeDir = path29.join(homeDir2, ".claude");
|
|
7460
7482
|
let claudeMdCount = 0;
|
|
7461
7483
|
let rulesCount = 0;
|
|
7462
7484
|
let hooksCount = 0;
|
|
7463
7485
|
const userMcpServers = /* @__PURE__ */ new Set();
|
|
7464
7486
|
const projectMcpServers = /* @__PURE__ */ new Set();
|
|
7465
|
-
if (
|
|
7466
|
-
rulesCount += countRulesInDir(
|
|
7467
|
-
const userSettings =
|
|
7487
|
+
if (fs26.existsSync(path29.join(claudeDir, "CLAUDE.md"))) claudeMdCount++;
|
|
7488
|
+
rulesCount += countRulesInDir(path29.join(claudeDir, "rules"));
|
|
7489
|
+
const userSettings = path29.join(claudeDir, "settings.json");
|
|
7468
7490
|
for (const name of getMcpServerNames(userSettings)) userMcpServers.add(name);
|
|
7469
7491
|
hooksCount += countHooksInFile(userSettings);
|
|
7470
|
-
const userClaudeJson =
|
|
7492
|
+
const userClaudeJson = path29.join(homeDir2, ".claude.json");
|
|
7471
7493
|
for (const name of getMcpServerNames(userClaudeJson)) userMcpServers.add(name);
|
|
7472
7494
|
for (const name of getDisabledMcpServers(userClaudeJson, "disabledMcpServers")) {
|
|
7473
7495
|
userMcpServers.delete(name);
|
|
7474
7496
|
}
|
|
7475
7497
|
if (cwd) {
|
|
7476
|
-
if (
|
|
7477
|
-
if (
|
|
7478
|
-
const projectClaudeDir =
|
|
7498
|
+
if (fs26.existsSync(path29.join(cwd, "CLAUDE.md"))) claudeMdCount++;
|
|
7499
|
+
if (fs26.existsSync(path29.join(cwd, "CLAUDE.local.md"))) claudeMdCount++;
|
|
7500
|
+
const projectClaudeDir = path29.join(cwd, ".claude");
|
|
7479
7501
|
const overlapsUserScope = isSamePath(projectClaudeDir, claudeDir);
|
|
7480
7502
|
if (!overlapsUserScope) {
|
|
7481
|
-
if (
|
|
7482
|
-
rulesCount += countRulesInDir(
|
|
7483
|
-
const projSettings =
|
|
7503
|
+
if (fs26.existsSync(path29.join(projectClaudeDir, "CLAUDE.md"))) claudeMdCount++;
|
|
7504
|
+
rulesCount += countRulesInDir(path29.join(projectClaudeDir, "rules"));
|
|
7505
|
+
const projSettings = path29.join(projectClaudeDir, "settings.json");
|
|
7484
7506
|
for (const name of getMcpServerNames(projSettings)) projectMcpServers.add(name);
|
|
7485
7507
|
hooksCount += countHooksInFile(projSettings);
|
|
7486
7508
|
}
|
|
7487
|
-
if (
|
|
7488
|
-
const localSettings =
|
|
7509
|
+
if (fs26.existsSync(path29.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
|
|
7510
|
+
const localSettings = path29.join(projectClaudeDir, "settings.local.json");
|
|
7489
7511
|
for (const name of getMcpServerNames(localSettings)) projectMcpServers.add(name);
|
|
7490
7512
|
hooksCount += countHooksInFile(localSettings);
|
|
7491
|
-
const mcpJsonServers = getMcpServerNames(
|
|
7513
|
+
const mcpJsonServers = getMcpServerNames(path29.join(cwd, ".mcp.json"));
|
|
7492
7514
|
const disabledMcpJson = getDisabledMcpServers(localSettings, "disabledMcpjsonServers");
|
|
7493
7515
|
for (const name of disabledMcpJson) mcpJsonServers.delete(name);
|
|
7494
7516
|
for (const name of mcpJsonServers) projectMcpServers.add(name);
|
|
@@ -7601,11 +7623,11 @@ async function main() {
|
|
|
7601
7623
|
try {
|
|
7602
7624
|
const cwd = stdin.cwd ?? process.cwd();
|
|
7603
7625
|
for (const configPath of [
|
|
7604
|
-
|
|
7605
|
-
|
|
7626
|
+
path29.join(cwd, "node9.config.json"),
|
|
7627
|
+
path29.join(os22.homedir(), ".node9", "config.json")
|
|
7606
7628
|
]) {
|
|
7607
|
-
if (!
|
|
7608
|
-
const cfg = JSON.parse(
|
|
7629
|
+
if (!fs26.existsSync(configPath)) continue;
|
|
7630
|
+
const cfg = JSON.parse(fs26.readFileSync(configPath, "utf-8"));
|
|
7609
7631
|
const hud = cfg.settings?.hud;
|
|
7610
7632
|
if (hud && "showEnvironmentCounts" in hud) return hud.showEnvironmentCounts !== false;
|
|
7611
7633
|
}
|
|
@@ -7654,6 +7676,16 @@ import path14 from "path";
|
|
|
7654
7676
|
import os10 from "os";
|
|
7655
7677
|
import chalk from "chalk";
|
|
7656
7678
|
import { confirm } from "@inquirer/prompts";
|
|
7679
|
+
var NODE9_MCP_SERVER_ENTRY = { command: "node9", args: ["mcp-server"] };
|
|
7680
|
+
function hasNode9McpServer(servers) {
|
|
7681
|
+
const entry = servers["node9"];
|
|
7682
|
+
return !!entry && entry.command === "node9" && Array.isArray(entry.args) && entry.args[0] === "mcp-server";
|
|
7683
|
+
}
|
|
7684
|
+
function removeNode9McpServer(servers) {
|
|
7685
|
+
if (!hasNode9McpServer(servers)) return false;
|
|
7686
|
+
delete servers["node9"];
|
|
7687
|
+
return true;
|
|
7688
|
+
}
|
|
7657
7689
|
function printDaemonTip() {
|
|
7658
7690
|
console.log(
|
|
7659
7691
|
chalk.cyan("\n \u{1F4A1} Node9 will protect you automatically using Native OS popups.") + chalk.white("\n To view your history or manage persistent rules, run:") + chalk.green("\n node9 daemon --openui")
|
|
@@ -7711,6 +7743,10 @@ function teardownClaude() {
|
|
|
7711
7743
|
const claudeConfig = readJson(mcpPath);
|
|
7712
7744
|
if (claudeConfig?.mcpServers) {
|
|
7713
7745
|
let mcpChanged = false;
|
|
7746
|
+
if (removeNode9McpServer(claudeConfig.mcpServers)) {
|
|
7747
|
+
mcpChanged = true;
|
|
7748
|
+
console.log(chalk.green(" \u2705 Removed node9 MCP server entry from ~/.claude.json"));
|
|
7749
|
+
}
|
|
7714
7750
|
for (const [name, server] of Object.entries(claudeConfig.mcpServers)) {
|
|
7715
7751
|
if (server.command === "node9" && Array.isArray(server.args) && server.args.length > 0) {
|
|
7716
7752
|
const [originalCmd, ...originalArgs] = server.args;
|
|
@@ -7754,6 +7790,10 @@ function teardownGemini() {
|
|
|
7754
7790
|
}
|
|
7755
7791
|
}
|
|
7756
7792
|
if (settings.mcpServers) {
|
|
7793
|
+
if (removeNode9McpServer(settings.mcpServers)) {
|
|
7794
|
+
changed = true;
|
|
7795
|
+
console.log(chalk.green(" \u2705 Removed node9 MCP server entry from ~/.gemini/settings.json"));
|
|
7796
|
+
}
|
|
7757
7797
|
for (const [name, server] of Object.entries(settings.mcpServers)) {
|
|
7758
7798
|
if (server.command === "node9" && Array.isArray(server.args) && server.args.length > 0) {
|
|
7759
7799
|
const [originalCmd, ...originalArgs] = server.args;
|
|
@@ -7782,6 +7822,10 @@ function teardownCursor() {
|
|
|
7782
7822
|
return;
|
|
7783
7823
|
}
|
|
7784
7824
|
let changed = false;
|
|
7825
|
+
if (removeNode9McpServer(mcpConfig.mcpServers)) {
|
|
7826
|
+
changed = true;
|
|
7827
|
+
console.log(chalk.green(" \u2705 Removed node9 MCP server entry from ~/.cursor/mcp.json"));
|
|
7828
|
+
}
|
|
7785
7829
|
for (const [name, server] of Object.entries(mcpConfig.mcpServers)) {
|
|
7786
7830
|
if (server.command === "node9" && Array.isArray(server.args) && server.args.length > 0) {
|
|
7787
7831
|
const [originalCmd, ...originalArgs] = server.args;
|
|
@@ -7807,6 +7851,7 @@ async function setupClaude() {
|
|
|
7807
7851
|
const claudeConfig = readJson(mcpPath) ?? {};
|
|
7808
7852
|
const settings = readJson(hooksPath) ?? {};
|
|
7809
7853
|
const servers = claudeConfig.mcpServers ?? {};
|
|
7854
|
+
let hooksChanged = false;
|
|
7810
7855
|
let anythingChanged = false;
|
|
7811
7856
|
if (!settings.hooks) settings.hooks = {};
|
|
7812
7857
|
const hasPreHook = settings.hooks.PreToolUse?.some(
|
|
@@ -7819,6 +7864,7 @@ async function setupClaude() {
|
|
|
7819
7864
|
hooks: [{ type: "command", command: fullPathCommand("check"), timeout: 60 }]
|
|
7820
7865
|
});
|
|
7821
7866
|
console.log(chalk.green(" \u2705 PreToolUse hook added \u2192 node9 check"));
|
|
7867
|
+
hooksChanged = true;
|
|
7822
7868
|
anythingChanged = true;
|
|
7823
7869
|
}
|
|
7824
7870
|
const hasPostHook = settings.hooks.PostToolUse?.some(
|
|
@@ -7831,9 +7877,17 @@ async function setupClaude() {
|
|
|
7831
7877
|
hooks: [{ type: "command", command: fullPathCommand("log"), timeout: 600 }]
|
|
7832
7878
|
});
|
|
7833
7879
|
console.log(chalk.green(" \u2705 PostToolUse hook added \u2192 node9 log"));
|
|
7880
|
+
hooksChanged = true;
|
|
7834
7881
|
anythingChanged = true;
|
|
7835
7882
|
}
|
|
7836
|
-
if (
|
|
7883
|
+
if (!hasNode9McpServer(servers)) {
|
|
7884
|
+
servers["node9"] = NODE9_MCP_SERVER_ENTRY;
|
|
7885
|
+
claudeConfig.mcpServers = servers;
|
|
7886
|
+
writeJson(mcpPath, claudeConfig);
|
|
7887
|
+
console.log(chalk.green(" \u2705 node9 MCP server added \u2192 node9 mcp-server"));
|
|
7888
|
+
anythingChanged = true;
|
|
7889
|
+
}
|
|
7890
|
+
if (hooksChanged) {
|
|
7837
7891
|
writeJson(hooksPath, settings);
|
|
7838
7892
|
console.log("");
|
|
7839
7893
|
}
|
|
@@ -7881,6 +7935,7 @@ async function setupGemini() {
|
|
|
7881
7935
|
const settingsPath = path14.join(homeDir2, ".gemini", "settings.json");
|
|
7882
7936
|
const settings = readJson(settingsPath) ?? {};
|
|
7883
7937
|
const servers = settings.mcpServers ?? {};
|
|
7938
|
+
let hooksChanged = false;
|
|
7884
7939
|
let anythingChanged = false;
|
|
7885
7940
|
if (!settings.hooks) settings.hooks = {};
|
|
7886
7941
|
const hasBeforeHook = Array.isArray(settings.hooks.BeforeTool) && settings.hooks.BeforeTool.some(
|
|
@@ -7901,6 +7956,7 @@ async function setupGemini() {
|
|
|
7901
7956
|
]
|
|
7902
7957
|
});
|
|
7903
7958
|
console.log(chalk.green(" \u2705 BeforeTool hook added \u2192 node9 check"));
|
|
7959
|
+
hooksChanged = true;
|
|
7904
7960
|
anythingChanged = true;
|
|
7905
7961
|
}
|
|
7906
7962
|
const hasAfterHook = Array.isArray(settings.hooks.AfterTool) && settings.hooks.AfterTool.some(
|
|
@@ -7914,9 +7970,17 @@ async function setupGemini() {
|
|
|
7914
7970
|
hooks: [{ name: "node9-log", type: "command", command: fullPathCommand("log") }]
|
|
7915
7971
|
});
|
|
7916
7972
|
console.log(chalk.green(" \u2705 AfterTool hook added \u2192 node9 log"));
|
|
7973
|
+
hooksChanged = true;
|
|
7917
7974
|
anythingChanged = true;
|
|
7918
7975
|
}
|
|
7919
|
-
if (
|
|
7976
|
+
if (!hasNode9McpServer(servers)) {
|
|
7977
|
+
servers["node9"] = NODE9_MCP_SERVER_ENTRY;
|
|
7978
|
+
settings.mcpServers = servers;
|
|
7979
|
+
console.log(chalk.green(" \u2705 node9 MCP server added \u2192 node9 mcp-server"));
|
|
7980
|
+
hooksChanged = true;
|
|
7981
|
+
anythingChanged = true;
|
|
7982
|
+
}
|
|
7983
|
+
if (hooksChanged) {
|
|
7920
7984
|
writeJson(settingsPath, settings);
|
|
7921
7985
|
console.log("");
|
|
7922
7986
|
}
|
|
@@ -7963,10 +8027,10 @@ function detectAgents(homeDir2 = os10.homedir()) {
|
|
|
7963
8027
|
const exists = (p) => {
|
|
7964
8028
|
try {
|
|
7965
8029
|
return fs11.existsSync(p);
|
|
7966
|
-
} catch (
|
|
7967
|
-
const code =
|
|
8030
|
+
} catch (err2) {
|
|
8031
|
+
const code = err2.code;
|
|
7968
8032
|
if (code !== "ENOENT") {
|
|
7969
|
-
process.stderr.write(`[node9] detectAgents: cannot access ${p}: ${code ?? String(
|
|
8033
|
+
process.stderr.write(`[node9] detectAgents: cannot access ${p}: ${code ?? String(err2)}
|
|
7970
8034
|
`);
|
|
7971
8035
|
}
|
|
7972
8036
|
return false;
|
|
@@ -7984,6 +8048,13 @@ async function setupCursor() {
|
|
|
7984
8048
|
const mcpConfig = readJson(mcpPath) ?? {};
|
|
7985
8049
|
const servers = mcpConfig.mcpServers ?? {};
|
|
7986
8050
|
let anythingChanged = false;
|
|
8051
|
+
if (!hasNode9McpServer(servers)) {
|
|
8052
|
+
servers["node9"] = NODE9_MCP_SERVER_ENTRY;
|
|
8053
|
+
mcpConfig.mcpServers = servers;
|
|
8054
|
+
writeJson(mcpPath, mcpConfig);
|
|
8055
|
+
console.log(chalk.green(" \u2705 node9 MCP server added \u2192 node9 mcp-server"));
|
|
8056
|
+
anythingChanged = true;
|
|
8057
|
+
}
|
|
7987
8058
|
const serversToWrap = [];
|
|
7988
8059
|
for (const [name, server] of Object.entries(servers)) {
|
|
7989
8060
|
if (!server.command || server.command === "node9") continue;
|
|
@@ -8083,9 +8154,9 @@ function teardownHud() {
|
|
|
8083
8154
|
// src/cli.ts
|
|
8084
8155
|
init_daemon2();
|
|
8085
8156
|
import chalk18 from "chalk";
|
|
8086
|
-
import
|
|
8087
|
-
import
|
|
8088
|
-
import
|
|
8157
|
+
import fs27 from "fs";
|
|
8158
|
+
import path30 from "path";
|
|
8159
|
+
import os23 from "os";
|
|
8089
8160
|
import { confirm as confirm2 } from "@inquirer/prompts";
|
|
8090
8161
|
|
|
8091
8162
|
// src/utils/duration.ts
|
|
@@ -8320,8 +8391,29 @@ import os14 from "os";
|
|
|
8320
8391
|
import { spawnSync as spawnSync4, spawn as spawn5 } from "child_process";
|
|
8321
8392
|
import crypto2 from "crypto";
|
|
8322
8393
|
import fs17 from "fs";
|
|
8394
|
+
import net3 from "net";
|
|
8323
8395
|
import path19 from "path";
|
|
8324
8396
|
import os13 from "os";
|
|
8397
|
+
var ACTIVITY_SOCKET_PATH3 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : path19.join(os13.tmpdir(), "node9-activity.sock");
|
|
8398
|
+
function notifySnapshotTaken(hash, tool, argsSummary, fileCount) {
|
|
8399
|
+
try {
|
|
8400
|
+
const payload = JSON.stringify({
|
|
8401
|
+
status: "snapshot",
|
|
8402
|
+
hash,
|
|
8403
|
+
tool,
|
|
8404
|
+
argsSummary,
|
|
8405
|
+
fileCount,
|
|
8406
|
+
ts: Date.now()
|
|
8407
|
+
});
|
|
8408
|
+
const sock = net3.createConnection(ACTIVITY_SOCKET_PATH3);
|
|
8409
|
+
sock.on("connect", () => {
|
|
8410
|
+
sock.end(payload);
|
|
8411
|
+
});
|
|
8412
|
+
sock.on("error", () => {
|
|
8413
|
+
});
|
|
8414
|
+
} catch {
|
|
8415
|
+
}
|
|
8416
|
+
}
|
|
8325
8417
|
var SNAPSHOT_STACK_PATH = path19.join(os13.homedir(), ".node9", "snapshots.json");
|
|
8326
8418
|
var UNDO_LATEST_PATH = path19.join(os13.homedir(), ".node9", "undo_latest.txt");
|
|
8327
8419
|
var MAX_SNAPSHOTS = 10;
|
|
@@ -8541,13 +8633,15 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
|
|
|
8541
8633
|
}
|
|
8542
8634
|
if (cwdCount > MAX_SNAPSHOTS) stack.splice(oldestCwdIdx, 1);
|
|
8543
8635
|
writeStack(stack);
|
|
8636
|
+
const entry = stack[stack.length - 1];
|
|
8637
|
+
notifySnapshotTaken(commitHash.slice(0, 7), tool, entry.argsSummary, capturedFiles.length);
|
|
8544
8638
|
fs17.writeFileSync(UNDO_LATEST_PATH, commitHash);
|
|
8545
8639
|
if (shouldGc) {
|
|
8546
8640
|
spawn5("git", ["gc", "--auto"], { env: shadowEnv, detached: true, stdio: "ignore" }).unref();
|
|
8547
8641
|
}
|
|
8548
8642
|
return commitHash;
|
|
8549
|
-
} catch (
|
|
8550
|
-
if (process.env.NODE9_DEBUG === "1") console.error("[Node9 Undo Engine Error]:",
|
|
8643
|
+
} catch (err2) {
|
|
8644
|
+
if (process.env.NODE9_DEBUG === "1") console.error("[Node9 Undo Engine Error]:", err2);
|
|
8551
8645
|
return null;
|
|
8552
8646
|
} finally {
|
|
8553
8647
|
if (indexFile) {
|
|
@@ -8651,11 +8745,11 @@ function registerCheckCommand(program2) {
|
|
|
8651
8745
|
let payload = JSON.parse(raw);
|
|
8652
8746
|
try {
|
|
8653
8747
|
payload = JSON.parse(raw);
|
|
8654
|
-
} catch (
|
|
8748
|
+
} catch (err2) {
|
|
8655
8749
|
const tempConfig = getConfig();
|
|
8656
8750
|
if (process.env.NODE9_DEBUG === "1" || tempConfig.settings.enableHookLogDebug) {
|
|
8657
8751
|
const logPath = path20.join(os14.homedir(), ".node9", "hook-debug.log");
|
|
8658
|
-
const errMsg =
|
|
8752
|
+
const errMsg = err2 instanceof Error ? err2.message : String(err2);
|
|
8659
8753
|
fs18.appendFileSync(
|
|
8660
8754
|
logPath,
|
|
8661
8755
|
`[${(/* @__PURE__ */ new Date()).toISOString()}] JSON_PARSE_ERROR: ${errMsg}
|
|
@@ -8776,10 +8870,10 @@ RAW: ${raw}
|
|
|
8776
8870
|
...result,
|
|
8777
8871
|
blockedByLabel: result.blockedByLabel
|
|
8778
8872
|
});
|
|
8779
|
-
} catch (
|
|
8873
|
+
} catch (err2) {
|
|
8780
8874
|
if (process.env.NODE9_DEBUG === "1") {
|
|
8781
8875
|
const logPath = path20.join(os14.homedir(), ".node9", "hook-debug.log");
|
|
8782
|
-
const errMsg =
|
|
8876
|
+
const errMsg = err2 instanceof Error ? err2.message : String(err2);
|
|
8783
8877
|
fs18.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] ERROR: ${errMsg}
|
|
8784
8878
|
`);
|
|
8785
8879
|
}
|
|
@@ -8925,8 +9019,8 @@ function registerLogCommand(program2) {
|
|
|
8925
9019
|
if (shouldSnapshot(tool, {}, config)) {
|
|
8926
9020
|
await createShadowSnapshot("unknown", {}, config.policy.snapshot.ignorePaths);
|
|
8927
9021
|
}
|
|
8928
|
-
} catch (
|
|
8929
|
-
const msg =
|
|
9022
|
+
} catch (err2) {
|
|
9023
|
+
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
8930
9024
|
process.stderr.write(`[Node9] audit log error: ${msg}
|
|
8931
9025
|
`);
|
|
8932
9026
|
const debugPath = path21.join(os15.homedir(), ".node9", "hook-debug.log");
|
|
@@ -10312,6 +10406,264 @@ function registerMcpGatewayCommand(program2) {
|
|
|
10312
10406
|
});
|
|
10313
10407
|
}
|
|
10314
10408
|
|
|
10409
|
+
// src/mcp-server/index.ts
|
|
10410
|
+
import readline4 from "readline";
|
|
10411
|
+
import fs24 from "fs";
|
|
10412
|
+
import os20 from "os";
|
|
10413
|
+
import path27 from "path";
|
|
10414
|
+
init_core();
|
|
10415
|
+
init_daemon();
|
|
10416
|
+
init_shields();
|
|
10417
|
+
function ok(id, result) {
|
|
10418
|
+
return JSON.stringify({ jsonrpc: "2.0", id: id ?? null, result });
|
|
10419
|
+
}
|
|
10420
|
+
function err(id, code, message) {
|
|
10421
|
+
return JSON.stringify({ jsonrpc: "2.0", id: id ?? null, error: { code, message } });
|
|
10422
|
+
}
|
|
10423
|
+
var TOOLS = [
|
|
10424
|
+
{
|
|
10425
|
+
name: "node9_status",
|
|
10426
|
+
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.",
|
|
10427
|
+
inputSchema: { type: "object", properties: {}, required: [] }
|
|
10428
|
+
},
|
|
10429
|
+
{
|
|
10430
|
+
name: "node9_config_get",
|
|
10431
|
+
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).",
|
|
10432
|
+
inputSchema: { type: "object", properties: {}, required: [] }
|
|
10433
|
+
},
|
|
10434
|
+
{
|
|
10435
|
+
name: "node9_shield_list",
|
|
10436
|
+
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).",
|
|
10437
|
+
inputSchema: { type: "object", properties: {}, required: [] }
|
|
10438
|
+
},
|
|
10439
|
+
{
|
|
10440
|
+
name: "node9_shield_enable",
|
|
10441
|
+
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.",
|
|
10442
|
+
inputSchema: {
|
|
10443
|
+
type: "object",
|
|
10444
|
+
properties: {
|
|
10445
|
+
service: {
|
|
10446
|
+
type: "string",
|
|
10447
|
+
description: 'Shield name to enable (e.g. "postgres", "aws", "github", "filesystem").'
|
|
10448
|
+
}
|
|
10449
|
+
},
|
|
10450
|
+
required: ["service"]
|
|
10451
|
+
}
|
|
10452
|
+
},
|
|
10453
|
+
{
|
|
10454
|
+
name: "node9_undo_list",
|
|
10455
|
+
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.",
|
|
10456
|
+
inputSchema: { type: "object", properties: {}, required: [] }
|
|
10457
|
+
},
|
|
10458
|
+
{
|
|
10459
|
+
name: "node9_undo_revert",
|
|
10460
|
+
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.",
|
|
10461
|
+
inputSchema: {
|
|
10462
|
+
type: "object",
|
|
10463
|
+
properties: {
|
|
10464
|
+
hash: {
|
|
10465
|
+
type: "string",
|
|
10466
|
+
description: "The full git commit hash from node9_undo_list."
|
|
10467
|
+
},
|
|
10468
|
+
cwd: {
|
|
10469
|
+
type: "string",
|
|
10470
|
+
description: "Absolute path to the project directory. Defaults to process.cwd()."
|
|
10471
|
+
}
|
|
10472
|
+
},
|
|
10473
|
+
required: ["hash"]
|
|
10474
|
+
}
|
|
10475
|
+
}
|
|
10476
|
+
];
|
|
10477
|
+
function handleStatus() {
|
|
10478
|
+
const config = getConfig();
|
|
10479
|
+
const settings = config.settings;
|
|
10480
|
+
const paused = checkPause();
|
|
10481
|
+
const daemonUp = isDaemonRunning();
|
|
10482
|
+
const activeShields = readActiveShields();
|
|
10483
|
+
const lines = [];
|
|
10484
|
+
lines.push(`Mode: ${settings.mode}`);
|
|
10485
|
+
lines.push(`Daemon: ${daemonUp ? "running" : "stopped"}`);
|
|
10486
|
+
lines.push(`Undo engine: ${settings.enableUndo ? "enabled" : "disabled"}`);
|
|
10487
|
+
if (paused.paused) {
|
|
10488
|
+
const until = paused.expiresAt ? new Date(paused.expiresAt).toLocaleTimeString() : "indefinitely";
|
|
10489
|
+
lines.push(`PAUSED until ${until} \u2014 all tool calls currently allowed`);
|
|
10490
|
+
} else {
|
|
10491
|
+
lines.push(`Pause state: not paused`);
|
|
10492
|
+
}
|
|
10493
|
+
lines.push(`Active shields: ${activeShields.length > 0 ? activeShields.join(", ") : "none"}`);
|
|
10494
|
+
lines.push(`Smart rules: ${config.policy.smartRules.length} loaded`);
|
|
10495
|
+
lines.push(`DLP: ${config.policy.dlp?.enabled !== false ? "enabled" : "disabled"}`);
|
|
10496
|
+
const projectConfig = path27.join(process.cwd(), "node9.config.json");
|
|
10497
|
+
const globalConfig = path27.join(os20.homedir(), ".node9", "config.json");
|
|
10498
|
+
lines.push(
|
|
10499
|
+
`Project config (node9.config.json): ${fs24.existsSync(projectConfig) ? "present" : "not found"}`
|
|
10500
|
+
);
|
|
10501
|
+
lines.push(
|
|
10502
|
+
`Global config (~/.node9/config.json): ${fs24.existsSync(globalConfig) ? "present" : "not found"}`
|
|
10503
|
+
);
|
|
10504
|
+
return lines.join("\n");
|
|
10505
|
+
}
|
|
10506
|
+
function handleConfigGet() {
|
|
10507
|
+
const config = getConfig();
|
|
10508
|
+
const s = config.settings;
|
|
10509
|
+
const lines = [
|
|
10510
|
+
`mode: ${s.mode}`,
|
|
10511
|
+
`enableUndo: ${s.enableUndo}`,
|
|
10512
|
+
`flightRecorder: ${s.flightRecorder}`,
|
|
10513
|
+
`approvalTimeoutMs: ${s.approvalTimeoutMs}`,
|
|
10514
|
+
`approvers:`,
|
|
10515
|
+
` native: ${s.approvers.native}`,
|
|
10516
|
+
` browser: ${s.approvers.browser}`,
|
|
10517
|
+
` cloud: ${s.approvers.cloud}`,
|
|
10518
|
+
` terminal: ${s.approvers.terminal}`,
|
|
10519
|
+
`dlp.enabled: ${config.policy.dlp?.enabled !== false}`,
|
|
10520
|
+
`dlp.scanIgnoredTools: ${config.policy.dlp?.scanIgnoredTools !== false}`,
|
|
10521
|
+
`smartRules: ${config.policy.smartRules.length} active`,
|
|
10522
|
+
`sandboxPaths: ${config.policy.sandboxPaths.length > 0 ? config.policy.sandboxPaths.join(", ") : "none"}`
|
|
10523
|
+
];
|
|
10524
|
+
return lines.join("\n");
|
|
10525
|
+
}
|
|
10526
|
+
function handleShieldList() {
|
|
10527
|
+
const all = listShields();
|
|
10528
|
+
const active = new Set(readActiveShields());
|
|
10529
|
+
if (all.length === 0) return "No shields available.";
|
|
10530
|
+
const lines = all.map((shield) => {
|
|
10531
|
+
const on = active.has(shield.name);
|
|
10532
|
+
const ruleCount = shield.smartRules.length;
|
|
10533
|
+
return `${on ? "[active]" : "[off] "} ${shield.name.padEnd(12)} \u2014 ${shield.description ?? ""} (${ruleCount} rule${ruleCount === 1 ? "" : "s"})`;
|
|
10534
|
+
});
|
|
10535
|
+
lines.unshift(`${active.size} of ${all.length} shields active:
|
|
10536
|
+
`);
|
|
10537
|
+
return lines.join("\n");
|
|
10538
|
+
}
|
|
10539
|
+
function handleShieldEnable(args) {
|
|
10540
|
+
const service = args.service;
|
|
10541
|
+
if (typeof service !== "string" || !service) {
|
|
10542
|
+
throw new Error("service is required");
|
|
10543
|
+
}
|
|
10544
|
+
const name = resolveShieldName(service);
|
|
10545
|
+
if (!name) {
|
|
10546
|
+
throw new Error(
|
|
10547
|
+
`Unknown shield: "${service}". Run node9_shield_list to see available shields.`
|
|
10548
|
+
);
|
|
10549
|
+
}
|
|
10550
|
+
const active = readActiveShields();
|
|
10551
|
+
if (active.includes(name)) {
|
|
10552
|
+
return `Shield "${name}" is already active.`;
|
|
10553
|
+
}
|
|
10554
|
+
writeActiveShields([...active, name]);
|
|
10555
|
+
const shield = getShield(name);
|
|
10556
|
+
return `Shield "${name}" enabled \u2014 ${shield.smartRules.length} smart rule${shield.smartRules.length === 1 ? "" : "s"} now active.`;
|
|
10557
|
+
}
|
|
10558
|
+
function handleUndoList() {
|
|
10559
|
+
const history = getSnapshotHistory();
|
|
10560
|
+
if (history.length === 0) {
|
|
10561
|
+
return "No snapshots found. Node9 captures snapshots automatically before file edits.";
|
|
10562
|
+
}
|
|
10563
|
+
const lines = history.slice().reverse().map((entry, i) => {
|
|
10564
|
+
const date = new Date(entry.timestamp).toLocaleString();
|
|
10565
|
+
const files = entry.files?.length ? `${entry.files.length} file(s)` : "unknown files";
|
|
10566
|
+
const summary = entry.argsSummary ? ` \u2014 ${entry.argsSummary}` : "";
|
|
10567
|
+
return `[${i + 1}] ${entry.hash.slice(0, 7)} ${date} ${entry.tool}${summary} (${files}) cwd: ${entry.cwd}
|
|
10568
|
+
full hash: ${entry.hash}`;
|
|
10569
|
+
});
|
|
10570
|
+
return lines.join("\n\n");
|
|
10571
|
+
}
|
|
10572
|
+
function handleUndoRevert(args) {
|
|
10573
|
+
const hash = args.hash;
|
|
10574
|
+
if (typeof hash !== "string" || !hash) {
|
|
10575
|
+
throw new Error("hash is required and must be a non-empty string");
|
|
10576
|
+
}
|
|
10577
|
+
if (!/^[0-9a-f]{7,40}$/i.test(hash)) {
|
|
10578
|
+
throw new Error(`Invalid hash format: ${hash}`);
|
|
10579
|
+
}
|
|
10580
|
+
const cwd = typeof args.cwd === "string" && args.cwd ? args.cwd : process.cwd();
|
|
10581
|
+
const success = applyUndo(hash, cwd);
|
|
10582
|
+
if (!success) {
|
|
10583
|
+
throw new Error(
|
|
10584
|
+
`Revert failed for hash ${hash}. The snapshot may not exist for this directory, or git encountered an error.`
|
|
10585
|
+
);
|
|
10586
|
+
}
|
|
10587
|
+
return `Successfully reverted to snapshot ${hash.slice(0, 7)} in ${cwd}.`;
|
|
10588
|
+
}
|
|
10589
|
+
function runMcpServer() {
|
|
10590
|
+
const rl = readline4.createInterface({ input: process.stdin, terminal: false });
|
|
10591
|
+
rl.on("line", (line) => {
|
|
10592
|
+
let msg;
|
|
10593
|
+
try {
|
|
10594
|
+
msg = JSON.parse(line);
|
|
10595
|
+
} catch {
|
|
10596
|
+
process.stdout.write(err(null, -32700, "Parse error") + "\n");
|
|
10597
|
+
return;
|
|
10598
|
+
}
|
|
10599
|
+
const { method, id, params } = msg;
|
|
10600
|
+
if (method === "initialize") {
|
|
10601
|
+
process.stdout.write(
|
|
10602
|
+
ok(id, {
|
|
10603
|
+
protocolVersion: "2024-11-05",
|
|
10604
|
+
serverInfo: { name: "node9", version: "1.0.0" },
|
|
10605
|
+
capabilities: { tools: {} }
|
|
10606
|
+
}) + "\n"
|
|
10607
|
+
);
|
|
10608
|
+
return;
|
|
10609
|
+
}
|
|
10610
|
+
if (id === void 0 || id === null) {
|
|
10611
|
+
return;
|
|
10612
|
+
}
|
|
10613
|
+
if (method === "tools/list") {
|
|
10614
|
+
process.stdout.write(ok(id, { tools: TOOLS }) + "\n");
|
|
10615
|
+
return;
|
|
10616
|
+
}
|
|
10617
|
+
if (method === "tools/call") {
|
|
10618
|
+
const p = params ?? {};
|
|
10619
|
+
const toolName = p.name;
|
|
10620
|
+
const toolArgs = p.arguments ?? {};
|
|
10621
|
+
try {
|
|
10622
|
+
let text;
|
|
10623
|
+
if (toolName === "node9_status") {
|
|
10624
|
+
text = handleStatus();
|
|
10625
|
+
} else if (toolName === "node9_config_get") {
|
|
10626
|
+
text = handleConfigGet();
|
|
10627
|
+
} else if (toolName === "node9_shield_list") {
|
|
10628
|
+
text = handleShieldList();
|
|
10629
|
+
} else if (toolName === "node9_shield_enable") {
|
|
10630
|
+
text = handleShieldEnable(toolArgs);
|
|
10631
|
+
} else if (toolName === "node9_undo_list") {
|
|
10632
|
+
text = handleUndoList();
|
|
10633
|
+
} else if (toolName === "node9_undo_revert") {
|
|
10634
|
+
text = handleUndoRevert(toolArgs);
|
|
10635
|
+
} else {
|
|
10636
|
+
process.stdout.write(err(id, -32601, `Unknown tool: ${toolName}`) + "\n");
|
|
10637
|
+
return;
|
|
10638
|
+
}
|
|
10639
|
+
process.stdout.write(ok(id, { content: [{ type: "text", text }] }) + "\n");
|
|
10640
|
+
} catch (e) {
|
|
10641
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
10642
|
+
process.stdout.write(
|
|
10643
|
+
ok(id, {
|
|
10644
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
10645
|
+
isError: true
|
|
10646
|
+
}) + "\n"
|
|
10647
|
+
);
|
|
10648
|
+
}
|
|
10649
|
+
return;
|
|
10650
|
+
}
|
|
10651
|
+
process.stdout.write(err(id, -32601, `Method not found: ${method}`) + "\n");
|
|
10652
|
+
});
|
|
10653
|
+
rl.on("close", () => {
|
|
10654
|
+
process.exit(0);
|
|
10655
|
+
});
|
|
10656
|
+
}
|
|
10657
|
+
|
|
10658
|
+
// src/cli/commands/mcp-server.ts
|
|
10659
|
+
function registerMcpServerCommand(program2) {
|
|
10660
|
+
program2.command("mcp-server").description(
|
|
10661
|
+
"Run the Node9 MCP server \u2014 exposes node9 tools (undo, rules, \u2026) to Claude, Cursor, and Gemini"
|
|
10662
|
+
).action(() => {
|
|
10663
|
+
runMcpServer();
|
|
10664
|
+
});
|
|
10665
|
+
}
|
|
10666
|
+
|
|
10315
10667
|
// src/cli/commands/trust.ts
|
|
10316
10668
|
init_trusted_hosts();
|
|
10317
10669
|
import chalk16 from "chalk";
|
|
@@ -10369,20 +10721,20 @@ function registerTrustCommand(program2) {
|
|
|
10369
10721
|
|
|
10370
10722
|
// src/cli.ts
|
|
10371
10723
|
var { version } = JSON.parse(
|
|
10372
|
-
|
|
10724
|
+
fs27.readFileSync(path30.join(__dirname, "../package.json"), "utf-8")
|
|
10373
10725
|
);
|
|
10374
10726
|
var program = new Command();
|
|
10375
10727
|
program.name("node9").description("The Sudo Command for AI Agents").version(version);
|
|
10376
10728
|
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) => {
|
|
10377
10729
|
const DEFAULT_API_URL = "https://api.node9.ai/api/v1/intercept";
|
|
10378
|
-
const credPath =
|
|
10379
|
-
if (!
|
|
10380
|
-
|
|
10730
|
+
const credPath = path30.join(os23.homedir(), ".node9", "credentials.json");
|
|
10731
|
+
if (!fs27.existsSync(path30.dirname(credPath)))
|
|
10732
|
+
fs27.mkdirSync(path30.dirname(credPath), { recursive: true });
|
|
10381
10733
|
const profileName = options.profile || "default";
|
|
10382
10734
|
let existingCreds = {};
|
|
10383
10735
|
try {
|
|
10384
|
-
if (
|
|
10385
|
-
const raw = JSON.parse(
|
|
10736
|
+
if (fs27.existsSync(credPath)) {
|
|
10737
|
+
const raw = JSON.parse(fs27.readFileSync(credPath, "utf-8"));
|
|
10386
10738
|
if (raw.apiKey) {
|
|
10387
10739
|
existingCreds = {
|
|
10388
10740
|
default: { apiKey: raw.apiKey, apiUrl: raw.apiUrl || DEFAULT_API_URL }
|
|
@@ -10394,13 +10746,13 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
|
|
|
10394
10746
|
} catch {
|
|
10395
10747
|
}
|
|
10396
10748
|
existingCreds[profileName] = { apiKey, apiUrl: DEFAULT_API_URL };
|
|
10397
|
-
|
|
10749
|
+
fs27.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), { mode: 384 });
|
|
10398
10750
|
if (profileName === "default") {
|
|
10399
|
-
const configPath =
|
|
10751
|
+
const configPath = path30.join(os23.homedir(), ".node9", "config.json");
|
|
10400
10752
|
let config = {};
|
|
10401
10753
|
try {
|
|
10402
|
-
if (
|
|
10403
|
-
config = JSON.parse(
|
|
10754
|
+
if (fs27.existsSync(configPath))
|
|
10755
|
+
config = JSON.parse(fs27.readFileSync(configPath, "utf-8"));
|
|
10404
10756
|
} catch {
|
|
10405
10757
|
}
|
|
10406
10758
|
if (!config.settings || typeof config.settings !== "object") config.settings = {};
|
|
@@ -10415,9 +10767,9 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
|
|
|
10415
10767
|
approvers.cloud = false;
|
|
10416
10768
|
}
|
|
10417
10769
|
s.approvers = approvers;
|
|
10418
|
-
if (!
|
|
10419
|
-
|
|
10420
|
-
|
|
10770
|
+
if (!fs27.existsSync(path30.dirname(configPath)))
|
|
10771
|
+
fs27.mkdirSync(path30.dirname(configPath), { recursive: true });
|
|
10772
|
+
fs27.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
|
|
10421
10773
|
}
|
|
10422
10774
|
if (options.profile && profileName !== "default") {
|
|
10423
10775
|
console.log(chalk18.green(`\u2705 Profile "${profileName}" saved`));
|
|
@@ -10477,8 +10829,8 @@ program.command("removefrom").description("Remove Node9 hooks from an AI agent c
|
|
|
10477
10829
|
`));
|
|
10478
10830
|
try {
|
|
10479
10831
|
fn();
|
|
10480
|
-
} catch (
|
|
10481
|
-
console.error(chalk18.red(` \u26A0\uFE0F Failed: ${
|
|
10832
|
+
} catch (err2) {
|
|
10833
|
+
console.error(chalk18.red(` \u26A0\uFE0F Failed: ${err2 instanceof Error ? err2.message : String(err2)}`));
|
|
10482
10834
|
process.exit(1);
|
|
10483
10835
|
}
|
|
10484
10836
|
console.log(chalk18.gray("\n Restart the agent for changes to take effect."));
|
|
@@ -10501,25 +10853,25 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
|
|
|
10501
10853
|
]) {
|
|
10502
10854
|
try {
|
|
10503
10855
|
fn();
|
|
10504
|
-
} catch (
|
|
10856
|
+
} catch (err2) {
|
|
10505
10857
|
teardownFailed = true;
|
|
10506
10858
|
console.error(
|
|
10507
10859
|
chalk18.red(
|
|
10508
|
-
` \u26A0\uFE0F Failed to remove ${label} hooks: ${
|
|
10860
|
+
` \u26A0\uFE0F Failed to remove ${label} hooks: ${err2 instanceof Error ? err2.message : String(err2)}`
|
|
10509
10861
|
)
|
|
10510
10862
|
);
|
|
10511
10863
|
}
|
|
10512
10864
|
}
|
|
10513
10865
|
if (options.purge) {
|
|
10514
|
-
const node9Dir =
|
|
10515
|
-
if (
|
|
10866
|
+
const node9Dir = path30.join(os23.homedir(), ".node9");
|
|
10867
|
+
if (fs27.existsSync(node9Dir)) {
|
|
10516
10868
|
const confirmed = await confirm2({
|
|
10517
10869
|
message: `Permanently delete ${node9Dir} (config, audit log, credentials)?`,
|
|
10518
10870
|
default: false
|
|
10519
10871
|
});
|
|
10520
10872
|
if (confirmed) {
|
|
10521
|
-
|
|
10522
|
-
if (
|
|
10873
|
+
fs27.rmSync(node9Dir, { recursive: true });
|
|
10874
|
+
if (fs27.existsSync(node9Dir)) {
|
|
10523
10875
|
console.error(
|
|
10524
10876
|
chalk18.red("\n \u26A0\uFE0F ~/.node9/ could not be fully deleted \u2014 remove it manually.")
|
|
10525
10877
|
);
|
|
@@ -10626,13 +10978,14 @@ program.command("tail").description("Stream live agent activity to the terminal"
|
|
|
10626
10978
|
const { startTail: startTail2 } = await Promise.resolve().then(() => (init_tail(), tail_exports));
|
|
10627
10979
|
try {
|
|
10628
10980
|
await startTail2(options);
|
|
10629
|
-
} catch (
|
|
10630
|
-
console.error(chalk18.red(`\u274C ${
|
|
10981
|
+
} catch (err2) {
|
|
10982
|
+
console.error(chalk18.red(`\u274C ${err2 instanceof Error ? err2.message : String(err2)}`));
|
|
10631
10983
|
process.exit(1);
|
|
10632
10984
|
}
|
|
10633
10985
|
});
|
|
10634
10986
|
registerWatchCommand(program);
|
|
10635
10987
|
registerMcpGatewayCommand(program);
|
|
10988
|
+
registerMcpServerCommand(program);
|
|
10636
10989
|
registerCheckCommand(program);
|
|
10637
10990
|
registerLogCommand(program);
|
|
10638
10991
|
program.command("hud").description("Render node9 security statusline (spawned by Claude Code statusLine)").action(async () => {
|
|
@@ -10735,9 +11088,9 @@ if (process.argv[2] !== "daemon") {
|
|
|
10735
11088
|
const isCheckHook = process.argv[2] === "check";
|
|
10736
11089
|
if (isCheckHook) {
|
|
10737
11090
|
if (process.env.NODE9_DEBUG === "1" || getConfig().settings.enableHookLogDebug) {
|
|
10738
|
-
const logPath =
|
|
11091
|
+
const logPath = path30.join(os23.homedir(), ".node9", "hook-debug.log");
|
|
10739
11092
|
const msg = reason instanceof Error ? reason.message : String(reason);
|
|
10740
|
-
|
|
11093
|
+
fs27.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
|
|
10741
11094
|
`);
|
|
10742
11095
|
}
|
|
10743
11096
|
process.exit(0);
|