@node9/proxy 1.3.2 → 1.4.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/dist/cli.js +493 -368
- package/dist/cli.mjs +479 -355
- package/dist/index.js +102 -60
- package/dist/index.mjs +102 -60
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -114,8 +114,8 @@ function sanitizeConfig(raw) {
|
|
|
114
114
|
}
|
|
115
115
|
}
|
|
116
116
|
const lines = result.error.issues.map((issue) => {
|
|
117
|
-
const
|
|
118
|
-
return ` \u2022 ${
|
|
117
|
+
const path25 = issue.path.length > 0 ? issue.path.join(".") : "root";
|
|
118
|
+
return ` \u2022 ${path25}: ${issue.message}`;
|
|
119
119
|
});
|
|
120
120
|
return {
|
|
121
121
|
sanitized,
|
|
@@ -1610,6 +1610,63 @@ var init_ssh_parser = __esm({
|
|
|
1610
1610
|
}
|
|
1611
1611
|
});
|
|
1612
1612
|
|
|
1613
|
+
// src/auth/trusted-hosts.ts
|
|
1614
|
+
function getTrustedHostsPath() {
|
|
1615
|
+
return import_path7.default.join(import_os5.default.homedir(), ".node9", "trusted-hosts.json");
|
|
1616
|
+
}
|
|
1617
|
+
function readTrustedHosts() {
|
|
1618
|
+
try {
|
|
1619
|
+
const raw = import_fs6.default.readFileSync(getTrustedHostsPath(), "utf8");
|
|
1620
|
+
const parsed = JSON.parse(raw);
|
|
1621
|
+
return Array.isArray(parsed.hosts) ? parsed.hosts : [];
|
|
1622
|
+
} catch {
|
|
1623
|
+
return [];
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
function writeTrustedHosts(hosts) {
|
|
1627
|
+
const filePath = getTrustedHostsPath();
|
|
1628
|
+
import_fs6.default.mkdirSync(import_path7.default.dirname(filePath), { recursive: true });
|
|
1629
|
+
const tmp = filePath + ".node9-tmp";
|
|
1630
|
+
import_fs6.default.writeFileSync(tmp, JSON.stringify({ hosts }, null, 2));
|
|
1631
|
+
import_fs6.default.renameSync(tmp, filePath);
|
|
1632
|
+
}
|
|
1633
|
+
function addTrustedHost(host) {
|
|
1634
|
+
const hosts = readTrustedHosts();
|
|
1635
|
+
if (hosts.some((h) => h.host === host)) return;
|
|
1636
|
+
hosts.push({ host, addedAt: Date.now(), addedBy: "user" });
|
|
1637
|
+
writeTrustedHosts(hosts);
|
|
1638
|
+
}
|
|
1639
|
+
function removeTrustedHost(host) {
|
|
1640
|
+
const hosts = readTrustedHosts();
|
|
1641
|
+
const filtered = hosts.filter((h) => h.host !== host);
|
|
1642
|
+
if (filtered.length === hosts.length) return false;
|
|
1643
|
+
writeTrustedHosts(filtered);
|
|
1644
|
+
return true;
|
|
1645
|
+
}
|
|
1646
|
+
function normalizeHost(raw) {
|
|
1647
|
+
return raw.toLowerCase().replace(/^https?:\/\//, "").replace(/\/.*$/, "").replace(/^[^@]+@/, "").replace(/:\d+$/, "");
|
|
1648
|
+
}
|
|
1649
|
+
function isTrustedHost(host) {
|
|
1650
|
+
const normalized = normalizeHost(host);
|
|
1651
|
+
return readTrustedHosts().some((entry) => {
|
|
1652
|
+
const entryHost = entry.host.toLowerCase();
|
|
1653
|
+
if (entryHost.startsWith("*.")) {
|
|
1654
|
+
const domain = entryHost.slice(2);
|
|
1655
|
+
return normalized === domain || normalized.endsWith("." + domain);
|
|
1656
|
+
}
|
|
1657
|
+
return normalized === entryHost;
|
|
1658
|
+
});
|
|
1659
|
+
}
|
|
1660
|
+
var import_fs6, import_path7, import_os5;
|
|
1661
|
+
var init_trusted_hosts = __esm({
|
|
1662
|
+
"src/auth/trusted-hosts.ts"() {
|
|
1663
|
+
"use strict";
|
|
1664
|
+
import_fs6 = __toESM(require("fs"));
|
|
1665
|
+
import_path7 = __toESM(require("path"));
|
|
1666
|
+
import_os5 = __toESM(require("os"));
|
|
1667
|
+
}
|
|
1668
|
+
});
|
|
1669
|
+
|
|
1613
1670
|
// src/policy/index.ts
|
|
1614
1671
|
function tokenize2(toolName) {
|
|
1615
1672
|
return toolName.toLowerCase().split(/[_.\-\s]+/).filter(Boolean);
|
|
@@ -1624,9 +1681,9 @@ function matchesPattern(text, patterns) {
|
|
|
1624
1681
|
const withoutDotSlash = text.replace(/^\.\//, "");
|
|
1625
1682
|
return isMatch(withoutDotSlash) || isMatch(`./${withoutDotSlash}`);
|
|
1626
1683
|
}
|
|
1627
|
-
function getNestedValue(obj,
|
|
1684
|
+
function getNestedValue(obj, path25) {
|
|
1628
1685
|
if (!obj || typeof obj !== "object") return null;
|
|
1629
|
-
return
|
|
1686
|
+
return path25.split(".").reduce((prev, curr) => prev?.[curr], obj);
|
|
1630
1687
|
}
|
|
1631
1688
|
function shouldSnapshot(toolName, args, config) {
|
|
1632
1689
|
if (!config.settings.enableUndo) return false;
|
|
@@ -1792,23 +1849,34 @@ async function evaluatePolicy(toolName, args, agent, cwd) {
|
|
|
1792
1849
|
return { decision: "review", blockedByLabel: "Node9 Standard (Inline Execution)", tier: 3 };
|
|
1793
1850
|
}
|
|
1794
1851
|
const pipeAnalysis = analyzePipeChain(shellCommand);
|
|
1795
|
-
if (pipeAnalysis.isPipeline) {
|
|
1852
|
+
if (pipeAnalysis.isPipeline && (pipeAnalysis.risk === "critical" || pipeAnalysis.risk === "high")) {
|
|
1853
|
+
const sinks = pipeAnalysis.sinkTargets;
|
|
1854
|
+
const allTrusted = sinks.length > 0 && sinks.every(isTrustedHost);
|
|
1796
1855
|
if (pipeAnalysis.risk === "critical") {
|
|
1856
|
+
if (allTrusted) {
|
|
1857
|
+
return {
|
|
1858
|
+
decision: "review",
|
|
1859
|
+
blockedByLabel: "Node9: Pipe-Chain to Trusted Host (obfuscated)",
|
|
1860
|
+
reason: `Obfuscated pipe to trusted host(s): ${sinks.join(", ")} \u2014 requires approval`,
|
|
1861
|
+
tier: 3
|
|
1862
|
+
};
|
|
1863
|
+
}
|
|
1797
1864
|
return {
|
|
1798
1865
|
decision: "block",
|
|
1799
1866
|
blockedByLabel: "Node9: Pipe-Chain Exfiltration (critical)",
|
|
1800
|
-
reason: `Sensitive file piped through obfuscator to network sink: ${pipeAnalysis.sourceFiles.join(", ")} \u2192 ${
|
|
1867
|
+
reason: `Sensitive file piped through obfuscator to network sink: ${pipeAnalysis.sourceFiles.join(", ")} \u2192 ${sinks.join(", ")}`,
|
|
1801
1868
|
tier: 3
|
|
1802
1869
|
};
|
|
1803
1870
|
}
|
|
1804
|
-
if (
|
|
1805
|
-
return {
|
|
1806
|
-
decision: "review",
|
|
1807
|
-
blockedByLabel: "Node9: Pipe-Chain Exfiltration (high)",
|
|
1808
|
-
reason: `Sensitive file piped to network sink: ${pipeAnalysis.sourceFiles.join(", ")} \u2192 ${pipeAnalysis.sinkTargets.join(", ")}`,
|
|
1809
|
-
tier: 3
|
|
1810
|
-
};
|
|
1871
|
+
if (allTrusted) {
|
|
1872
|
+
return { decision: "allow" };
|
|
1811
1873
|
}
|
|
1874
|
+
return {
|
|
1875
|
+
decision: "review",
|
|
1876
|
+
blockedByLabel: "Node9: Pipe-Chain Exfiltration (high)",
|
|
1877
|
+
reason: `Sensitive file piped to network sink: ${pipeAnalysis.sourceFiles.join(", ")} \u2192 ${sinks.join(", ")}`,
|
|
1878
|
+
tier: 3
|
|
1879
|
+
};
|
|
1812
1880
|
}
|
|
1813
1881
|
const firstToken = analyzed.actions[0] ?? "";
|
|
1814
1882
|
if (["ssh", "scp", "rsync"].includes(firstToken)) {
|
|
@@ -1816,7 +1884,7 @@ async function evaluatePolicy(toolName, args, agent, cwd) {
|
|
|
1816
1884
|
const sshHosts = extractAllSshHosts(rawTokens.slice(1));
|
|
1817
1885
|
allTokens.push(...sshHosts);
|
|
1818
1886
|
}
|
|
1819
|
-
if (firstToken &&
|
|
1887
|
+
if (firstToken && import_path8.default.posix.isAbsolute(firstToken)) {
|
|
1820
1888
|
const prov = checkProvenance(firstToken, cwd);
|
|
1821
1889
|
if (prov.trustLevel === "suspect") {
|
|
1822
1890
|
return {
|
|
@@ -1913,9 +1981,9 @@ async function evaluatePolicy(toolName, args, agent, cwd) {
|
|
|
1913
1981
|
}
|
|
1914
1982
|
async function explainPolicy(toolName, args) {
|
|
1915
1983
|
const steps = [];
|
|
1916
|
-
const globalPath =
|
|
1917
|
-
const projectPath =
|
|
1918
|
-
const credsPath =
|
|
1984
|
+
const globalPath = import_path8.default.join(import_os6.default.homedir(), ".node9", "config.json");
|
|
1985
|
+
const projectPath = import_path8.default.join(process.cwd(), "node9.config.json");
|
|
1986
|
+
const credsPath = import_path8.default.join(import_os6.default.homedir(), ".node9", "credentials.json");
|
|
1919
1987
|
const waterfall = [
|
|
1920
1988
|
{
|
|
1921
1989
|
tier: 1,
|
|
@@ -1926,19 +1994,19 @@ async function explainPolicy(toolName, args) {
|
|
|
1926
1994
|
{
|
|
1927
1995
|
tier: 2,
|
|
1928
1996
|
label: "Cloud policy",
|
|
1929
|
-
status:
|
|
1930
|
-
note:
|
|
1997
|
+
status: import_fs7.default.existsSync(credsPath) ? "active" : "missing",
|
|
1998
|
+
note: import_fs7.default.existsSync(credsPath) ? "credentials found (not evaluated in explain mode)" : "not connected \u2014 run: node9 login"
|
|
1931
1999
|
},
|
|
1932
2000
|
{
|
|
1933
2001
|
tier: 3,
|
|
1934
2002
|
label: "Project config",
|
|
1935
|
-
status:
|
|
2003
|
+
status: import_fs7.default.existsSync(projectPath) ? "active" : "missing",
|
|
1936
2004
|
path: projectPath
|
|
1937
2005
|
},
|
|
1938
2006
|
{
|
|
1939
2007
|
tier: 4,
|
|
1940
2008
|
label: "Global config",
|
|
1941
|
-
status:
|
|
2009
|
+
status: import_fs7.default.existsSync(globalPath) ? "active" : "missing",
|
|
1942
2010
|
path: globalPath
|
|
1943
2011
|
},
|
|
1944
2012
|
{
|
|
@@ -2175,13 +2243,13 @@ function isIgnoredTool(toolName) {
|
|
|
2175
2243
|
const config = getConfig();
|
|
2176
2244
|
return matchesPattern(toolName, config.policy.ignoredTools);
|
|
2177
2245
|
}
|
|
2178
|
-
var
|
|
2246
|
+
var import_fs7, import_path8, import_os6, import_picomatch, import_sh_syntax, SQL_DML_KEYWORDS;
|
|
2179
2247
|
var init_policy = __esm({
|
|
2180
2248
|
"src/policy/index.ts"() {
|
|
2181
2249
|
"use strict";
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2250
|
+
import_fs7 = __toESM(require("fs"));
|
|
2251
|
+
import_path8 = __toESM(require("path"));
|
|
2252
|
+
import_os6 = __toESM(require("os"));
|
|
2185
2253
|
import_picomatch = __toESM(require("picomatch"));
|
|
2186
2254
|
import_sh_syntax = require("sh-syntax");
|
|
2187
2255
|
init_dlp();
|
|
@@ -2190,6 +2258,7 @@ var init_policy = __esm({
|
|
|
2190
2258
|
init_provenance();
|
|
2191
2259
|
init_pipe_chain();
|
|
2192
2260
|
init_ssh_parser();
|
|
2261
|
+
init_trusted_hosts();
|
|
2193
2262
|
SQL_DML_KEYWORDS = /* @__PURE__ */ new Set(["select", "insert", "update", "delete", "merge", "upsert"]);
|
|
2194
2263
|
}
|
|
2195
2264
|
});
|
|
@@ -2197,11 +2266,11 @@ var init_policy = __esm({
|
|
|
2197
2266
|
// src/auth/state.ts
|
|
2198
2267
|
function checkPause() {
|
|
2199
2268
|
try {
|
|
2200
|
-
if (!
|
|
2201
|
-
const state = JSON.parse(
|
|
2269
|
+
if (!import_fs8.default.existsSync(PAUSED_FILE)) return { paused: false };
|
|
2270
|
+
const state = JSON.parse(import_fs8.default.readFileSync(PAUSED_FILE, "utf-8"));
|
|
2202
2271
|
if (state.expiry > 0 && Date.now() >= state.expiry) {
|
|
2203
2272
|
try {
|
|
2204
|
-
|
|
2273
|
+
import_fs8.default.unlinkSync(PAUSED_FILE);
|
|
2205
2274
|
} catch {
|
|
2206
2275
|
}
|
|
2207
2276
|
return { paused: false };
|
|
@@ -2212,11 +2281,11 @@ function checkPause() {
|
|
|
2212
2281
|
}
|
|
2213
2282
|
}
|
|
2214
2283
|
function atomicWriteSync(filePath, data, options) {
|
|
2215
|
-
const dir =
|
|
2216
|
-
if (!
|
|
2217
|
-
const tmpPath = `${filePath}.${
|
|
2218
|
-
|
|
2219
|
-
|
|
2284
|
+
const dir = import_path9.default.dirname(filePath);
|
|
2285
|
+
if (!import_fs8.default.existsSync(dir)) import_fs8.default.mkdirSync(dir, { recursive: true });
|
|
2286
|
+
const tmpPath = `${filePath}.${import_os7.default.hostname()}.${process.pid}.tmp`;
|
|
2287
|
+
import_fs8.default.writeFileSync(tmpPath, data, options);
|
|
2288
|
+
import_fs8.default.renameSync(tmpPath, filePath);
|
|
2220
2289
|
}
|
|
2221
2290
|
function pauseNode9(durationMs, durationStr) {
|
|
2222
2291
|
const state = { expiry: Date.now() + durationMs, duration: durationStr };
|
|
@@ -2224,18 +2293,18 @@ function pauseNode9(durationMs, durationStr) {
|
|
|
2224
2293
|
}
|
|
2225
2294
|
function resumeNode9() {
|
|
2226
2295
|
try {
|
|
2227
|
-
if (
|
|
2296
|
+
if (import_fs8.default.existsSync(PAUSED_FILE)) import_fs8.default.unlinkSync(PAUSED_FILE);
|
|
2228
2297
|
} catch {
|
|
2229
2298
|
}
|
|
2230
2299
|
}
|
|
2231
2300
|
function getActiveTrustSession(toolName) {
|
|
2232
2301
|
try {
|
|
2233
|
-
if (!
|
|
2234
|
-
const trust = JSON.parse(
|
|
2302
|
+
if (!import_fs8.default.existsSync(TRUST_FILE)) return false;
|
|
2303
|
+
const trust = JSON.parse(import_fs8.default.readFileSync(TRUST_FILE, "utf-8"));
|
|
2235
2304
|
const now = Date.now();
|
|
2236
2305
|
const active = trust.entries.filter((e) => e.expiry > now);
|
|
2237
2306
|
if (active.length !== trust.entries.length) {
|
|
2238
|
-
|
|
2307
|
+
import_fs8.default.writeFileSync(TRUST_FILE, JSON.stringify({ entries: active }, null, 2));
|
|
2239
2308
|
}
|
|
2240
2309
|
return active.some((e) => e.tool === toolName || matchesPattern(toolName, e.tool));
|
|
2241
2310
|
} catch {
|
|
@@ -2246,8 +2315,8 @@ function writeTrustSession(toolName, durationMs) {
|
|
|
2246
2315
|
try {
|
|
2247
2316
|
let trust = { entries: [] };
|
|
2248
2317
|
try {
|
|
2249
|
-
if (
|
|
2250
|
-
trust = JSON.parse(
|
|
2318
|
+
if (import_fs8.default.existsSync(TRUST_FILE)) {
|
|
2319
|
+
trust = JSON.parse(import_fs8.default.readFileSync(TRUST_FILE, "utf-8"));
|
|
2251
2320
|
}
|
|
2252
2321
|
} catch {
|
|
2253
2322
|
}
|
|
@@ -2263,34 +2332,34 @@ function writeTrustSession(toolName, durationMs) {
|
|
|
2263
2332
|
}
|
|
2264
2333
|
function getPersistentDecision(toolName) {
|
|
2265
2334
|
try {
|
|
2266
|
-
const file =
|
|
2267
|
-
if (!
|
|
2268
|
-
const decisions = JSON.parse(
|
|
2335
|
+
const file = import_path9.default.join(import_os7.default.homedir(), ".node9", "decisions.json");
|
|
2336
|
+
if (!import_fs8.default.existsSync(file)) return null;
|
|
2337
|
+
const decisions = JSON.parse(import_fs8.default.readFileSync(file, "utf-8"));
|
|
2269
2338
|
const d = decisions[toolName];
|
|
2270
2339
|
if (d === "allow" || d === "deny") return d;
|
|
2271
2340
|
} catch {
|
|
2272
2341
|
}
|
|
2273
2342
|
return null;
|
|
2274
2343
|
}
|
|
2275
|
-
var
|
|
2344
|
+
var import_fs8, import_path9, import_os7, PAUSED_FILE, TRUST_FILE;
|
|
2276
2345
|
var init_state = __esm({
|
|
2277
2346
|
"src/auth/state.ts"() {
|
|
2278
2347
|
"use strict";
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2348
|
+
import_fs8 = __toESM(require("fs"));
|
|
2349
|
+
import_path9 = __toESM(require("path"));
|
|
2350
|
+
import_os7 = __toESM(require("os"));
|
|
2282
2351
|
init_policy();
|
|
2283
|
-
PAUSED_FILE =
|
|
2284
|
-
TRUST_FILE =
|
|
2352
|
+
PAUSED_FILE = import_path9.default.join(import_os7.default.homedir(), ".node9", "PAUSED");
|
|
2353
|
+
TRUST_FILE = import_path9.default.join(import_os7.default.homedir(), ".node9", "trust.json");
|
|
2285
2354
|
}
|
|
2286
2355
|
});
|
|
2287
2356
|
|
|
2288
2357
|
// src/auth/daemon.ts
|
|
2289
2358
|
function getInternalToken() {
|
|
2290
2359
|
try {
|
|
2291
|
-
const pidFile =
|
|
2292
|
-
if (!
|
|
2293
|
-
const data = JSON.parse(
|
|
2360
|
+
const pidFile = import_path10.default.join(import_os8.default.homedir(), ".node9", "daemon.pid");
|
|
2361
|
+
if (!import_fs9.default.existsSync(pidFile)) return null;
|
|
2362
|
+
const data = JSON.parse(import_fs9.default.readFileSync(pidFile, "utf-8"));
|
|
2294
2363
|
process.kill(data.pid, 0);
|
|
2295
2364
|
return data.internalToken ?? null;
|
|
2296
2365
|
} catch {
|
|
@@ -2298,10 +2367,10 @@ function getInternalToken() {
|
|
|
2298
2367
|
}
|
|
2299
2368
|
}
|
|
2300
2369
|
function isDaemonRunning() {
|
|
2301
|
-
const pidFile =
|
|
2302
|
-
if (
|
|
2370
|
+
const pidFile = import_path10.default.join(import_os8.default.homedir(), ".node9", "daemon.pid");
|
|
2371
|
+
if (import_fs9.default.existsSync(pidFile)) {
|
|
2303
2372
|
try {
|
|
2304
|
-
const { pid, port } = JSON.parse(
|
|
2373
|
+
const { pid, port } = JSON.parse(import_fs9.default.readFileSync(pidFile, "utf-8"));
|
|
2305
2374
|
if (port !== DAEMON_PORT) return false;
|
|
2306
2375
|
process.kill(pid, 0);
|
|
2307
2376
|
return true;
|
|
@@ -2394,13 +2463,13 @@ async function resolveViaDaemon(id, decision, internalToken) {
|
|
|
2394
2463
|
signal: AbortSignal.timeout(3e3)
|
|
2395
2464
|
});
|
|
2396
2465
|
}
|
|
2397
|
-
var
|
|
2466
|
+
var import_fs9, import_path10, import_os8, import_child_process, DAEMON_PORT, DAEMON_HOST;
|
|
2398
2467
|
var init_daemon = __esm({
|
|
2399
2468
|
"src/auth/daemon.ts"() {
|
|
2400
2469
|
"use strict";
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2470
|
+
import_fs9 = __toESM(require("fs"));
|
|
2471
|
+
import_path10 = __toESM(require("path"));
|
|
2472
|
+
import_os8 = __toESM(require("os"));
|
|
2404
2473
|
import_child_process = require("child_process");
|
|
2405
2474
|
DAEMON_PORT = 7391;
|
|
2406
2475
|
DAEMON_HOST = "127.0.0.1";
|
|
@@ -2459,7 +2528,7 @@ function computeRiskMetadata(args, tier, blockedByLabel, matchedField, matchedWo
|
|
|
2459
2528
|
intent = "EDIT";
|
|
2460
2529
|
if (obj.file_path) {
|
|
2461
2530
|
editFilePath = String(obj.file_path);
|
|
2462
|
-
editFileName =
|
|
2531
|
+
editFileName = import_path11.default.basename(editFilePath);
|
|
2463
2532
|
}
|
|
2464
2533
|
const result = extractContext(String(obj.new_string), matchedWord);
|
|
2465
2534
|
contextSnippet = result.snippet;
|
|
@@ -2491,11 +2560,11 @@ function computeRiskMetadata(args, tier, blockedByLabel, matchedField, matchedWo
|
|
|
2491
2560
|
...ruleName && { ruleName }
|
|
2492
2561
|
};
|
|
2493
2562
|
}
|
|
2494
|
-
var
|
|
2563
|
+
var import_path11, CODE_KEYS;
|
|
2495
2564
|
var init_context_sniper = __esm({
|
|
2496
2565
|
"src/context-sniper.ts"() {
|
|
2497
2566
|
"use strict";
|
|
2498
|
-
|
|
2567
|
+
import_path11 = __toESM(require("path"));
|
|
2499
2568
|
CODE_KEYS = [
|
|
2500
2569
|
"command",
|
|
2501
2570
|
"cmd",
|
|
@@ -2534,7 +2603,7 @@ function formatArgs(args, matchedField, matchedWord) {
|
|
|
2534
2603
|
if (typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
2535
2604
|
const obj = parsed;
|
|
2536
2605
|
if (obj.old_string !== void 0 && obj.new_string !== void 0) {
|
|
2537
|
-
const file = obj.file_path ?
|
|
2606
|
+
const file = obj.file_path ? import_path12.default.basename(String(obj.file_path)) : "file";
|
|
2538
2607
|
const oldPreview = smartTruncate(String(obj.old_string), 120);
|
|
2539
2608
|
const newPreview = extractContext(String(obj.new_string), matchedWord).snippet;
|
|
2540
2609
|
return {
|
|
@@ -2707,12 +2776,12 @@ end run`;
|
|
|
2707
2776
|
}
|
|
2708
2777
|
});
|
|
2709
2778
|
}
|
|
2710
|
-
var import_child_process2,
|
|
2779
|
+
var import_child_process2, import_path12, isTestEnv;
|
|
2711
2780
|
var init_native = __esm({
|
|
2712
2781
|
"src/ui/native.ts"() {
|
|
2713
2782
|
"use strict";
|
|
2714
2783
|
import_child_process2 = require("child_process");
|
|
2715
|
-
|
|
2784
|
+
import_path12 = __toESM(require("path"));
|
|
2716
2785
|
init_context_sniper();
|
|
2717
2786
|
isTestEnv = () => {
|
|
2718
2787
|
return process.env.NODE_ENV === "test" || process.env.VITEST === "true" || !!process.env.VITEST || process.env.CI === "true" || !!process.env.CI || process.env.NODE9_TESTING === "1";
|
|
@@ -2732,9 +2801,9 @@ function auditLocalAllow(toolName, args, checkedBy, creds, meta) {
|
|
|
2732
2801
|
context: {
|
|
2733
2802
|
agent: meta?.agent,
|
|
2734
2803
|
mcpServer: meta?.mcpServer,
|
|
2735
|
-
hostname:
|
|
2804
|
+
hostname: import_os9.default.hostname(),
|
|
2736
2805
|
cwd: process.cwd(),
|
|
2737
|
-
platform:
|
|
2806
|
+
platform: import_os9.default.platform()
|
|
2738
2807
|
}
|
|
2739
2808
|
}),
|
|
2740
2809
|
signal: AbortSignal.timeout(5e3)
|
|
@@ -2755,9 +2824,9 @@ async function initNode9SaaS(toolName, args, creds, meta, riskMetadata) {
|
|
|
2755
2824
|
context: {
|
|
2756
2825
|
agent: meta?.agent,
|
|
2757
2826
|
mcpServer: meta?.mcpServer,
|
|
2758
|
-
hostname:
|
|
2827
|
+
hostname: import_os9.default.hostname(),
|
|
2759
2828
|
cwd: process.cwd(),
|
|
2760
|
-
platform:
|
|
2829
|
+
platform: import_os9.default.platform()
|
|
2761
2830
|
},
|
|
2762
2831
|
...riskMetadata && { riskMetadata }
|
|
2763
2832
|
}),
|
|
@@ -2813,26 +2882,26 @@ async function resolveNode9SaaS(requestId, creds, approved, decidedBy) {
|
|
|
2813
2882
|
});
|
|
2814
2883
|
clearTimeout(timer);
|
|
2815
2884
|
if (!res.ok) {
|
|
2816
|
-
|
|
2885
|
+
import_fs10.default.appendFileSync(
|
|
2817
2886
|
HOOK_DEBUG_LOG,
|
|
2818
2887
|
`[resolve-cloud] PATCH ${resolveUrl} \u2192 HTTP ${res.status}
|
|
2819
2888
|
`
|
|
2820
2889
|
);
|
|
2821
2890
|
}
|
|
2822
2891
|
} catch (err) {
|
|
2823
|
-
|
|
2892
|
+
import_fs10.default.appendFileSync(
|
|
2824
2893
|
HOOK_DEBUG_LOG,
|
|
2825
2894
|
`[resolve-cloud] PATCH failed for ${requestId}: ${err.message}
|
|
2826
2895
|
`
|
|
2827
2896
|
);
|
|
2828
2897
|
}
|
|
2829
2898
|
}
|
|
2830
|
-
var
|
|
2899
|
+
var import_fs10, import_os9;
|
|
2831
2900
|
var init_cloud = __esm({
|
|
2832
2901
|
"src/auth/cloud.ts"() {
|
|
2833
2902
|
"use strict";
|
|
2834
|
-
|
|
2835
|
-
|
|
2903
|
+
import_fs10 = __toESM(require("fs"));
|
|
2904
|
+
import_os9 = __toESM(require("os"));
|
|
2836
2905
|
init_audit();
|
|
2837
2906
|
}
|
|
2838
2907
|
});
|
|
@@ -3184,13 +3253,13 @@ REASON: Action blocked because no approval channels are available. (Native/Brows
|
|
|
3184
3253
|
}
|
|
3185
3254
|
return finalResult;
|
|
3186
3255
|
}
|
|
3187
|
-
var import_net,
|
|
3256
|
+
var import_net, import_path13, import_os10, import_crypto2, ACTIVITY_SOCKET_PATH;
|
|
3188
3257
|
var init_orchestrator = __esm({
|
|
3189
3258
|
"src/auth/orchestrator.ts"() {
|
|
3190
3259
|
"use strict";
|
|
3191
3260
|
import_net = __toESM(require("net"));
|
|
3192
|
-
|
|
3193
|
-
|
|
3261
|
+
import_path13 = __toESM(require("path"));
|
|
3262
|
+
import_os10 = __toESM(require("os"));
|
|
3194
3263
|
import_crypto2 = require("crypto");
|
|
3195
3264
|
init_native();
|
|
3196
3265
|
init_context_sniper();
|
|
@@ -3201,7 +3270,7 @@ var init_orchestrator = __esm({
|
|
|
3201
3270
|
init_state();
|
|
3202
3271
|
init_daemon();
|
|
3203
3272
|
init_cloud();
|
|
3204
|
-
ACTIVITY_SOCKET_PATH = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" :
|
|
3273
|
+
ACTIVITY_SOCKET_PATH = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : import_path13.default.join(import_os10.default.tmpdir(), "node9-activity.sock");
|
|
3205
3274
|
}
|
|
3206
3275
|
});
|
|
3207
3276
|
|
|
@@ -4694,11 +4763,11 @@ function markRejectionHandlerRegistered() {
|
|
|
4694
4763
|
daemonRejectionHandlerRegistered = true;
|
|
4695
4764
|
}
|
|
4696
4765
|
function atomicWriteSync2(filePath, data, options) {
|
|
4697
|
-
const dir =
|
|
4698
|
-
if (!
|
|
4766
|
+
const dir = import_path15.default.dirname(filePath);
|
|
4767
|
+
if (!import_fs12.default.existsSync(dir)) import_fs12.default.mkdirSync(dir, { recursive: true });
|
|
4699
4768
|
const tmpPath = `${filePath}.${(0, import_crypto3.randomUUID)()}.tmp`;
|
|
4700
|
-
|
|
4701
|
-
|
|
4769
|
+
import_fs12.default.writeFileSync(tmpPath, data, options);
|
|
4770
|
+
import_fs12.default.renameSync(tmpPath, filePath);
|
|
4702
4771
|
}
|
|
4703
4772
|
function redactArgs(value) {
|
|
4704
4773
|
if (!value || typeof value !== "object") return value;
|
|
@@ -4718,16 +4787,16 @@ function appendAuditLog(data) {
|
|
|
4718
4787
|
decision: data.decision,
|
|
4719
4788
|
source: "daemon"
|
|
4720
4789
|
};
|
|
4721
|
-
const dir =
|
|
4722
|
-
if (!
|
|
4723
|
-
|
|
4790
|
+
const dir = import_path15.default.dirname(AUDIT_LOG_FILE);
|
|
4791
|
+
if (!import_fs12.default.existsSync(dir)) import_fs12.default.mkdirSync(dir, { recursive: true });
|
|
4792
|
+
import_fs12.default.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
|
|
4724
4793
|
} catch {
|
|
4725
4794
|
}
|
|
4726
4795
|
}
|
|
4727
4796
|
function getAuditHistory(limit = 20) {
|
|
4728
4797
|
try {
|
|
4729
|
-
if (!
|
|
4730
|
-
const lines =
|
|
4798
|
+
if (!import_fs12.default.existsSync(AUDIT_LOG_FILE)) return [];
|
|
4799
|
+
const lines = import_fs12.default.readFileSync(AUDIT_LOG_FILE, "utf-8").trim().split("\n");
|
|
4731
4800
|
if (lines.length === 1 && lines[0] === "") return [];
|
|
4732
4801
|
return lines.slice(-limit).map((l) => JSON.parse(l)).reverse();
|
|
4733
4802
|
} catch {
|
|
@@ -4736,19 +4805,19 @@ function getAuditHistory(limit = 20) {
|
|
|
4736
4805
|
}
|
|
4737
4806
|
function getOrgName() {
|
|
4738
4807
|
try {
|
|
4739
|
-
if (
|
|
4808
|
+
if (import_fs12.default.existsSync(CREDENTIALS_FILE)) return "Node9 Cloud";
|
|
4740
4809
|
} catch {
|
|
4741
4810
|
}
|
|
4742
4811
|
return null;
|
|
4743
4812
|
}
|
|
4744
4813
|
function hasStoredSlackKey() {
|
|
4745
|
-
return
|
|
4814
|
+
return import_fs12.default.existsSync(CREDENTIALS_FILE);
|
|
4746
4815
|
}
|
|
4747
4816
|
function writeGlobalSetting(key, value) {
|
|
4748
4817
|
let config = {};
|
|
4749
4818
|
try {
|
|
4750
|
-
if (
|
|
4751
|
-
config = JSON.parse(
|
|
4819
|
+
if (import_fs12.default.existsSync(GLOBAL_CONFIG_FILE)) {
|
|
4820
|
+
config = JSON.parse(import_fs12.default.readFileSync(GLOBAL_CONFIG_FILE, "utf-8"));
|
|
4752
4821
|
}
|
|
4753
4822
|
} catch {
|
|
4754
4823
|
}
|
|
@@ -4760,8 +4829,8 @@ function writeTrustEntry(toolName, durationMs) {
|
|
|
4760
4829
|
try {
|
|
4761
4830
|
let trust = { entries: [] };
|
|
4762
4831
|
try {
|
|
4763
|
-
if (
|
|
4764
|
-
trust = JSON.parse(
|
|
4832
|
+
if (import_fs12.default.existsSync(TRUST_FILE2))
|
|
4833
|
+
trust = JSON.parse(import_fs12.default.readFileSync(TRUST_FILE2, "utf-8"));
|
|
4765
4834
|
} catch {
|
|
4766
4835
|
}
|
|
4767
4836
|
trust.entries = trust.entries.filter((e) => e.tool !== toolName && e.expiry > Date.now());
|
|
@@ -4772,8 +4841,8 @@ function writeTrustEntry(toolName, durationMs) {
|
|
|
4772
4841
|
}
|
|
4773
4842
|
function readPersistentDecisions() {
|
|
4774
4843
|
try {
|
|
4775
|
-
if (
|
|
4776
|
-
return JSON.parse(
|
|
4844
|
+
if (import_fs12.default.existsSync(DECISIONS_FILE)) {
|
|
4845
|
+
return JSON.parse(import_fs12.default.readFileSync(DECISIONS_FILE, "utf-8"));
|
|
4777
4846
|
}
|
|
4778
4847
|
} catch {
|
|
4779
4848
|
}
|
|
@@ -4838,7 +4907,7 @@ function abandonPending() {
|
|
|
4838
4907
|
});
|
|
4839
4908
|
if (autoStarted) {
|
|
4840
4909
|
try {
|
|
4841
|
-
|
|
4910
|
+
import_fs12.default.unlinkSync(DAEMON_PID_FILE);
|
|
4842
4911
|
} catch {
|
|
4843
4912
|
}
|
|
4844
4913
|
setTimeout(() => {
|
|
@@ -4849,7 +4918,7 @@ function abandonPending() {
|
|
|
4849
4918
|
}
|
|
4850
4919
|
function startActivitySocket() {
|
|
4851
4920
|
try {
|
|
4852
|
-
|
|
4921
|
+
import_fs12.default.unlinkSync(ACTIVITY_SOCKET_PATH2);
|
|
4853
4922
|
} catch {
|
|
4854
4923
|
}
|
|
4855
4924
|
const ACTIVITY_MAX_BYTES = 1024 * 1024;
|
|
@@ -4891,29 +4960,29 @@ function startActivitySocket() {
|
|
|
4891
4960
|
unixServer.listen(ACTIVITY_SOCKET_PATH2);
|
|
4892
4961
|
process.on("exit", () => {
|
|
4893
4962
|
try {
|
|
4894
|
-
|
|
4963
|
+
import_fs12.default.unlinkSync(ACTIVITY_SOCKET_PATH2);
|
|
4895
4964
|
} catch {
|
|
4896
4965
|
}
|
|
4897
4966
|
});
|
|
4898
4967
|
}
|
|
4899
|
-
var import_net2,
|
|
4968
|
+
var import_net2, import_fs12, import_path15, import_os12, import_child_process3, import_crypto3, homeDir, DAEMON_PID_FILE, DECISIONS_FILE, AUDIT_LOG_FILE, TRUST_FILE2, GLOBAL_CONFIG_FILE, CREDENTIALS_FILE, pending, sseClients, _abandonTimer, _hadBrowserClient, _daemonServer, daemonRejectionHandlerRegistered, AUTO_DENY_MS, TRUST_DURATIONS, autoStarted, ACTIVITY_SOCKET_PATH2, ACTIVITY_RING_SIZE, activityRing, SECRET_KEY_RE;
|
|
4900
4969
|
var init_state2 = __esm({
|
|
4901
4970
|
"src/daemon/state.ts"() {
|
|
4902
4971
|
"use strict";
|
|
4903
4972
|
import_net2 = __toESM(require("net"));
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4973
|
+
import_fs12 = __toESM(require("fs"));
|
|
4974
|
+
import_path15 = __toESM(require("path"));
|
|
4975
|
+
import_os12 = __toESM(require("os"));
|
|
4907
4976
|
import_child_process3 = require("child_process");
|
|
4908
4977
|
import_crypto3 = require("crypto");
|
|
4909
4978
|
init_daemon();
|
|
4910
|
-
homeDir =
|
|
4911
|
-
DAEMON_PID_FILE =
|
|
4912
|
-
DECISIONS_FILE =
|
|
4913
|
-
AUDIT_LOG_FILE =
|
|
4914
|
-
TRUST_FILE2 =
|
|
4915
|
-
GLOBAL_CONFIG_FILE =
|
|
4916
|
-
CREDENTIALS_FILE =
|
|
4979
|
+
homeDir = import_os12.default.homedir();
|
|
4980
|
+
DAEMON_PID_FILE = import_path15.default.join(homeDir, ".node9", "daemon.pid");
|
|
4981
|
+
DECISIONS_FILE = import_path15.default.join(homeDir, ".node9", "decisions.json");
|
|
4982
|
+
AUDIT_LOG_FILE = import_path15.default.join(homeDir, ".node9", "audit.log");
|
|
4983
|
+
TRUST_FILE2 = import_path15.default.join(homeDir, ".node9", "trust.json");
|
|
4984
|
+
GLOBAL_CONFIG_FILE = import_path15.default.join(homeDir, ".node9", "config.json");
|
|
4985
|
+
CREDENTIALS_FILE = import_path15.default.join(homeDir, ".node9", "credentials.json");
|
|
4917
4986
|
pending = /* @__PURE__ */ new Map();
|
|
4918
4987
|
sseClients = /* @__PURE__ */ new Set();
|
|
4919
4988
|
_abandonTimer = null;
|
|
@@ -4927,7 +4996,7 @@ var init_state2 = __esm({
|
|
|
4927
4996
|
"2h": 2 * 60 * 6e4
|
|
4928
4997
|
};
|
|
4929
4998
|
autoStarted = process.env.NODE9_AUTO_STARTED === "1";
|
|
4930
|
-
ACTIVITY_SOCKET_PATH2 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" :
|
|
4999
|
+
ACTIVITY_SOCKET_PATH2 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : import_path15.default.join(import_os12.default.tmpdir(), "node9-activity.sock");
|
|
4931
5000
|
ACTIVITY_RING_SIZE = 100;
|
|
4932
5001
|
activityRing = [];
|
|
4933
5002
|
SECRET_KEY_RE = /password|secret|token|key|apikey|credential|auth/i;
|
|
@@ -4950,7 +5019,7 @@ function startDaemon() {
|
|
|
4950
5019
|
idleTimer = setTimeout(() => {
|
|
4951
5020
|
if (autoStarted) {
|
|
4952
5021
|
try {
|
|
4953
|
-
|
|
5022
|
+
import_fs13.default.unlinkSync(DAEMON_PID_FILE);
|
|
4954
5023
|
} catch {
|
|
4955
5024
|
}
|
|
4956
5025
|
}
|
|
@@ -5092,7 +5161,7 @@ data: ${JSON.stringify(item.data)}
|
|
|
5092
5161
|
status: "pending"
|
|
5093
5162
|
});
|
|
5094
5163
|
}
|
|
5095
|
-
const projectCwd = typeof cwd === "string" &&
|
|
5164
|
+
const projectCwd = typeof cwd === "string" && import_path16.default.isAbsolute(cwd) ? cwd : void 0;
|
|
5096
5165
|
const projectConfig = getConfig(projectCwd);
|
|
5097
5166
|
const browserEnabled = projectConfig.settings.approvers?.browser !== false;
|
|
5098
5167
|
const terminalEnabled = projectConfig.settings.approvers?.terminal !== false;
|
|
@@ -5396,14 +5465,14 @@ data: ${JSON.stringify(item.data)}
|
|
|
5396
5465
|
server.on("error", (e) => {
|
|
5397
5466
|
if (e.code === "EADDRINUSE") {
|
|
5398
5467
|
try {
|
|
5399
|
-
if (
|
|
5400
|
-
const { pid } = JSON.parse(
|
|
5468
|
+
if (import_fs13.default.existsSync(DAEMON_PID_FILE)) {
|
|
5469
|
+
const { pid } = JSON.parse(import_fs13.default.readFileSync(DAEMON_PID_FILE, "utf-8"));
|
|
5401
5470
|
process.kill(pid, 0);
|
|
5402
5471
|
return process.exit(0);
|
|
5403
5472
|
}
|
|
5404
5473
|
} catch {
|
|
5405
5474
|
try {
|
|
5406
|
-
|
|
5475
|
+
import_fs13.default.unlinkSync(DAEMON_PID_FILE);
|
|
5407
5476
|
} catch {
|
|
5408
5477
|
}
|
|
5409
5478
|
server.listen(DAEMON_PORT, DAEMON_HOST);
|
|
@@ -5462,13 +5531,13 @@ data: ${JSON.stringify(item.data)}
|
|
|
5462
5531
|
}
|
|
5463
5532
|
startActivitySocket();
|
|
5464
5533
|
}
|
|
5465
|
-
var import_http,
|
|
5534
|
+
var import_http, import_fs13, import_path16, import_crypto4, import_child_process4, import_chalk2;
|
|
5466
5535
|
var init_server = __esm({
|
|
5467
5536
|
"src/daemon/server.ts"() {
|
|
5468
5537
|
"use strict";
|
|
5469
5538
|
import_http = __toESM(require("http"));
|
|
5470
|
-
|
|
5471
|
-
|
|
5539
|
+
import_fs13 = __toESM(require("fs"));
|
|
5540
|
+
import_path16 = __toESM(require("path"));
|
|
5472
5541
|
import_crypto4 = require("crypto");
|
|
5473
5542
|
import_child_process4 = require("child_process");
|
|
5474
5543
|
import_chalk2 = __toESM(require("chalk"));
|
|
@@ -5481,24 +5550,24 @@ var init_server = __esm({
|
|
|
5481
5550
|
|
|
5482
5551
|
// src/daemon/index.ts
|
|
5483
5552
|
function stopDaemon() {
|
|
5484
|
-
if (!
|
|
5553
|
+
if (!import_fs14.default.existsSync(DAEMON_PID_FILE)) return console.log(import_chalk3.default.yellow("Not running."));
|
|
5485
5554
|
try {
|
|
5486
|
-
const { pid } = JSON.parse(
|
|
5555
|
+
const { pid } = JSON.parse(import_fs14.default.readFileSync(DAEMON_PID_FILE, "utf-8"));
|
|
5487
5556
|
process.kill(pid, "SIGTERM");
|
|
5488
5557
|
console.log(import_chalk3.default.green("\u2705 Stopped."));
|
|
5489
5558
|
} catch {
|
|
5490
5559
|
console.log(import_chalk3.default.gray("Cleaned up stale PID file."));
|
|
5491
5560
|
} finally {
|
|
5492
5561
|
try {
|
|
5493
|
-
|
|
5562
|
+
import_fs14.default.unlinkSync(DAEMON_PID_FILE);
|
|
5494
5563
|
} catch {
|
|
5495
5564
|
}
|
|
5496
5565
|
}
|
|
5497
5566
|
}
|
|
5498
5567
|
function daemonStatus() {
|
|
5499
|
-
if (
|
|
5568
|
+
if (import_fs14.default.existsSync(DAEMON_PID_FILE)) {
|
|
5500
5569
|
try {
|
|
5501
|
-
const { pid } = JSON.parse(
|
|
5570
|
+
const { pid } = JSON.parse(import_fs14.default.readFileSync(DAEMON_PID_FILE, "utf-8"));
|
|
5502
5571
|
process.kill(pid, 0);
|
|
5503
5572
|
console.log(import_chalk3.default.green("Node9 daemon: running"));
|
|
5504
5573
|
return;
|
|
@@ -5517,11 +5586,11 @@ function daemonStatus() {
|
|
|
5517
5586
|
console.log(import_chalk3.default.yellow("Node9 daemon: not running"));
|
|
5518
5587
|
}
|
|
5519
5588
|
}
|
|
5520
|
-
var
|
|
5589
|
+
var import_fs14, import_chalk3, import_child_process5;
|
|
5521
5590
|
var init_daemon2 = __esm({
|
|
5522
5591
|
"src/daemon/index.ts"() {
|
|
5523
5592
|
"use strict";
|
|
5524
|
-
|
|
5593
|
+
import_fs14 = __toESM(require("fs"));
|
|
5525
5594
|
import_chalk3 = __toESM(require("chalk"));
|
|
5526
5595
|
import_child_process5 = require("child_process");
|
|
5527
5596
|
init_server();
|
|
@@ -5548,17 +5617,17 @@ function formatBase(activity) {
|
|
|
5548
5617
|
const toolName = activity.tool.slice(0, 16).padEnd(16);
|
|
5549
5618
|
const argsStr = JSON.stringify(activity.args ?? {}).replace(/\s+/g, " ");
|
|
5550
5619
|
const argsPreview = argsStr.length > 70 ? argsStr.slice(0, 70) + "\u2026" : argsStr;
|
|
5551
|
-
return `${
|
|
5620
|
+
return `${import_chalk15.default.gray(time)} ${icon} ${import_chalk15.default.white.bold(toolName)} ${import_chalk15.default.dim(argsPreview)}`;
|
|
5552
5621
|
}
|
|
5553
5622
|
function renderResult(activity, result) {
|
|
5554
5623
|
const base = formatBase(activity);
|
|
5555
5624
|
let status;
|
|
5556
5625
|
if (result.status === "allow") {
|
|
5557
|
-
status =
|
|
5626
|
+
status = import_chalk15.default.green("\u2713 ALLOW");
|
|
5558
5627
|
} else if (result.status === "dlp") {
|
|
5559
|
-
status =
|
|
5628
|
+
status = import_chalk15.default.bgRed.white.bold(" \u{1F6E1}\uFE0F DLP ");
|
|
5560
5629
|
} else {
|
|
5561
|
-
status =
|
|
5630
|
+
status = import_chalk15.default.red("\u2717 BLOCK");
|
|
5562
5631
|
}
|
|
5563
5632
|
if (process.stdout.isTTY) {
|
|
5564
5633
|
import_readline3.default.clearLine(process.stdout, 0);
|
|
@@ -5568,16 +5637,16 @@ function renderResult(activity, result) {
|
|
|
5568
5637
|
}
|
|
5569
5638
|
function renderPending(activity) {
|
|
5570
5639
|
if (!process.stdout.isTTY) return;
|
|
5571
|
-
process.stdout.write(`${formatBase(activity)} ${
|
|
5640
|
+
process.stdout.write(`${formatBase(activity)} ${import_chalk15.default.yellow("\u25CF \u2026")}\r`);
|
|
5572
5641
|
}
|
|
5573
5642
|
async function ensureDaemon() {
|
|
5574
5643
|
let pidPort = null;
|
|
5575
|
-
if (
|
|
5644
|
+
if (import_fs21.default.existsSync(PID_FILE)) {
|
|
5576
5645
|
try {
|
|
5577
|
-
const { port } = JSON.parse(
|
|
5646
|
+
const { port } = JSON.parse(import_fs21.default.readFileSync(PID_FILE, "utf-8"));
|
|
5578
5647
|
pidPort = port;
|
|
5579
5648
|
} catch {
|
|
5580
|
-
console.error(
|
|
5649
|
+
console.error(import_chalk15.default.dim("\u26A0\uFE0F Could not read PID file; falling back to default port."));
|
|
5581
5650
|
}
|
|
5582
5651
|
}
|
|
5583
5652
|
const checkPort = pidPort ?? DAEMON_PORT;
|
|
@@ -5588,7 +5657,7 @@ async function ensureDaemon() {
|
|
|
5588
5657
|
if (res.ok) return checkPort;
|
|
5589
5658
|
} catch {
|
|
5590
5659
|
}
|
|
5591
|
-
console.log(
|
|
5660
|
+
console.log(import_chalk15.default.dim("\u{1F6E1}\uFE0F Starting Node9 daemon..."));
|
|
5592
5661
|
const child = (0, import_child_process13.spawn)(process.execPath, [process.argv[1], "daemon"], {
|
|
5593
5662
|
detached: true,
|
|
5594
5663
|
stdio: "ignore",
|
|
@@ -5605,7 +5674,7 @@ async function ensureDaemon() {
|
|
|
5605
5674
|
} catch {
|
|
5606
5675
|
}
|
|
5607
5676
|
}
|
|
5608
|
-
console.error(
|
|
5677
|
+
console.error(import_chalk15.default.red("\u274C Daemon failed to start. Try: node9 daemon start"));
|
|
5609
5678
|
process.exit(1);
|
|
5610
5679
|
}
|
|
5611
5680
|
function postDecisionHttp(id, decision, csrfToken, port) {
|
|
@@ -5676,7 +5745,7 @@ async function startTail(options = {}) {
|
|
|
5676
5745
|
req2.end();
|
|
5677
5746
|
});
|
|
5678
5747
|
if (result.ok) {
|
|
5679
|
-
console.log(
|
|
5748
|
+
console.log(import_chalk15.default.green("\u2713 Flight Recorder buffer cleared."));
|
|
5680
5749
|
} else if (result.code === "ECONNREFUSED") {
|
|
5681
5750
|
throw new Error("Daemon is not running. Start it with: node9 daemon start");
|
|
5682
5751
|
} else if (result.code === "ETIMEDOUT") {
|
|
@@ -5739,16 +5808,16 @@ async function startTail(options = {}) {
|
|
|
5739
5808
|
process.stdout.write(SHOW_CURSOR);
|
|
5740
5809
|
postDecisionHttp(req2.id, decision, csrfToken, port).catch((err) => {
|
|
5741
5810
|
try {
|
|
5742
|
-
|
|
5743
|
-
|
|
5811
|
+
import_fs21.default.appendFileSync(
|
|
5812
|
+
import_path23.default.join(import_os19.default.homedir(), ".node9", "hook-debug.log"),
|
|
5744
5813
|
`[tail] POST /decision failed: ${String(err)}
|
|
5745
5814
|
`
|
|
5746
5815
|
);
|
|
5747
5816
|
} catch {
|
|
5748
5817
|
}
|
|
5749
5818
|
});
|
|
5750
|
-
const decisionLabel = decision === "allow" ?
|
|
5751
|
-
console.log(`${
|
|
5819
|
+
const decisionLabel = decision === "allow" ? import_chalk15.default.green("\u2713 ALLOWED (terminal)") : import_chalk15.default.red("\u2717 DENIED (terminal)");
|
|
5820
|
+
console.log(`${import_chalk15.default.cyan("\u25C6")} ${import_chalk15.default.bold(req2.toolName.padEnd(16))} ${decisionLabel}`);
|
|
5752
5821
|
approvalQueue.shift();
|
|
5753
5822
|
cardActive = false;
|
|
5754
5823
|
showNextCard();
|
|
@@ -5785,16 +5854,16 @@ async function startTail(options = {}) {
|
|
|
5785
5854
|
}
|
|
5786
5855
|
} catch {
|
|
5787
5856
|
}
|
|
5788
|
-
console.log(
|
|
5789
|
-
\u{1F6F0}\uFE0F Node9 tail `) +
|
|
5857
|
+
console.log(import_chalk15.default.cyan.bold(`
|
|
5858
|
+
\u{1F6F0}\uFE0F Node9 tail `) + import_chalk15.default.dim(`\u2192 ${dashboardUrl}`));
|
|
5790
5859
|
if (canApprove) {
|
|
5791
|
-
console.log(
|
|
5860
|
+
console.log(import_chalk15.default.dim("Interactive approvals enabled. [A] Allow [D] Deny"));
|
|
5792
5861
|
}
|
|
5793
5862
|
if (options.history) {
|
|
5794
|
-
console.log(
|
|
5863
|
+
console.log(import_chalk15.default.dim("Showing history + live events. Press Ctrl+C to exit.\n"));
|
|
5795
5864
|
} else {
|
|
5796
5865
|
console.log(
|
|
5797
|
-
|
|
5866
|
+
import_chalk15.default.dim("Showing live events only. Use --history to include past. Press Ctrl+C to exit.\n")
|
|
5798
5867
|
);
|
|
5799
5868
|
}
|
|
5800
5869
|
process.on("SIGINT", () => {
|
|
@@ -5804,13 +5873,13 @@ async function startTail(options = {}) {
|
|
|
5804
5873
|
import_readline3.default.clearLine(process.stdout, 0);
|
|
5805
5874
|
import_readline3.default.cursorTo(process.stdout, 0);
|
|
5806
5875
|
}
|
|
5807
|
-
console.log(
|
|
5876
|
+
console.log(import_chalk15.default.dim("\n\u{1F6F0}\uFE0F Disconnected."));
|
|
5808
5877
|
process.exit(0);
|
|
5809
5878
|
});
|
|
5810
5879
|
const sseUrl = `http://127.0.0.1:${port}/events?capabilities=input`;
|
|
5811
5880
|
const req = import_http2.default.get(sseUrl, (res) => {
|
|
5812
5881
|
if (res.statusCode !== 200) {
|
|
5813
|
-
console.error(
|
|
5882
|
+
console.error(import_chalk15.default.red(`Failed to connect: HTTP ${res.statusCode}`));
|
|
5814
5883
|
process.exit(1);
|
|
5815
5884
|
}
|
|
5816
5885
|
let currentEvent = "";
|
|
@@ -5840,7 +5909,7 @@ async function startTail(options = {}) {
|
|
|
5840
5909
|
import_readline3.default.clearLine(process.stdout, 0);
|
|
5841
5910
|
import_readline3.default.cursorTo(process.stdout, 0);
|
|
5842
5911
|
}
|
|
5843
|
-
console.log(
|
|
5912
|
+
console.log(import_chalk15.default.red("\n\u274C Daemon disconnected."));
|
|
5844
5913
|
process.exit(1);
|
|
5845
5914
|
});
|
|
5846
5915
|
});
|
|
@@ -5920,25 +5989,25 @@ async function startTail(options = {}) {
|
|
|
5920
5989
|
}
|
|
5921
5990
|
req.on("error", (err) => {
|
|
5922
5991
|
const msg = err.code === "ECONNREFUSED" ? "Daemon is not running. Start it with: node9 daemon start" : err.message;
|
|
5923
|
-
console.error(
|
|
5992
|
+
console.error(import_chalk15.default.red(`
|
|
5924
5993
|
\u274C ${msg}`));
|
|
5925
5994
|
process.exit(1);
|
|
5926
5995
|
});
|
|
5927
5996
|
}
|
|
5928
|
-
var import_http2,
|
|
5997
|
+
var import_http2, import_chalk15, import_fs21, import_os19, import_path23, import_readline3, import_child_process13, PID_FILE, ICONS, RESET, BOLD, RED, YELLOW, CYAN, GRAY, GREEN, HIDE_CURSOR, SHOW_CURSOR, ERASE_DOWN, SAVE_CURSOR, RESTORE_CURSOR;
|
|
5929
5998
|
var init_tail = __esm({
|
|
5930
5999
|
"src/tui/tail.ts"() {
|
|
5931
6000
|
"use strict";
|
|
5932
6001
|
import_http2 = __toESM(require("http"));
|
|
5933
|
-
|
|
5934
|
-
|
|
5935
|
-
|
|
5936
|
-
|
|
6002
|
+
import_chalk15 = __toESM(require("chalk"));
|
|
6003
|
+
import_fs21 = __toESM(require("fs"));
|
|
6004
|
+
import_os19 = __toESM(require("os"));
|
|
6005
|
+
import_path23 = __toESM(require("path"));
|
|
5937
6006
|
import_readline3 = __toESM(require("readline"));
|
|
5938
6007
|
import_child_process13 = require("child_process");
|
|
5939
6008
|
init_daemon2();
|
|
5940
6009
|
init_core();
|
|
5941
|
-
PID_FILE =
|
|
6010
|
+
PID_FILE = import_path23.default.join(import_os19.default.homedir(), ".node9", "daemon.pid");
|
|
5942
6011
|
ICONS = {
|
|
5943
6012
|
bash: "\u{1F4BB}",
|
|
5944
6013
|
shell: "\u{1F4BB}",
|
|
@@ -5976,9 +6045,9 @@ var import_commander = require("commander");
|
|
|
5976
6045
|
init_core();
|
|
5977
6046
|
|
|
5978
6047
|
// src/setup.ts
|
|
5979
|
-
var
|
|
5980
|
-
var
|
|
5981
|
-
var
|
|
6048
|
+
var import_fs11 = __toESM(require("fs"));
|
|
6049
|
+
var import_path14 = __toESM(require("path"));
|
|
6050
|
+
var import_os11 = __toESM(require("os"));
|
|
5982
6051
|
var import_chalk = __toESM(require("chalk"));
|
|
5983
6052
|
var import_prompts = require("@inquirer/prompts");
|
|
5984
6053
|
function printDaemonTip() {
|
|
@@ -5995,26 +6064,26 @@ function fullPathCommand(subcommand) {
|
|
|
5995
6064
|
}
|
|
5996
6065
|
function readJson(filePath) {
|
|
5997
6066
|
try {
|
|
5998
|
-
if (
|
|
5999
|
-
return JSON.parse(
|
|
6067
|
+
if (import_fs11.default.existsSync(filePath)) {
|
|
6068
|
+
return JSON.parse(import_fs11.default.readFileSync(filePath, "utf-8"));
|
|
6000
6069
|
}
|
|
6001
6070
|
} catch {
|
|
6002
6071
|
}
|
|
6003
6072
|
return null;
|
|
6004
6073
|
}
|
|
6005
6074
|
function writeJson(filePath, data) {
|
|
6006
|
-
const dir =
|
|
6007
|
-
if (!
|
|
6008
|
-
|
|
6075
|
+
const dir = import_path14.default.dirname(filePath);
|
|
6076
|
+
if (!import_fs11.default.existsSync(dir)) import_fs11.default.mkdirSync(dir, { recursive: true });
|
|
6077
|
+
import_fs11.default.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n");
|
|
6009
6078
|
}
|
|
6010
6079
|
function isNode9Hook(cmd) {
|
|
6011
6080
|
if (!cmd) return false;
|
|
6012
6081
|
return /(?:^|[\s/\\])node9 (?:check|log)/.test(cmd) || /(?:^|[\s/\\])cli\.js (?:check|log)/.test(cmd);
|
|
6013
6082
|
}
|
|
6014
6083
|
function teardownClaude() {
|
|
6015
|
-
const homeDir2 =
|
|
6016
|
-
const hooksPath =
|
|
6017
|
-
const mcpPath =
|
|
6084
|
+
const homeDir2 = import_os11.default.homedir();
|
|
6085
|
+
const hooksPath = import_path14.default.join(homeDir2, ".claude", "settings.json");
|
|
6086
|
+
const mcpPath = import_path14.default.join(homeDir2, ".claude.json");
|
|
6018
6087
|
let changed = false;
|
|
6019
6088
|
const settings = readJson(hooksPath);
|
|
6020
6089
|
if (settings?.hooks) {
|
|
@@ -6062,8 +6131,8 @@ function teardownClaude() {
|
|
|
6062
6131
|
}
|
|
6063
6132
|
}
|
|
6064
6133
|
function teardownGemini() {
|
|
6065
|
-
const homeDir2 =
|
|
6066
|
-
const settingsPath =
|
|
6134
|
+
const homeDir2 = import_os11.default.homedir();
|
|
6135
|
+
const settingsPath = import_path14.default.join(homeDir2, ".gemini", "settings.json");
|
|
6067
6136
|
const settings = readJson(settingsPath);
|
|
6068
6137
|
if (!settings) {
|
|
6069
6138
|
console.log(import_chalk.default.blue(" \u2139\uFE0F ~/.gemini/settings.json not found \u2014 nothing to remove"));
|
|
@@ -6101,8 +6170,8 @@ function teardownGemini() {
|
|
|
6101
6170
|
}
|
|
6102
6171
|
}
|
|
6103
6172
|
function teardownCursor() {
|
|
6104
|
-
const homeDir2 =
|
|
6105
|
-
const mcpPath =
|
|
6173
|
+
const homeDir2 = import_os11.default.homedir();
|
|
6174
|
+
const mcpPath = import_path14.default.join(homeDir2, ".cursor", "mcp.json");
|
|
6106
6175
|
const mcpConfig = readJson(mcpPath);
|
|
6107
6176
|
if (!mcpConfig?.mcpServers) {
|
|
6108
6177
|
console.log(import_chalk.default.blue(" \u2139\uFE0F ~/.cursor/mcp.json not found \u2014 nothing to remove"));
|
|
@@ -6128,9 +6197,9 @@ function teardownCursor() {
|
|
|
6128
6197
|
}
|
|
6129
6198
|
}
|
|
6130
6199
|
async function setupClaude() {
|
|
6131
|
-
const homeDir2 =
|
|
6132
|
-
const mcpPath =
|
|
6133
|
-
const hooksPath =
|
|
6200
|
+
const homeDir2 = import_os11.default.homedir();
|
|
6201
|
+
const mcpPath = import_path14.default.join(homeDir2, ".claude.json");
|
|
6202
|
+
const hooksPath = import_path14.default.join(homeDir2, ".claude", "settings.json");
|
|
6134
6203
|
const claudeConfig = readJson(mcpPath) ?? {};
|
|
6135
6204
|
const settings = readJson(hooksPath) ?? {};
|
|
6136
6205
|
const servers = claudeConfig.mcpServers ?? {};
|
|
@@ -6204,8 +6273,8 @@ async function setupClaude() {
|
|
|
6204
6273
|
}
|
|
6205
6274
|
}
|
|
6206
6275
|
async function setupGemini() {
|
|
6207
|
-
const homeDir2 =
|
|
6208
|
-
const settingsPath =
|
|
6276
|
+
const homeDir2 = import_os11.default.homedir();
|
|
6277
|
+
const settingsPath = import_path14.default.join(homeDir2, ".gemini", "settings.json");
|
|
6209
6278
|
const settings = readJson(settingsPath) ?? {};
|
|
6210
6279
|
const servers = settings.mcpServers ?? {};
|
|
6211
6280
|
let anythingChanged = false;
|
|
@@ -6287,8 +6356,8 @@ async function setupGemini() {
|
|
|
6287
6356
|
}
|
|
6288
6357
|
}
|
|
6289
6358
|
async function setupCursor() {
|
|
6290
|
-
const homeDir2 =
|
|
6291
|
-
const mcpPath =
|
|
6359
|
+
const homeDir2 = import_os11.default.homedir();
|
|
6360
|
+
const mcpPath = import_path14.default.join(homeDir2, ".cursor", "mcp.json");
|
|
6292
6361
|
const mcpConfig = readJson(mcpPath) ?? {};
|
|
6293
6362
|
const servers = mcpConfig.mcpServers ?? {};
|
|
6294
6363
|
let anythingChanged = false;
|
|
@@ -6344,10 +6413,10 @@ async function setupCursor() {
|
|
|
6344
6413
|
|
|
6345
6414
|
// src/cli.ts
|
|
6346
6415
|
init_daemon2();
|
|
6347
|
-
var
|
|
6348
|
-
var
|
|
6349
|
-
var
|
|
6350
|
-
var
|
|
6416
|
+
var import_chalk16 = __toESM(require("chalk"));
|
|
6417
|
+
var import_fs22 = __toESM(require("fs"));
|
|
6418
|
+
var import_path24 = __toESM(require("path"));
|
|
6419
|
+
var import_os20 = __toESM(require("os"));
|
|
6351
6420
|
var import_prompts3 = require("@inquirer/prompts");
|
|
6352
6421
|
|
|
6353
6422
|
// src/utils/duration.ts
|
|
@@ -6568,9 +6637,9 @@ async function autoStartDaemonAndWait() {
|
|
|
6568
6637
|
|
|
6569
6638
|
// src/cli/commands/check.ts
|
|
6570
6639
|
var import_chalk5 = __toESM(require("chalk"));
|
|
6571
|
-
var
|
|
6572
|
-
var
|
|
6573
|
-
var
|
|
6640
|
+
var import_fs16 = __toESM(require("fs"));
|
|
6641
|
+
var import_path18 = __toESM(require("path"));
|
|
6642
|
+
var import_os14 = __toESM(require("os"));
|
|
6574
6643
|
init_orchestrator();
|
|
6575
6644
|
init_daemon();
|
|
6576
6645
|
init_config();
|
|
@@ -6579,25 +6648,25 @@ init_policy();
|
|
|
6579
6648
|
// src/undo.ts
|
|
6580
6649
|
var import_child_process8 = require("child_process");
|
|
6581
6650
|
var import_crypto5 = __toESM(require("crypto"));
|
|
6582
|
-
var
|
|
6583
|
-
var
|
|
6584
|
-
var
|
|
6585
|
-
var SNAPSHOT_STACK_PATH =
|
|
6586
|
-
var UNDO_LATEST_PATH =
|
|
6651
|
+
var import_fs15 = __toESM(require("fs"));
|
|
6652
|
+
var import_path17 = __toESM(require("path"));
|
|
6653
|
+
var import_os13 = __toESM(require("os"));
|
|
6654
|
+
var SNAPSHOT_STACK_PATH = import_path17.default.join(import_os13.default.homedir(), ".node9", "snapshots.json");
|
|
6655
|
+
var UNDO_LATEST_PATH = import_path17.default.join(import_os13.default.homedir(), ".node9", "undo_latest.txt");
|
|
6587
6656
|
var MAX_SNAPSHOTS = 10;
|
|
6588
6657
|
var GIT_TIMEOUT = 15e3;
|
|
6589
6658
|
function readStack() {
|
|
6590
6659
|
try {
|
|
6591
|
-
if (
|
|
6592
|
-
return JSON.parse(
|
|
6660
|
+
if (import_fs15.default.existsSync(SNAPSHOT_STACK_PATH))
|
|
6661
|
+
return JSON.parse(import_fs15.default.readFileSync(SNAPSHOT_STACK_PATH, "utf-8"));
|
|
6593
6662
|
} catch {
|
|
6594
6663
|
}
|
|
6595
6664
|
return [];
|
|
6596
6665
|
}
|
|
6597
6666
|
function writeStack(stack) {
|
|
6598
|
-
const dir =
|
|
6599
|
-
if (!
|
|
6600
|
-
|
|
6667
|
+
const dir = import_path17.default.dirname(SNAPSHOT_STACK_PATH);
|
|
6668
|
+
if (!import_fs15.default.existsSync(dir)) import_fs15.default.mkdirSync(dir, { recursive: true });
|
|
6669
|
+
import_fs15.default.writeFileSync(SNAPSHOT_STACK_PATH, JSON.stringify(stack, null, 2));
|
|
6601
6670
|
}
|
|
6602
6671
|
function buildArgsSummary(tool, args) {
|
|
6603
6672
|
if (!args || typeof args !== "object") return "";
|
|
@@ -6613,7 +6682,7 @@ function buildArgsSummary(tool, args) {
|
|
|
6613
6682
|
function normalizeCwdForHash(cwd) {
|
|
6614
6683
|
let normalized;
|
|
6615
6684
|
try {
|
|
6616
|
-
normalized =
|
|
6685
|
+
normalized = import_fs15.default.realpathSync(cwd);
|
|
6617
6686
|
} catch {
|
|
6618
6687
|
normalized = cwd;
|
|
6619
6688
|
}
|
|
@@ -6623,16 +6692,16 @@ function normalizeCwdForHash(cwd) {
|
|
|
6623
6692
|
}
|
|
6624
6693
|
function getShadowRepoDir(cwd) {
|
|
6625
6694
|
const hash = import_crypto5.default.createHash("sha256").update(normalizeCwdForHash(cwd)).digest("hex").slice(0, 16);
|
|
6626
|
-
return
|
|
6695
|
+
return import_path17.default.join(import_os13.default.homedir(), ".node9", "snapshots", hash);
|
|
6627
6696
|
}
|
|
6628
6697
|
function cleanOrphanedIndexFiles(shadowDir) {
|
|
6629
6698
|
try {
|
|
6630
6699
|
const cutoff = Date.now() - 6e4;
|
|
6631
|
-
for (const f of
|
|
6700
|
+
for (const f of import_fs15.default.readdirSync(shadowDir)) {
|
|
6632
6701
|
if (f.startsWith("index_")) {
|
|
6633
|
-
const fp =
|
|
6702
|
+
const fp = import_path17.default.join(shadowDir, f);
|
|
6634
6703
|
try {
|
|
6635
|
-
if (
|
|
6704
|
+
if (import_fs15.default.statSync(fp).mtimeMs < cutoff) import_fs15.default.unlinkSync(fp);
|
|
6636
6705
|
} catch {
|
|
6637
6706
|
}
|
|
6638
6707
|
}
|
|
@@ -6644,7 +6713,7 @@ function writeShadowExcludes(shadowDir, ignorePaths) {
|
|
|
6644
6713
|
const hardcoded = [".git", ".node9"];
|
|
6645
6714
|
const lines = [...hardcoded, ...ignorePaths].join("\n");
|
|
6646
6715
|
try {
|
|
6647
|
-
|
|
6716
|
+
import_fs15.default.writeFileSync(import_path17.default.join(shadowDir, "info", "exclude"), lines + "\n", "utf8");
|
|
6648
6717
|
} catch {
|
|
6649
6718
|
}
|
|
6650
6719
|
}
|
|
@@ -6657,25 +6726,25 @@ function ensureShadowRepo(shadowDir, cwd) {
|
|
|
6657
6726
|
timeout: 3e3
|
|
6658
6727
|
});
|
|
6659
6728
|
if (check.status === 0) {
|
|
6660
|
-
const ptPath =
|
|
6729
|
+
const ptPath = import_path17.default.join(shadowDir, "project-path.txt");
|
|
6661
6730
|
try {
|
|
6662
|
-
const stored =
|
|
6731
|
+
const stored = import_fs15.default.readFileSync(ptPath, "utf8").trim();
|
|
6663
6732
|
if (stored === normalizedCwd) return true;
|
|
6664
6733
|
if (process.env.NODE9_DEBUG === "1")
|
|
6665
6734
|
console.error(
|
|
6666
6735
|
`[Node9] Shadow repo path mismatch: stored="${stored}" expected="${normalizedCwd}" \u2014 reinitializing`
|
|
6667
6736
|
);
|
|
6668
|
-
|
|
6737
|
+
import_fs15.default.rmSync(shadowDir, { recursive: true, force: true });
|
|
6669
6738
|
} catch {
|
|
6670
6739
|
try {
|
|
6671
|
-
|
|
6740
|
+
import_fs15.default.writeFileSync(ptPath, normalizedCwd, "utf8");
|
|
6672
6741
|
} catch {
|
|
6673
6742
|
}
|
|
6674
6743
|
return true;
|
|
6675
6744
|
}
|
|
6676
6745
|
}
|
|
6677
6746
|
try {
|
|
6678
|
-
|
|
6747
|
+
import_fs15.default.mkdirSync(shadowDir, { recursive: true });
|
|
6679
6748
|
} catch {
|
|
6680
6749
|
}
|
|
6681
6750
|
const init = (0, import_child_process8.spawnSync)("git", ["init", "--bare", shadowDir], { timeout: 5e3 });
|
|
@@ -6684,7 +6753,7 @@ function ensureShadowRepo(shadowDir, cwd) {
|
|
|
6684
6753
|
console.error("[Node9] git init --bare failed:", init.stderr?.toString());
|
|
6685
6754
|
return false;
|
|
6686
6755
|
}
|
|
6687
|
-
const configFile =
|
|
6756
|
+
const configFile = import_path17.default.join(shadowDir, "config");
|
|
6688
6757
|
(0, import_child_process8.spawnSync)("git", ["config", "--file", configFile, "core.untrackedCache", "true"], {
|
|
6689
6758
|
timeout: 3e3
|
|
6690
6759
|
});
|
|
@@ -6692,7 +6761,7 @@ function ensureShadowRepo(shadowDir, cwd) {
|
|
|
6692
6761
|
timeout: 3e3
|
|
6693
6762
|
});
|
|
6694
6763
|
try {
|
|
6695
|
-
|
|
6764
|
+
import_fs15.default.writeFileSync(import_path17.default.join(shadowDir, "project-path.txt"), normalizedCwd, "utf8");
|
|
6696
6765
|
} catch {
|
|
6697
6766
|
}
|
|
6698
6767
|
return true;
|
|
@@ -6715,7 +6784,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
|
|
|
6715
6784
|
const shadowDir = getShadowRepoDir(cwd);
|
|
6716
6785
|
if (!ensureShadowRepo(shadowDir, cwd)) return null;
|
|
6717
6786
|
writeShadowExcludes(shadowDir, ignorePaths);
|
|
6718
|
-
indexFile =
|
|
6787
|
+
indexFile = import_path17.default.join(shadowDir, `index_${process.pid}_${Date.now()}`);
|
|
6719
6788
|
const shadowEnv = {
|
|
6720
6789
|
...process.env,
|
|
6721
6790
|
GIT_DIR: shadowDir,
|
|
@@ -6744,7 +6813,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
|
|
|
6744
6813
|
const shouldGc = stack.length % 5 === 0;
|
|
6745
6814
|
if (stack.length > MAX_SNAPSHOTS) stack.splice(0, stack.length - MAX_SNAPSHOTS);
|
|
6746
6815
|
writeStack(stack);
|
|
6747
|
-
|
|
6816
|
+
import_fs15.default.writeFileSync(UNDO_LATEST_PATH, commitHash);
|
|
6748
6817
|
if (shouldGc) {
|
|
6749
6818
|
(0, import_child_process8.spawn)("git", ["gc", "--auto"], { env: shadowEnv, detached: true, stdio: "ignore" }).unref();
|
|
6750
6819
|
}
|
|
@@ -6755,7 +6824,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
|
|
|
6755
6824
|
} finally {
|
|
6756
6825
|
if (indexFile) {
|
|
6757
6826
|
try {
|
|
6758
|
-
|
|
6827
|
+
import_fs15.default.unlinkSync(indexFile);
|
|
6759
6828
|
} catch {
|
|
6760
6829
|
}
|
|
6761
6830
|
}
|
|
@@ -6824,9 +6893,9 @@ function applyUndo(hash, cwd) {
|
|
|
6824
6893
|
timeout: GIT_TIMEOUT
|
|
6825
6894
|
}).stdout?.toString().trim().split("\n").filter(Boolean) ?? [];
|
|
6826
6895
|
for (const file of [...tracked, ...untracked]) {
|
|
6827
|
-
const fullPath =
|
|
6828
|
-
if (!snapshotFiles.has(file) &&
|
|
6829
|
-
|
|
6896
|
+
const fullPath = import_path17.default.join(dir, file);
|
|
6897
|
+
if (!snapshotFiles.has(file) && import_fs15.default.existsSync(fullPath)) {
|
|
6898
|
+
import_fs15.default.unlinkSync(fullPath);
|
|
6830
6899
|
}
|
|
6831
6900
|
}
|
|
6832
6901
|
return true;
|
|
@@ -6850,9 +6919,9 @@ function registerCheckCommand(program2) {
|
|
|
6850
6919
|
} catch (err) {
|
|
6851
6920
|
const tempConfig = getConfig();
|
|
6852
6921
|
if (process.env.NODE9_DEBUG === "1" || tempConfig.settings.enableHookLogDebug) {
|
|
6853
|
-
const logPath =
|
|
6922
|
+
const logPath = import_path18.default.join(import_os14.default.homedir(), ".node9", "hook-debug.log");
|
|
6854
6923
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
6855
|
-
|
|
6924
|
+
import_fs16.default.appendFileSync(
|
|
6856
6925
|
logPath,
|
|
6857
6926
|
`[${(/* @__PURE__ */ new Date()).toISOString()}] JSON_PARSE_ERROR: ${errMsg}
|
|
6858
6927
|
RAW: ${raw}
|
|
@@ -6863,10 +6932,10 @@ RAW: ${raw}
|
|
|
6863
6932
|
}
|
|
6864
6933
|
const config = getConfig(payload.cwd || void 0);
|
|
6865
6934
|
if (process.env.NODE9_DEBUG === "1" || config.settings.enableHookLogDebug) {
|
|
6866
|
-
const logPath =
|
|
6867
|
-
if (!
|
|
6868
|
-
|
|
6869
|
-
|
|
6935
|
+
const logPath = import_path18.default.join(import_os14.default.homedir(), ".node9", "hook-debug.log");
|
|
6936
|
+
if (!import_fs16.default.existsSync(import_path18.default.dirname(logPath)))
|
|
6937
|
+
import_fs16.default.mkdirSync(import_path18.default.dirname(logPath), { recursive: true });
|
|
6938
|
+
import_fs16.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${raw}
|
|
6870
6939
|
`);
|
|
6871
6940
|
}
|
|
6872
6941
|
const toolName = sanitize2(payload.tool_name ?? payload.name ?? "");
|
|
@@ -6879,8 +6948,8 @@ RAW: ${raw}
|
|
|
6879
6948
|
const isHumanDecision = blockedByContext.toLowerCase().includes("user") || blockedByContext.toLowerCase().includes("daemon") || blockedByContext.toLowerCase().includes("decision");
|
|
6880
6949
|
let ttyFd = null;
|
|
6881
6950
|
try {
|
|
6882
|
-
ttyFd =
|
|
6883
|
-
const writeTty = (line) =>
|
|
6951
|
+
ttyFd = import_fs16.default.openSync("/dev/tty", "w");
|
|
6952
|
+
const writeTty = (line) => import_fs16.default.writeSync(ttyFd, line + "\n");
|
|
6884
6953
|
if (blockedByContext.includes("DLP") || blockedByContext.includes("Secret Detected") || blockedByContext.includes("Credential Review")) {
|
|
6885
6954
|
writeTty(import_chalk5.default.bgRed.white.bold(`
|
|
6886
6955
|
\u{1F6A8} NODE9 DLP ALERT \u2014 CREDENTIAL DETECTED `));
|
|
@@ -6896,7 +6965,7 @@ RAW: ${raw}
|
|
|
6896
6965
|
} finally {
|
|
6897
6966
|
if (ttyFd !== null)
|
|
6898
6967
|
try {
|
|
6899
|
-
|
|
6968
|
+
import_fs16.default.closeSync(ttyFd);
|
|
6900
6969
|
} catch {
|
|
6901
6970
|
}
|
|
6902
6971
|
}
|
|
@@ -6927,7 +6996,7 @@ RAW: ${raw}
|
|
|
6927
6996
|
if (shouldSnapshot(toolName, toolInput, config)) {
|
|
6928
6997
|
await createShadowSnapshot(toolName, toolInput, config.policy.snapshot.ignorePaths);
|
|
6929
6998
|
}
|
|
6930
|
-
const safeCwdForAuth = typeof payload.cwd === "string" &&
|
|
6999
|
+
const safeCwdForAuth = typeof payload.cwd === "string" && import_path18.default.isAbsolute(payload.cwd) ? payload.cwd : void 0;
|
|
6931
7000
|
const result = await authorizeHeadless(toolName, toolInput, meta, {
|
|
6932
7001
|
cwd: safeCwdForAuth
|
|
6933
7002
|
});
|
|
@@ -6939,12 +7008,12 @@ RAW: ${raw}
|
|
|
6939
7008
|
}
|
|
6940
7009
|
if (result.noApprovalMechanism && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON && !process.stdout.isTTY && config.settings.autoStartDaemon) {
|
|
6941
7010
|
try {
|
|
6942
|
-
const tty =
|
|
6943
|
-
|
|
7011
|
+
const tty = import_fs16.default.openSync("/dev/tty", "w");
|
|
7012
|
+
import_fs16.default.writeSync(
|
|
6944
7013
|
tty,
|
|
6945
7014
|
import_chalk5.default.cyan("\n\u{1F6E1}\uFE0F Node9: Starting approval daemon automatically...\n")
|
|
6946
7015
|
);
|
|
6947
|
-
|
|
7016
|
+
import_fs16.default.closeSync(tty);
|
|
6948
7017
|
} catch {
|
|
6949
7018
|
}
|
|
6950
7019
|
const daemonReady = await autoStartDaemonAndWait();
|
|
@@ -6971,9 +7040,9 @@ RAW: ${raw}
|
|
|
6971
7040
|
});
|
|
6972
7041
|
} catch (err) {
|
|
6973
7042
|
if (process.env.NODE9_DEBUG === "1") {
|
|
6974
|
-
const logPath =
|
|
7043
|
+
const logPath = import_path18.default.join(import_os14.default.homedir(), ".node9", "hook-debug.log");
|
|
6975
7044
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
6976
|
-
|
|
7045
|
+
import_fs16.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] ERROR: ${errMsg}
|
|
6977
7046
|
`);
|
|
6978
7047
|
}
|
|
6979
7048
|
process.exit(0);
|
|
@@ -7007,9 +7076,9 @@ RAW: ${raw}
|
|
|
7007
7076
|
}
|
|
7008
7077
|
|
|
7009
7078
|
// src/cli/commands/log.ts
|
|
7010
|
-
var
|
|
7011
|
-
var
|
|
7012
|
-
var
|
|
7079
|
+
var import_fs17 = __toESM(require("fs"));
|
|
7080
|
+
var import_path19 = __toESM(require("path"));
|
|
7081
|
+
var import_os15 = __toESM(require("os"));
|
|
7013
7082
|
init_audit();
|
|
7014
7083
|
init_config();
|
|
7015
7084
|
init_policy();
|
|
@@ -7031,11 +7100,11 @@ function registerLogCommand(program2) {
|
|
|
7031
7100
|
decision: "allowed",
|
|
7032
7101
|
source: "post-hook"
|
|
7033
7102
|
};
|
|
7034
|
-
const logPath =
|
|
7035
|
-
if (!
|
|
7036
|
-
|
|
7037
|
-
|
|
7038
|
-
const safeCwd = typeof payload.cwd === "string" &&
|
|
7103
|
+
const logPath = import_path19.default.join(import_os15.default.homedir(), ".node9", "audit.log");
|
|
7104
|
+
if (!import_fs17.default.existsSync(import_path19.default.dirname(logPath)))
|
|
7105
|
+
import_fs17.default.mkdirSync(import_path19.default.dirname(logPath), { recursive: true });
|
|
7106
|
+
import_fs17.default.appendFileSync(logPath, JSON.stringify(entry) + "\n");
|
|
7107
|
+
const safeCwd = typeof payload.cwd === "string" && import_path19.default.isAbsolute(payload.cwd) ? payload.cwd : void 0;
|
|
7039
7108
|
const config = getConfig(safeCwd);
|
|
7040
7109
|
if (shouldSnapshot(tool, {}, config)) {
|
|
7041
7110
|
await createShadowSnapshot("unknown", {}, config.policy.snapshot.ignorePaths);
|
|
@@ -7044,9 +7113,9 @@ function registerLogCommand(program2) {
|
|
|
7044
7113
|
const msg = err instanceof Error ? err.message : String(err);
|
|
7045
7114
|
process.stderr.write(`[Node9] audit log error: ${msg}
|
|
7046
7115
|
`);
|
|
7047
|
-
const debugPath =
|
|
7116
|
+
const debugPath = import_path19.default.join(import_os15.default.homedir(), ".node9", "hook-debug.log");
|
|
7048
7117
|
try {
|
|
7049
|
-
|
|
7118
|
+
import_fs17.default.appendFileSync(debugPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] LOG_ERROR: ${msg}
|
|
7050
7119
|
`);
|
|
7051
7120
|
} catch {
|
|
7052
7121
|
}
|
|
@@ -7350,14 +7419,14 @@ function registerConfigShowCommand(program2) {
|
|
|
7350
7419
|
|
|
7351
7420
|
// src/cli/commands/doctor.ts
|
|
7352
7421
|
var import_chalk7 = __toESM(require("chalk"));
|
|
7353
|
-
var
|
|
7354
|
-
var
|
|
7355
|
-
var
|
|
7422
|
+
var import_fs18 = __toESM(require("fs"));
|
|
7423
|
+
var import_path20 = __toESM(require("path"));
|
|
7424
|
+
var import_os16 = __toESM(require("os"));
|
|
7356
7425
|
var import_child_process9 = require("child_process");
|
|
7357
7426
|
init_daemon();
|
|
7358
7427
|
function registerDoctorCommand(program2, version2) {
|
|
7359
7428
|
program2.command("doctor").description("Check that Node9 is installed and configured correctly").action(() => {
|
|
7360
|
-
const homeDir2 =
|
|
7429
|
+
const homeDir2 = import_os16.default.homedir();
|
|
7361
7430
|
let failures = 0;
|
|
7362
7431
|
function pass(msg) {
|
|
7363
7432
|
console.log(import_chalk7.default.green(" \u2705 ") + msg);
|
|
@@ -7406,10 +7475,10 @@ function registerDoctorCommand(program2, version2) {
|
|
|
7406
7475
|
);
|
|
7407
7476
|
}
|
|
7408
7477
|
section("Configuration");
|
|
7409
|
-
const globalConfigPath =
|
|
7410
|
-
if (
|
|
7478
|
+
const globalConfigPath = import_path20.default.join(homeDir2, ".node9", "config.json");
|
|
7479
|
+
if (import_fs18.default.existsSync(globalConfigPath)) {
|
|
7411
7480
|
try {
|
|
7412
|
-
JSON.parse(
|
|
7481
|
+
JSON.parse(import_fs18.default.readFileSync(globalConfigPath, "utf-8"));
|
|
7413
7482
|
pass("~/.node9/config.json found and valid");
|
|
7414
7483
|
} catch {
|
|
7415
7484
|
fail("~/.node9/config.json is invalid JSON", "Run: node9 init --force");
|
|
@@ -7417,10 +7486,10 @@ function registerDoctorCommand(program2, version2) {
|
|
|
7417
7486
|
} else {
|
|
7418
7487
|
warn("~/.node9/config.json not found (using defaults)", "Run: node9 init");
|
|
7419
7488
|
}
|
|
7420
|
-
const projectConfigPath =
|
|
7421
|
-
if (
|
|
7489
|
+
const projectConfigPath = import_path20.default.join(process.cwd(), "node9.config.json");
|
|
7490
|
+
if (import_fs18.default.existsSync(projectConfigPath)) {
|
|
7422
7491
|
try {
|
|
7423
|
-
JSON.parse(
|
|
7492
|
+
JSON.parse(import_fs18.default.readFileSync(projectConfigPath, "utf-8"));
|
|
7424
7493
|
pass("node9.config.json found and valid (project)");
|
|
7425
7494
|
} catch {
|
|
7426
7495
|
fail(
|
|
@@ -7429,8 +7498,8 @@ function registerDoctorCommand(program2, version2) {
|
|
|
7429
7498
|
);
|
|
7430
7499
|
}
|
|
7431
7500
|
}
|
|
7432
|
-
const credsPath =
|
|
7433
|
-
if (
|
|
7501
|
+
const credsPath = import_path20.default.join(homeDir2, ".node9", "credentials.json");
|
|
7502
|
+
if (import_fs18.default.existsSync(credsPath)) {
|
|
7434
7503
|
pass("Cloud credentials found (~/.node9/credentials.json)");
|
|
7435
7504
|
} else {
|
|
7436
7505
|
warn(
|
|
@@ -7439,10 +7508,10 @@ function registerDoctorCommand(program2, version2) {
|
|
|
7439
7508
|
);
|
|
7440
7509
|
}
|
|
7441
7510
|
section("Agent Hooks");
|
|
7442
|
-
const claudeSettingsPath =
|
|
7443
|
-
if (
|
|
7511
|
+
const claudeSettingsPath = import_path20.default.join(homeDir2, ".claude", "settings.json");
|
|
7512
|
+
if (import_fs18.default.existsSync(claudeSettingsPath)) {
|
|
7444
7513
|
try {
|
|
7445
|
-
const cs = JSON.parse(
|
|
7514
|
+
const cs = JSON.parse(import_fs18.default.readFileSync(claudeSettingsPath, "utf-8"));
|
|
7446
7515
|
const hasHook = cs.hooks?.PreToolUse?.some(
|
|
7447
7516
|
(m) => m.hooks.some((h) => h.command?.includes("node9") || h.command?.includes("cli.js"))
|
|
7448
7517
|
);
|
|
@@ -7458,10 +7527,10 @@ function registerDoctorCommand(program2, version2) {
|
|
|
7458
7527
|
} else {
|
|
7459
7528
|
warn("Claude Code \u2014 not configured", "Run: node9 setup claude");
|
|
7460
7529
|
}
|
|
7461
|
-
const geminiSettingsPath =
|
|
7462
|
-
if (
|
|
7530
|
+
const geminiSettingsPath = import_path20.default.join(homeDir2, ".gemini", "settings.json");
|
|
7531
|
+
if (import_fs18.default.existsSync(geminiSettingsPath)) {
|
|
7463
7532
|
try {
|
|
7464
|
-
const gs = JSON.parse(
|
|
7533
|
+
const gs = JSON.parse(import_fs18.default.readFileSync(geminiSettingsPath, "utf-8"));
|
|
7465
7534
|
const hasHook = gs.hooks?.BeforeTool?.some(
|
|
7466
7535
|
(m) => m.hooks.some((h) => h.command?.includes("node9") || h.command?.includes("cli.js"))
|
|
7467
7536
|
);
|
|
@@ -7477,10 +7546,10 @@ function registerDoctorCommand(program2, version2) {
|
|
|
7477
7546
|
} else {
|
|
7478
7547
|
warn("Gemini CLI \u2014 not configured", "Run: node9 setup gemini (skip if not using Gemini)");
|
|
7479
7548
|
}
|
|
7480
|
-
const cursorHooksPath =
|
|
7481
|
-
if (
|
|
7549
|
+
const cursorHooksPath = import_path20.default.join(homeDir2, ".cursor", "hooks.json");
|
|
7550
|
+
if (import_fs18.default.existsSync(cursorHooksPath)) {
|
|
7482
7551
|
try {
|
|
7483
|
-
const cur = JSON.parse(
|
|
7552
|
+
const cur = JSON.parse(import_fs18.default.readFileSync(cursorHooksPath, "utf-8"));
|
|
7484
7553
|
const hasHook = cur.hooks?.preToolUse?.some(
|
|
7485
7554
|
(h) => h.command?.includes("node9") || h.command?.includes("cli.js")
|
|
7486
7555
|
);
|
|
@@ -7518,9 +7587,9 @@ function registerDoctorCommand(program2, version2) {
|
|
|
7518
7587
|
|
|
7519
7588
|
// src/cli/commands/audit.ts
|
|
7520
7589
|
var import_chalk8 = __toESM(require("chalk"));
|
|
7521
|
-
var
|
|
7522
|
-
var
|
|
7523
|
-
var
|
|
7590
|
+
var import_fs19 = __toESM(require("fs"));
|
|
7591
|
+
var import_path21 = __toESM(require("path"));
|
|
7592
|
+
var import_os17 = __toESM(require("os"));
|
|
7524
7593
|
function formatRelativeTime(timestamp) {
|
|
7525
7594
|
const diff = Date.now() - new Date(timestamp).getTime();
|
|
7526
7595
|
const sec = Math.floor(diff / 1e3);
|
|
@@ -7533,14 +7602,14 @@ function formatRelativeTime(timestamp) {
|
|
|
7533
7602
|
}
|
|
7534
7603
|
function registerAuditCommand(program2) {
|
|
7535
7604
|
program2.command("audit").description("View local execution audit log").option("--tail <n>", "Number of entries to show", "20").option("--tool <pattern>", "Filter by tool name (substring match)").option("--deny", "Show only denied actions").option("--json", "Output raw JSON").action((options) => {
|
|
7536
|
-
const logPath =
|
|
7537
|
-
if (!
|
|
7605
|
+
const logPath = import_path21.default.join(import_os17.default.homedir(), ".node9", "audit.log");
|
|
7606
|
+
if (!import_fs19.default.existsSync(logPath)) {
|
|
7538
7607
|
console.log(
|
|
7539
7608
|
import_chalk8.default.yellow("No audit logs found. Run node9 with an agent to generate entries.")
|
|
7540
7609
|
);
|
|
7541
7610
|
return;
|
|
7542
7611
|
}
|
|
7543
|
-
const raw =
|
|
7612
|
+
const raw = import_fs19.default.readFileSync(logPath, "utf-8");
|
|
7544
7613
|
const lines = raw.split("\n").filter((l) => l.trim() !== "");
|
|
7545
7614
|
let entries = lines.flatMap((line) => {
|
|
7546
7615
|
try {
|
|
@@ -7658,9 +7727,9 @@ function registerDaemonCommand(program2) {
|
|
|
7658
7727
|
|
|
7659
7728
|
// src/cli/commands/status.ts
|
|
7660
7729
|
var import_chalk10 = __toESM(require("chalk"));
|
|
7661
|
-
var
|
|
7662
|
-
var
|
|
7663
|
-
var
|
|
7730
|
+
var import_fs20 = __toESM(require("fs"));
|
|
7731
|
+
var import_path22 = __toESM(require("path"));
|
|
7732
|
+
var import_os18 = __toESM(require("os"));
|
|
7664
7733
|
init_core();
|
|
7665
7734
|
init_daemon();
|
|
7666
7735
|
function registerStatusCommand(program2) {
|
|
@@ -7697,13 +7766,13 @@ function registerStatusCommand(program2) {
|
|
|
7697
7766
|
console.log("");
|
|
7698
7767
|
const modeLabel = settings.mode === "audit" ? import_chalk10.default.blue("audit") : settings.mode === "strict" ? import_chalk10.default.red("strict") : import_chalk10.default.white("standard");
|
|
7699
7768
|
console.log(` Mode: ${modeLabel}`);
|
|
7700
|
-
const projectConfig =
|
|
7701
|
-
const globalConfig =
|
|
7769
|
+
const projectConfig = import_path22.default.join(process.cwd(), "node9.config.json");
|
|
7770
|
+
const globalConfig = import_path22.default.join(import_os18.default.homedir(), ".node9", "config.json");
|
|
7702
7771
|
console.log(
|
|
7703
|
-
` Local: ${
|
|
7772
|
+
` Local: ${import_fs20.default.existsSync(projectConfig) ? import_chalk10.default.green("Active (node9.config.json)") : import_chalk10.default.gray("Not present")}`
|
|
7704
7773
|
);
|
|
7705
7774
|
console.log(
|
|
7706
|
-
` Global: ${
|
|
7775
|
+
` Global: ${import_fs20.default.existsSync(globalConfig) ? import_chalk10.default.green("Active (~/.node9/config.json)") : import_chalk10.default.gray("Not present")}`
|
|
7707
7776
|
);
|
|
7708
7777
|
if (mergedConfig.policy.sandboxPaths.length > 0) {
|
|
7709
7778
|
console.log(
|
|
@@ -8087,22 +8156,77 @@ function registerMcpGatewayCommand(program2) {
|
|
|
8087
8156
|
});
|
|
8088
8157
|
}
|
|
8089
8158
|
|
|
8159
|
+
// src/cli/commands/trust.ts
|
|
8160
|
+
var import_chalk14 = __toESM(require("chalk"));
|
|
8161
|
+
init_trusted_hosts();
|
|
8162
|
+
function isValidHost(host) {
|
|
8163
|
+
return /^(\*\.)?[a-z0-9][a-z0-9.-]*\.[a-z]{2,}$/.test(host);
|
|
8164
|
+
}
|
|
8165
|
+
function registerTrustCommand(program2) {
|
|
8166
|
+
const trustCmd = program2.command("trust").description("Manage trusted network hosts (reduces approval friction for known destinations)");
|
|
8167
|
+
trustCmd.command("add <host>").description("Add a trusted host \u2014 pipe-chain blocks targeting this host are downgraded").action((host) => {
|
|
8168
|
+
const normalized = normalizeHost(host.trim());
|
|
8169
|
+
if (!isValidHost(normalized)) {
|
|
8170
|
+
console.error(
|
|
8171
|
+
import_chalk14.default.red(`
|
|
8172
|
+
\u274C Invalid host: "${host}"
|
|
8173
|
+
`) + import_chalk14.default.gray(" Use an FQDN like api.mycompany.com or *.mycompany.com\n")
|
|
8174
|
+
);
|
|
8175
|
+
process.exit(1);
|
|
8176
|
+
}
|
|
8177
|
+
addTrustedHost(normalized);
|
|
8178
|
+
console.log(import_chalk14.default.green(`
|
|
8179
|
+
\u2705 ${normalized} added to trusted hosts.`));
|
|
8180
|
+
console.log(
|
|
8181
|
+
import_chalk14.default.gray(" Pipe-chain blocks to this host: critical \u2192 review, high \u2192 allow\n")
|
|
8182
|
+
);
|
|
8183
|
+
});
|
|
8184
|
+
trustCmd.command("remove <host>").description("Remove a trusted host").action((host) => {
|
|
8185
|
+
const normalized = normalizeHost(host.trim());
|
|
8186
|
+
const removed = removeTrustedHost(normalized);
|
|
8187
|
+
if (!removed) {
|
|
8188
|
+
console.error(import_chalk14.default.yellow(`
|
|
8189
|
+
\u26A0\uFE0F "${normalized}" is not in the trusted hosts list.
|
|
8190
|
+
`));
|
|
8191
|
+
process.exit(1);
|
|
8192
|
+
}
|
|
8193
|
+
console.log(import_chalk14.default.green(`
|
|
8194
|
+
\u2705 ${normalized} removed from trusted hosts.
|
|
8195
|
+
`));
|
|
8196
|
+
});
|
|
8197
|
+
trustCmd.command("list").description("Show all trusted hosts").action(() => {
|
|
8198
|
+
const hosts = readTrustedHosts();
|
|
8199
|
+
if (hosts.length === 0) {
|
|
8200
|
+
console.log(import_chalk14.default.gray("\n No trusted hosts configured.\n"));
|
|
8201
|
+
console.log(` Add one: ${import_chalk14.default.cyan("node9 trust add api.mycompany.com")}
|
|
8202
|
+
`);
|
|
8203
|
+
return;
|
|
8204
|
+
}
|
|
8205
|
+
console.log(import_chalk14.default.bold("\n\u{1F513} Trusted Hosts\n"));
|
|
8206
|
+
for (const entry of hosts) {
|
|
8207
|
+
const date = new Date(entry.addedAt).toLocaleDateString();
|
|
8208
|
+
console.log(` ${import_chalk14.default.cyan(entry.host.padEnd(40))} ${import_chalk14.default.gray(`added ${date}`)}`);
|
|
8209
|
+
}
|
|
8210
|
+
console.log("");
|
|
8211
|
+
});
|
|
8212
|
+
}
|
|
8213
|
+
|
|
8090
8214
|
// src/cli.ts
|
|
8091
8215
|
var { version } = JSON.parse(
|
|
8092
|
-
|
|
8216
|
+
import_fs22.default.readFileSync(import_path24.default.join(__dirname, "../package.json"), "utf-8")
|
|
8093
8217
|
);
|
|
8094
8218
|
var program = new import_commander.Command();
|
|
8095
8219
|
program.name("node9").description("The Sudo Command for AI Agents").version(version);
|
|
8096
8220
|
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) => {
|
|
8097
8221
|
const DEFAULT_API_URL = "https://api.node9.ai/api/v1/intercept";
|
|
8098
|
-
const credPath =
|
|
8099
|
-
if (!
|
|
8100
|
-
|
|
8222
|
+
const credPath = import_path24.default.join(import_os20.default.homedir(), ".node9", "credentials.json");
|
|
8223
|
+
if (!import_fs22.default.existsSync(import_path24.default.dirname(credPath)))
|
|
8224
|
+
import_fs22.default.mkdirSync(import_path24.default.dirname(credPath), { recursive: true });
|
|
8101
8225
|
const profileName = options.profile || "default";
|
|
8102
8226
|
let existingCreds = {};
|
|
8103
8227
|
try {
|
|
8104
|
-
if (
|
|
8105
|
-
const raw = JSON.parse(
|
|
8228
|
+
if (import_fs22.default.existsSync(credPath)) {
|
|
8229
|
+
const raw = JSON.parse(import_fs22.default.readFileSync(credPath, "utf-8"));
|
|
8106
8230
|
if (raw.apiKey) {
|
|
8107
8231
|
existingCreds = {
|
|
8108
8232
|
default: { apiKey: raw.apiKey, apiUrl: raw.apiUrl || DEFAULT_API_URL }
|
|
@@ -8114,13 +8238,13 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
|
|
|
8114
8238
|
} catch {
|
|
8115
8239
|
}
|
|
8116
8240
|
existingCreds[profileName] = { apiKey, apiUrl: DEFAULT_API_URL };
|
|
8117
|
-
|
|
8241
|
+
import_fs22.default.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), { mode: 384 });
|
|
8118
8242
|
if (profileName === "default") {
|
|
8119
|
-
const configPath =
|
|
8243
|
+
const configPath = import_path24.default.join(import_os20.default.homedir(), ".node9", "config.json");
|
|
8120
8244
|
let config = {};
|
|
8121
8245
|
try {
|
|
8122
|
-
if (
|
|
8123
|
-
config = JSON.parse(
|
|
8246
|
+
if (import_fs22.default.existsSync(configPath))
|
|
8247
|
+
config = JSON.parse(import_fs22.default.readFileSync(configPath, "utf-8"));
|
|
8124
8248
|
} catch {
|
|
8125
8249
|
}
|
|
8126
8250
|
if (!config.settings || typeof config.settings !== "object") config.settings = {};
|
|
@@ -8135,36 +8259,36 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
|
|
|
8135
8259
|
approvers.cloud = false;
|
|
8136
8260
|
}
|
|
8137
8261
|
s.approvers = approvers;
|
|
8138
|
-
if (!
|
|
8139
|
-
|
|
8140
|
-
|
|
8262
|
+
if (!import_fs22.default.existsSync(import_path24.default.dirname(configPath)))
|
|
8263
|
+
import_fs22.default.mkdirSync(import_path24.default.dirname(configPath), { recursive: true });
|
|
8264
|
+
import_fs22.default.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
|
|
8141
8265
|
}
|
|
8142
8266
|
if (options.profile && profileName !== "default") {
|
|
8143
|
-
console.log(
|
|
8144
|
-
console.log(
|
|
8267
|
+
console.log(import_chalk16.default.green(`\u2705 Profile "${profileName}" saved`));
|
|
8268
|
+
console.log(import_chalk16.default.gray(` Switch to it per-session: NODE9_PROFILE=${profileName} claude`));
|
|
8145
8269
|
} else if (options.local) {
|
|
8146
|
-
console.log(
|
|
8147
|
-
console.log(
|
|
8270
|
+
console.log(import_chalk16.default.green(`\u2705 Privacy mode \u{1F6E1}\uFE0F`));
|
|
8271
|
+
console.log(import_chalk16.default.gray(` All decisions stay on this machine.`));
|
|
8148
8272
|
} else {
|
|
8149
|
-
console.log(
|
|
8150
|
-
console.log(
|
|
8273
|
+
console.log(import_chalk16.default.green(`\u2705 Logged in \u2014 agent mode`));
|
|
8274
|
+
console.log(import_chalk16.default.gray(` Team policy enforced for all calls via Node9 cloud.`));
|
|
8151
8275
|
}
|
|
8152
8276
|
});
|
|
8153
8277
|
program.command("addto").description("Integrate Node9 with an AI agent").addHelpText("after", "\n Supported targets: claude gemini cursor").argument("<target>", "The agent to protect: claude | gemini | cursor").action(async (target) => {
|
|
8154
8278
|
if (target === "gemini") return await setupGemini();
|
|
8155
8279
|
if (target === "claude") return await setupClaude();
|
|
8156
8280
|
if (target === "cursor") return await setupCursor();
|
|
8157
|
-
console.error(
|
|
8281
|
+
console.error(import_chalk16.default.red(`Unknown target: "${target}". Supported: claude, gemini, cursor`));
|
|
8158
8282
|
process.exit(1);
|
|
8159
8283
|
});
|
|
8160
8284
|
program.command("setup").description('Alias for "addto" \u2014 integrate Node9 with an AI agent').addHelpText("after", "\n Supported targets: claude gemini cursor").argument("[target]", "The agent to protect: claude | gemini | cursor").action(async (target) => {
|
|
8161
8285
|
if (!target) {
|
|
8162
|
-
console.log(
|
|
8163
|
-
console.log(" Usage: " +
|
|
8286
|
+
console.log(import_chalk16.default.cyan("\n\u{1F6E1}\uFE0F Node9 Setup \u2014 integrate with your AI agent\n"));
|
|
8287
|
+
console.log(" Usage: " + import_chalk16.default.white("node9 setup <target>") + "\n");
|
|
8164
8288
|
console.log(" Targets:");
|
|
8165
|
-
console.log(" " +
|
|
8166
|
-
console.log(" " +
|
|
8167
|
-
console.log(" " +
|
|
8289
|
+
console.log(" " + import_chalk16.default.green("claude") + " \u2014 Claude Code (hook mode)");
|
|
8290
|
+
console.log(" " + import_chalk16.default.green("gemini") + " \u2014 Gemini CLI (hook mode)");
|
|
8291
|
+
console.log(" " + import_chalk16.default.green("cursor") + " \u2014 Cursor (hook mode)");
|
|
8168
8292
|
console.log("");
|
|
8169
8293
|
return;
|
|
8170
8294
|
}
|
|
@@ -8172,7 +8296,7 @@ program.command("setup").description('Alias for "addto" \u2014 integrate Node9 w
|
|
|
8172
8296
|
if (t === "gemini") return await setupGemini();
|
|
8173
8297
|
if (t === "claude") return await setupClaude();
|
|
8174
8298
|
if (t === "cursor") return await setupCursor();
|
|
8175
|
-
console.error(
|
|
8299
|
+
console.error(import_chalk16.default.red(`Unknown target: "${target}". Supported: claude, gemini, cursor`));
|
|
8176
8300
|
process.exit(1);
|
|
8177
8301
|
});
|
|
8178
8302
|
program.command("removefrom").description("Remove Node9 hooks from an AI agent configuration").addHelpText("after", "\n Supported targets: claude gemini cursor").argument("<target>", "The agent to remove from: claude | gemini | cursor").action((target) => {
|
|
@@ -8181,30 +8305,30 @@ program.command("removefrom").description("Remove Node9 hooks from an AI agent c
|
|
|
8181
8305
|
else if (target === "gemini") fn = teardownGemini;
|
|
8182
8306
|
else if (target === "cursor") fn = teardownCursor;
|
|
8183
8307
|
else {
|
|
8184
|
-
console.error(
|
|
8308
|
+
console.error(import_chalk16.default.red(`Unknown target: "${target}". Supported: claude, gemini, cursor`));
|
|
8185
8309
|
process.exit(1);
|
|
8186
8310
|
}
|
|
8187
|
-
console.log(
|
|
8311
|
+
console.log(import_chalk16.default.cyan(`
|
|
8188
8312
|
\u{1F6E1}\uFE0F Node9: removing hooks from ${target}...
|
|
8189
8313
|
`));
|
|
8190
8314
|
try {
|
|
8191
8315
|
fn();
|
|
8192
8316
|
} catch (err) {
|
|
8193
|
-
console.error(
|
|
8317
|
+
console.error(import_chalk16.default.red(` \u26A0\uFE0F Failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
8194
8318
|
process.exit(1);
|
|
8195
8319
|
}
|
|
8196
|
-
console.log(
|
|
8320
|
+
console.log(import_chalk16.default.gray("\n Restart the agent for changes to take effect."));
|
|
8197
8321
|
});
|
|
8198
8322
|
program.command("uninstall").description("Remove all Node9 hooks and optionally delete config files").option("--purge", "Also delete ~/.node9/ directory (config, audit log, credentials)").action(async (options) => {
|
|
8199
|
-
console.log(
|
|
8200
|
-
console.log(
|
|
8323
|
+
console.log(import_chalk16.default.cyan("\n\u{1F6E1}\uFE0F Node9 Uninstall\n"));
|
|
8324
|
+
console.log(import_chalk16.default.bold("Stopping daemon..."));
|
|
8201
8325
|
try {
|
|
8202
8326
|
stopDaemon();
|
|
8203
|
-
console.log(
|
|
8327
|
+
console.log(import_chalk16.default.green(" \u2705 Daemon stopped"));
|
|
8204
8328
|
} catch {
|
|
8205
|
-
console.log(
|
|
8329
|
+
console.log(import_chalk16.default.blue(" \u2139\uFE0F Daemon was not running"));
|
|
8206
8330
|
}
|
|
8207
|
-
console.log(
|
|
8331
|
+
console.log(import_chalk16.default.bold("\nRemoving hooks..."));
|
|
8208
8332
|
let teardownFailed = false;
|
|
8209
8333
|
for (const [label, fn] of [
|
|
8210
8334
|
["Claude", teardownClaude],
|
|
@@ -8216,45 +8340,45 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
|
|
|
8216
8340
|
} catch (err) {
|
|
8217
8341
|
teardownFailed = true;
|
|
8218
8342
|
console.error(
|
|
8219
|
-
|
|
8343
|
+
import_chalk16.default.red(
|
|
8220
8344
|
` \u26A0\uFE0F Failed to remove ${label} hooks: ${err instanceof Error ? err.message : String(err)}`
|
|
8221
8345
|
)
|
|
8222
8346
|
);
|
|
8223
8347
|
}
|
|
8224
8348
|
}
|
|
8225
8349
|
if (options.purge) {
|
|
8226
|
-
const node9Dir =
|
|
8227
|
-
if (
|
|
8350
|
+
const node9Dir = import_path24.default.join(import_os20.default.homedir(), ".node9");
|
|
8351
|
+
if (import_fs22.default.existsSync(node9Dir)) {
|
|
8228
8352
|
const confirmed = await (0, import_prompts3.confirm)({
|
|
8229
8353
|
message: `Permanently delete ${node9Dir} (config, audit log, credentials)?`,
|
|
8230
8354
|
default: false
|
|
8231
8355
|
});
|
|
8232
8356
|
if (confirmed) {
|
|
8233
|
-
|
|
8234
|
-
if (
|
|
8357
|
+
import_fs22.default.rmSync(node9Dir, { recursive: true });
|
|
8358
|
+
if (import_fs22.default.existsSync(node9Dir)) {
|
|
8235
8359
|
console.error(
|
|
8236
|
-
|
|
8360
|
+
import_chalk16.default.red("\n \u26A0\uFE0F ~/.node9/ could not be fully deleted \u2014 remove it manually.")
|
|
8237
8361
|
);
|
|
8238
8362
|
} else {
|
|
8239
|
-
console.log(
|
|
8363
|
+
console.log(import_chalk16.default.green("\n \u2705 Deleted ~/.node9/ (config, audit log, credentials)"));
|
|
8240
8364
|
}
|
|
8241
8365
|
} else {
|
|
8242
|
-
console.log(
|
|
8366
|
+
console.log(import_chalk16.default.yellow("\n Skipped \u2014 ~/.node9/ was not deleted."));
|
|
8243
8367
|
}
|
|
8244
8368
|
} else {
|
|
8245
|
-
console.log(
|
|
8369
|
+
console.log(import_chalk16.default.blue("\n \u2139\uFE0F ~/.node9/ not found \u2014 nothing to delete"));
|
|
8246
8370
|
}
|
|
8247
8371
|
} else {
|
|
8248
8372
|
console.log(
|
|
8249
|
-
|
|
8373
|
+
import_chalk16.default.gray("\n ~/.node9/ kept \u2014 run with --purge to delete config and audit log")
|
|
8250
8374
|
);
|
|
8251
8375
|
}
|
|
8252
8376
|
if (teardownFailed) {
|
|
8253
|
-
console.error(
|
|
8377
|
+
console.error(import_chalk16.default.red("\n \u26A0\uFE0F Some hooks could not be removed \u2014 see errors above."));
|
|
8254
8378
|
process.exit(1);
|
|
8255
8379
|
}
|
|
8256
|
-
console.log(
|
|
8257
|
-
console.log(
|
|
8380
|
+
console.log(import_chalk16.default.green.bold("\n\u{1F6E1}\uFE0F Node9 removed. Run: npm uninstall -g @node9/proxy"));
|
|
8381
|
+
console.log(import_chalk16.default.gray(" Restart any open AI agent sessions for changes to take effect.\n"));
|
|
8258
8382
|
});
|
|
8259
8383
|
registerDoctorCommand(program, version);
|
|
8260
8384
|
program.command("explain").description(
|
|
@@ -8267,7 +8391,7 @@ program.command("explain").description(
|
|
|
8267
8391
|
try {
|
|
8268
8392
|
args = JSON.parse(trimmed);
|
|
8269
8393
|
} catch {
|
|
8270
|
-
console.error(
|
|
8394
|
+
console.error(import_chalk16.default.red(`
|
|
8271
8395
|
\u274C Invalid JSON: ${trimmed}
|
|
8272
8396
|
`));
|
|
8273
8397
|
process.exit(1);
|
|
@@ -8278,63 +8402,63 @@ program.command("explain").description(
|
|
|
8278
8402
|
}
|
|
8279
8403
|
const result = await explainPolicy(tool, args);
|
|
8280
8404
|
console.log("");
|
|
8281
|
-
console.log(
|
|
8405
|
+
console.log(import_chalk16.default.cyan.bold("\u{1F6E1}\uFE0F Node9 Explain"));
|
|
8282
8406
|
console.log("");
|
|
8283
|
-
console.log(` ${
|
|
8407
|
+
console.log(` ${import_chalk16.default.bold("Tool:")} ${import_chalk16.default.white(result.tool)}`);
|
|
8284
8408
|
if (argsRaw) {
|
|
8285
8409
|
const preview = argsRaw.length > 80 ? argsRaw.slice(0, 77) + "\u2026" : argsRaw;
|
|
8286
|
-
console.log(` ${
|
|
8410
|
+
console.log(` ${import_chalk16.default.bold("Input:")} ${import_chalk16.default.gray(preview)}`);
|
|
8287
8411
|
}
|
|
8288
8412
|
console.log("");
|
|
8289
|
-
console.log(
|
|
8413
|
+
console.log(import_chalk16.default.bold("Config Sources (Waterfall):"));
|
|
8290
8414
|
for (const tier of result.waterfall) {
|
|
8291
|
-
const num =
|
|
8415
|
+
const num = import_chalk16.default.gray(` ${tier.tier}.`);
|
|
8292
8416
|
const label = tier.label.padEnd(16);
|
|
8293
8417
|
let statusStr;
|
|
8294
8418
|
if (tier.tier === 1) {
|
|
8295
|
-
statusStr =
|
|
8419
|
+
statusStr = import_chalk16.default.gray(tier.note ?? "");
|
|
8296
8420
|
} else if (tier.status === "active") {
|
|
8297
|
-
const loc = tier.path ?
|
|
8298
|
-
const note = tier.note ?
|
|
8299
|
-
statusStr =
|
|
8421
|
+
const loc = tier.path ? import_chalk16.default.gray(tier.path) : "";
|
|
8422
|
+
const note = tier.note ? import_chalk16.default.gray(`(${tier.note})`) : "";
|
|
8423
|
+
statusStr = import_chalk16.default.green("\u2713 active") + (loc ? " " + loc : "") + (note ? " " + note : "");
|
|
8300
8424
|
} else {
|
|
8301
|
-
statusStr =
|
|
8425
|
+
statusStr = import_chalk16.default.gray("\u25CB " + (tier.note ?? "not found"));
|
|
8302
8426
|
}
|
|
8303
|
-
console.log(`${num} ${
|
|
8427
|
+
console.log(`${num} ${import_chalk16.default.white(label)} ${statusStr}`);
|
|
8304
8428
|
}
|
|
8305
8429
|
console.log("");
|
|
8306
|
-
console.log(
|
|
8430
|
+
console.log(import_chalk16.default.bold("Policy Evaluation:"));
|
|
8307
8431
|
for (const step of result.steps) {
|
|
8308
8432
|
const isFinal = step.isFinal;
|
|
8309
8433
|
let icon;
|
|
8310
|
-
if (step.outcome === "allow") icon =
|
|
8311
|
-
else if (step.outcome === "review") icon =
|
|
8312
|
-
else if (step.outcome === "skip") icon =
|
|
8313
|
-
else icon =
|
|
8434
|
+
if (step.outcome === "allow") icon = import_chalk16.default.green(" \u2705");
|
|
8435
|
+
else if (step.outcome === "review") icon = import_chalk16.default.red(" \u{1F534}");
|
|
8436
|
+
else if (step.outcome === "skip") icon = import_chalk16.default.gray(" \u2500 ");
|
|
8437
|
+
else icon = import_chalk16.default.gray(" \u25CB ");
|
|
8314
8438
|
const name = step.name.padEnd(18);
|
|
8315
|
-
const nameStr = isFinal ?
|
|
8316
|
-
const detail = isFinal ?
|
|
8317
|
-
const arrow = isFinal ?
|
|
8439
|
+
const nameStr = isFinal ? import_chalk16.default.white.bold(name) : import_chalk16.default.white(name);
|
|
8440
|
+
const detail = isFinal ? import_chalk16.default.white(step.detail) : import_chalk16.default.gray(step.detail);
|
|
8441
|
+
const arrow = isFinal ? import_chalk16.default.yellow(" \u2190 STOP") : "";
|
|
8318
8442
|
console.log(`${icon} ${nameStr} ${detail}${arrow}`);
|
|
8319
8443
|
}
|
|
8320
8444
|
console.log("");
|
|
8321
8445
|
if (result.decision === "allow") {
|
|
8322
|
-
console.log(
|
|
8446
|
+
console.log(import_chalk16.default.green.bold(" Decision: \u2705 ALLOW") + import_chalk16.default.gray(" \u2014 no approval needed"));
|
|
8323
8447
|
} else {
|
|
8324
8448
|
console.log(
|
|
8325
|
-
|
|
8449
|
+
import_chalk16.default.red.bold(" Decision: \u{1F534} REVIEW") + import_chalk16.default.gray(" \u2014 human approval required")
|
|
8326
8450
|
);
|
|
8327
8451
|
if (result.blockedByLabel) {
|
|
8328
|
-
console.log(
|
|
8452
|
+
console.log(import_chalk16.default.gray(` Reason: ${result.blockedByLabel}`));
|
|
8329
8453
|
}
|
|
8330
8454
|
}
|
|
8331
8455
|
console.log("");
|
|
8332
8456
|
});
|
|
8333
8457
|
program.command("init").description("Create ~/.node9/config.json with default policy (safe to run multiple times)").option("--force", "Overwrite existing config").option("-m, --mode <mode>", "Set initial security mode (standard, strict, audit)", "standard").action((options) => {
|
|
8334
|
-
const configPath =
|
|
8335
|
-
if (
|
|
8336
|
-
console.log(
|
|
8337
|
-
console.log(
|
|
8458
|
+
const configPath = import_path24.default.join(import_os20.default.homedir(), ".node9", "config.json");
|
|
8459
|
+
if (import_fs22.default.existsSync(configPath) && !options.force) {
|
|
8460
|
+
console.log(import_chalk16.default.yellow(`\u2139\uFE0F Global config already exists: ${configPath}`));
|
|
8461
|
+
console.log(import_chalk16.default.gray(` Run with --force to overwrite.`));
|
|
8338
8462
|
return;
|
|
8339
8463
|
}
|
|
8340
8464
|
const requestedMode = options.mode.toLowerCase();
|
|
@@ -8346,13 +8470,13 @@ program.command("init").description("Create ~/.node9/config.json with default po
|
|
|
8346
8470
|
mode: safeMode
|
|
8347
8471
|
}
|
|
8348
8472
|
};
|
|
8349
|
-
const dir =
|
|
8350
|
-
if (!
|
|
8351
|
-
|
|
8352
|
-
console.log(
|
|
8353
|
-
console.log(
|
|
8473
|
+
const dir = import_path24.default.dirname(configPath);
|
|
8474
|
+
if (!import_fs22.default.existsSync(dir)) import_fs22.default.mkdirSync(dir, { recursive: true });
|
|
8475
|
+
import_fs22.default.writeFileSync(configPath, JSON.stringify(configToSave, null, 2));
|
|
8476
|
+
console.log(import_chalk16.default.green(`\u2705 Global config created: ${configPath}`));
|
|
8477
|
+
console.log(import_chalk16.default.cyan(` Mode set to: ${safeMode}`));
|
|
8354
8478
|
console.log(
|
|
8355
|
-
|
|
8479
|
+
import_chalk16.default.gray(` Undo Engine is ENABLED by default. Use 'node9 undo' to revert AI changes.`)
|
|
8356
8480
|
);
|
|
8357
8481
|
});
|
|
8358
8482
|
registerAuditCommand(program);
|
|
@@ -8363,7 +8487,7 @@ program.command("tail").description("Stream live agent activity to the terminal"
|
|
|
8363
8487
|
try {
|
|
8364
8488
|
await startTail2(options);
|
|
8365
8489
|
} catch (err) {
|
|
8366
|
-
console.error(
|
|
8490
|
+
console.error(import_chalk16.default.red(`\u274C ${err instanceof Error ? err.message : String(err)}`));
|
|
8367
8491
|
process.exit(1);
|
|
8368
8492
|
}
|
|
8369
8493
|
});
|
|
@@ -8375,7 +8499,7 @@ program.command("pause").description("Temporarily disable Node9 protection for a
|
|
|
8375
8499
|
const ms = parseDuration(options.duration);
|
|
8376
8500
|
if (ms === null) {
|
|
8377
8501
|
console.error(
|
|
8378
|
-
|
|
8502
|
+
import_chalk16.default.red(`
|
|
8379
8503
|
\u274C Invalid duration: "${options.duration}". Use format like 15m, 1h, 30s.
|
|
8380
8504
|
`)
|
|
8381
8505
|
);
|
|
@@ -8383,20 +8507,20 @@ program.command("pause").description("Temporarily disable Node9 protection for a
|
|
|
8383
8507
|
}
|
|
8384
8508
|
pauseNode9(ms, options.duration);
|
|
8385
8509
|
const expiresAt = new Date(Date.now() + ms).toLocaleTimeString();
|
|
8386
|
-
console.log(
|
|
8510
|
+
console.log(import_chalk16.default.yellow(`
|
|
8387
8511
|
\u23F8 Node9 paused until ${expiresAt}`));
|
|
8388
|
-
console.log(
|
|
8389
|
-
console.log(
|
|
8512
|
+
console.log(import_chalk16.default.gray(` All tool calls will be allowed without review.`));
|
|
8513
|
+
console.log(import_chalk16.default.gray(` Run "node9 resume" to re-enable early.
|
|
8390
8514
|
`));
|
|
8391
8515
|
});
|
|
8392
8516
|
program.command("resume").description("Re-enable Node9 protection immediately").action(() => {
|
|
8393
8517
|
const { paused } = checkPause();
|
|
8394
8518
|
if (!paused) {
|
|
8395
|
-
console.log(
|
|
8519
|
+
console.log(import_chalk16.default.gray("\nNode9 is already active \u2014 nothing to resume.\n"));
|
|
8396
8520
|
return;
|
|
8397
8521
|
}
|
|
8398
8522
|
resumeNode9();
|
|
8399
|
-
console.log(
|
|
8523
|
+
console.log(import_chalk16.default.green("\n\u25B6 Node9 resumed \u2014 protection is active.\n"));
|
|
8400
8524
|
});
|
|
8401
8525
|
var HOOK_BASED_AGENTS = {
|
|
8402
8526
|
claude: "claude",
|
|
@@ -8409,15 +8533,15 @@ program.argument("[command...]", "The agent command to run (e.g., gemini)").acti
|
|
|
8409
8533
|
if (HOOK_BASED_AGENTS[firstArg2] !== void 0) {
|
|
8410
8534
|
const target = HOOK_BASED_AGENTS[firstArg2];
|
|
8411
8535
|
console.error(
|
|
8412
|
-
|
|
8536
|
+
import_chalk16.default.yellow(`
|
|
8413
8537
|
\u26A0\uFE0F Node9 proxy mode does not support "${target}" directly.`)
|
|
8414
8538
|
);
|
|
8415
|
-
console.error(
|
|
8539
|
+
console.error(import_chalk16.default.white(`
|
|
8416
8540
|
"${target}" uses its own hook system. Use:`));
|
|
8417
8541
|
console.error(
|
|
8418
|
-
|
|
8542
|
+
import_chalk16.default.green(` node9 addto ${target} `) + import_chalk16.default.gray("# one-time setup")
|
|
8419
8543
|
);
|
|
8420
|
-
console.error(
|
|
8544
|
+
console.error(import_chalk16.default.green(` ${target} `) + import_chalk16.default.gray("# run normally"));
|
|
8421
8545
|
process.exit(1);
|
|
8422
8546
|
}
|
|
8423
8547
|
const runArgs = firstArg2 === "shell" ? commandArgs.slice(1) : commandArgs;
|
|
@@ -8434,7 +8558,7 @@ program.argument("[command...]", "The agent command to run (e.g., gemini)").acti
|
|
|
8434
8558
|
}
|
|
8435
8559
|
);
|
|
8436
8560
|
if (result.noApprovalMechanism && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON && getConfig().settings.autoStartDaemon) {
|
|
8437
|
-
console.error(
|
|
8561
|
+
console.error(import_chalk16.default.cyan("\n\u{1F6E1}\uFE0F Node9: Starting approval daemon automatically..."));
|
|
8438
8562
|
const daemonReady = await autoStartDaemonAndWait();
|
|
8439
8563
|
if (daemonReady) result = await authorizeHeadless("shell", { command: fullCommand });
|
|
8440
8564
|
}
|
|
@@ -8447,12 +8571,12 @@ program.argument("[command...]", "The agent command to run (e.g., gemini)").acti
|
|
|
8447
8571
|
}
|
|
8448
8572
|
if (!result.approved) {
|
|
8449
8573
|
console.error(
|
|
8450
|
-
|
|
8574
|
+
import_chalk16.default.red(`
|
|
8451
8575
|
\u274C Node9 Blocked: ${result.reason || "Dangerous command detected."}`)
|
|
8452
8576
|
);
|
|
8453
8577
|
process.exit(1);
|
|
8454
8578
|
}
|
|
8455
|
-
console.error(
|
|
8579
|
+
console.error(import_chalk16.default.green("\n\u2705 Approved \u2014 running command...\n"));
|
|
8456
8580
|
await runProxy(fullCommand);
|
|
8457
8581
|
} else {
|
|
8458
8582
|
program.help();
|
|
@@ -8461,14 +8585,15 @@ program.argument("[command...]", "The agent command to run (e.g., gemini)").acti
|
|
|
8461
8585
|
registerUndoCommand(program);
|
|
8462
8586
|
registerShieldCommand(program);
|
|
8463
8587
|
registerConfigShowCommand(program);
|
|
8588
|
+
registerTrustCommand(program);
|
|
8464
8589
|
if (process.argv[2] !== "daemon") {
|
|
8465
8590
|
process.on("unhandledRejection", (reason) => {
|
|
8466
8591
|
const isCheckHook = process.argv[2] === "check";
|
|
8467
8592
|
if (isCheckHook) {
|
|
8468
8593
|
if (process.env.NODE9_DEBUG === "1" || getConfig().settings.enableHookLogDebug) {
|
|
8469
|
-
const logPath =
|
|
8594
|
+
const logPath = import_path24.default.join(import_os20.default.homedir(), ".node9", "hook-debug.log");
|
|
8470
8595
|
const msg = reason instanceof Error ? reason.message : String(reason);
|
|
8471
|
-
|
|
8596
|
+
import_fs22.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
|
|
8472
8597
|
`);
|
|
8473
8598
|
}
|
|
8474
8599
|
process.exit(0);
|