@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 CHANGED
@@ -114,8 +114,8 @@ function sanitizeConfig(raw) {
114
114
  }
115
115
  }
116
116
  const lines = result.error.issues.map((issue) => {
117
- const path24 = issue.path.length > 0 ? issue.path.join(".") : "root";
118
- return ` \u2022 ${path24}: ${issue.message}`;
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, path24) {
1684
+ function getNestedValue(obj, path25) {
1628
1685
  if (!obj || typeof obj !== "object") return null;
1629
- return path24.split(".").reduce((prev, curr) => prev?.[curr], obj);
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 ${pipeAnalysis.sinkTargets.join(", ")}`,
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 (pipeAnalysis.risk === "high") {
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 && import_path7.default.posix.isAbsolute(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 = import_path7.default.join(import_os5.default.homedir(), ".node9", "config.json");
1917
- const projectPath = import_path7.default.join(process.cwd(), "node9.config.json");
1918
- const credsPath = import_path7.default.join(import_os5.default.homedir(), ".node9", "credentials.json");
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: import_fs6.default.existsSync(credsPath) ? "active" : "missing",
1930
- note: import_fs6.default.existsSync(credsPath) ? "credentials found (not evaluated in explain mode)" : "not connected \u2014 run: node9 login"
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: import_fs6.default.existsSync(projectPath) ? "active" : "missing",
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: import_fs6.default.existsSync(globalPath) ? "active" : "missing",
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 import_fs6, import_path7, import_os5, import_picomatch, import_sh_syntax, SQL_DML_KEYWORDS;
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
- import_fs6 = __toESM(require("fs"));
2183
- import_path7 = __toESM(require("path"));
2184
- import_os5 = __toESM(require("os"));
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 (!import_fs7.default.existsSync(PAUSED_FILE)) return { paused: false };
2201
- const state = JSON.parse(import_fs7.default.readFileSync(PAUSED_FILE, "utf-8"));
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
- import_fs7.default.unlinkSync(PAUSED_FILE);
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 = import_path8.default.dirname(filePath);
2216
- if (!import_fs7.default.existsSync(dir)) import_fs7.default.mkdirSync(dir, { recursive: true });
2217
- const tmpPath = `${filePath}.${import_os6.default.hostname()}.${process.pid}.tmp`;
2218
- import_fs7.default.writeFileSync(tmpPath, data, options);
2219
- import_fs7.default.renameSync(tmpPath, filePath);
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 (import_fs7.default.existsSync(PAUSED_FILE)) import_fs7.default.unlinkSync(PAUSED_FILE);
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 (!import_fs7.default.existsSync(TRUST_FILE)) return false;
2234
- const trust = JSON.parse(import_fs7.default.readFileSync(TRUST_FILE, "utf-8"));
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
- import_fs7.default.writeFileSync(TRUST_FILE, JSON.stringify({ entries: active }, null, 2));
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 (import_fs7.default.existsSync(TRUST_FILE)) {
2250
- trust = JSON.parse(import_fs7.default.readFileSync(TRUST_FILE, "utf-8"));
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 = import_path8.default.join(import_os6.default.homedir(), ".node9", "decisions.json");
2267
- if (!import_fs7.default.existsSync(file)) return null;
2268
- const decisions = JSON.parse(import_fs7.default.readFileSync(file, "utf-8"));
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 import_fs7, import_path8, import_os6, PAUSED_FILE, TRUST_FILE;
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
- import_fs7 = __toESM(require("fs"));
2280
- import_path8 = __toESM(require("path"));
2281
- import_os6 = __toESM(require("os"));
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 = import_path8.default.join(import_os6.default.homedir(), ".node9", "PAUSED");
2284
- TRUST_FILE = import_path8.default.join(import_os6.default.homedir(), ".node9", "trust.json");
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 = import_path9.default.join(import_os7.default.homedir(), ".node9", "daemon.pid");
2292
- if (!import_fs8.default.existsSync(pidFile)) return null;
2293
- const data = JSON.parse(import_fs8.default.readFileSync(pidFile, "utf-8"));
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 = import_path9.default.join(import_os7.default.homedir(), ".node9", "daemon.pid");
2302
- if (import_fs8.default.existsSync(pidFile)) {
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(import_fs8.default.readFileSync(pidFile, "utf-8"));
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 import_fs8, import_path9, import_os7, import_child_process, DAEMON_PORT, DAEMON_HOST;
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
- import_fs8 = __toESM(require("fs"));
2402
- import_path9 = __toESM(require("path"));
2403
- import_os7 = __toESM(require("os"));
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 = import_path10.default.basename(editFilePath);
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 import_path10, CODE_KEYS;
2563
+ var import_path11, CODE_KEYS;
2495
2564
  var init_context_sniper = __esm({
2496
2565
  "src/context-sniper.ts"() {
2497
2566
  "use strict";
2498
- import_path10 = __toESM(require("path"));
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 ? import_path11.default.basename(String(obj.file_path)) : "file";
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, import_path11, isTestEnv;
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
- import_path11 = __toESM(require("path"));
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: import_os8.default.hostname(),
2804
+ hostname: import_os9.default.hostname(),
2736
2805
  cwd: process.cwd(),
2737
- platform: import_os8.default.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: import_os8.default.hostname(),
2827
+ hostname: import_os9.default.hostname(),
2759
2828
  cwd: process.cwd(),
2760
- platform: import_os8.default.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
- import_fs9.default.appendFileSync(
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
- import_fs9.default.appendFileSync(
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 import_fs9, import_os8;
2899
+ var import_fs10, import_os9;
2831
2900
  var init_cloud = __esm({
2832
2901
  "src/auth/cloud.ts"() {
2833
2902
  "use strict";
2834
- import_fs9 = __toESM(require("fs"));
2835
- import_os8 = __toESM(require("os"));
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, import_path12, import_os9, import_crypto2, ACTIVITY_SOCKET_PATH;
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
- import_path12 = __toESM(require("path"));
3193
- import_os9 = __toESM(require("os"));
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" : import_path12.default.join(import_os9.default.tmpdir(), "node9-activity.sock");
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 = import_path14.default.dirname(filePath);
4698
- if (!import_fs11.default.existsSync(dir)) import_fs11.default.mkdirSync(dir, { recursive: true });
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
- import_fs11.default.writeFileSync(tmpPath, data, options);
4701
- import_fs11.default.renameSync(tmpPath, filePath);
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 = import_path14.default.dirname(AUDIT_LOG_FILE);
4722
- if (!import_fs11.default.existsSync(dir)) import_fs11.default.mkdirSync(dir, { recursive: true });
4723
- import_fs11.default.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
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 (!import_fs11.default.existsSync(AUDIT_LOG_FILE)) return [];
4730
- const lines = import_fs11.default.readFileSync(AUDIT_LOG_FILE, "utf-8").trim().split("\n");
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 (import_fs11.default.existsSync(CREDENTIALS_FILE)) return "Node9 Cloud";
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 import_fs11.default.existsSync(CREDENTIALS_FILE);
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 (import_fs11.default.existsSync(GLOBAL_CONFIG_FILE)) {
4751
- config = JSON.parse(import_fs11.default.readFileSync(GLOBAL_CONFIG_FILE, "utf-8"));
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 (import_fs11.default.existsSync(TRUST_FILE2))
4764
- trust = JSON.parse(import_fs11.default.readFileSync(TRUST_FILE2, "utf-8"));
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 (import_fs11.default.existsSync(DECISIONS_FILE)) {
4776
- return JSON.parse(import_fs11.default.readFileSync(DECISIONS_FILE, "utf-8"));
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
- import_fs11.default.unlinkSync(DAEMON_PID_FILE);
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
- import_fs11.default.unlinkSync(ACTIVITY_SOCKET_PATH2);
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
- import_fs11.default.unlinkSync(ACTIVITY_SOCKET_PATH2);
4963
+ import_fs12.default.unlinkSync(ACTIVITY_SOCKET_PATH2);
4895
4964
  } catch {
4896
4965
  }
4897
4966
  });
4898
4967
  }
4899
- var import_net2, import_fs11, import_path14, import_os11, 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;
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
- import_fs11 = __toESM(require("fs"));
4905
- import_path14 = __toESM(require("path"));
4906
- import_os11 = __toESM(require("os"));
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 = import_os11.default.homedir();
4911
- DAEMON_PID_FILE = import_path14.default.join(homeDir, ".node9", "daemon.pid");
4912
- DECISIONS_FILE = import_path14.default.join(homeDir, ".node9", "decisions.json");
4913
- AUDIT_LOG_FILE = import_path14.default.join(homeDir, ".node9", "audit.log");
4914
- TRUST_FILE2 = import_path14.default.join(homeDir, ".node9", "trust.json");
4915
- GLOBAL_CONFIG_FILE = import_path14.default.join(homeDir, ".node9", "config.json");
4916
- CREDENTIALS_FILE = import_path14.default.join(homeDir, ".node9", "credentials.json");
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" : import_path14.default.join(import_os11.default.tmpdir(), "node9-activity.sock");
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
- import_fs12.default.unlinkSync(DAEMON_PID_FILE);
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" && import_path15.default.isAbsolute(cwd) ? cwd : void 0;
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 (import_fs12.default.existsSync(DAEMON_PID_FILE)) {
5400
- const { pid } = JSON.parse(import_fs12.default.readFileSync(DAEMON_PID_FILE, "utf-8"));
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
- import_fs12.default.unlinkSync(DAEMON_PID_FILE);
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, import_fs12, import_path15, import_crypto4, import_child_process4, import_chalk2;
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
- import_fs12 = __toESM(require("fs"));
5471
- import_path15 = __toESM(require("path"));
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 (!import_fs13.default.existsSync(DAEMON_PID_FILE)) return console.log(import_chalk3.default.yellow("Not running."));
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(import_fs13.default.readFileSync(DAEMON_PID_FILE, "utf-8"));
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
- import_fs13.default.unlinkSync(DAEMON_PID_FILE);
5562
+ import_fs14.default.unlinkSync(DAEMON_PID_FILE);
5494
5563
  } catch {
5495
5564
  }
5496
5565
  }
5497
5566
  }
5498
5567
  function daemonStatus() {
5499
- if (import_fs13.default.existsSync(DAEMON_PID_FILE)) {
5568
+ if (import_fs14.default.existsSync(DAEMON_PID_FILE)) {
5500
5569
  try {
5501
- const { pid } = JSON.parse(import_fs13.default.readFileSync(DAEMON_PID_FILE, "utf-8"));
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 import_fs13, import_chalk3, import_child_process5;
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
- import_fs13 = __toESM(require("fs"));
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 `${import_chalk14.default.gray(time)} ${icon} ${import_chalk14.default.white.bold(toolName)} ${import_chalk14.default.dim(argsPreview)}`;
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 = import_chalk14.default.green("\u2713 ALLOW");
5626
+ status = import_chalk15.default.green("\u2713 ALLOW");
5558
5627
  } else if (result.status === "dlp") {
5559
- status = import_chalk14.default.bgRed.white.bold(" \u{1F6E1}\uFE0F DLP ");
5628
+ status = import_chalk15.default.bgRed.white.bold(" \u{1F6E1}\uFE0F DLP ");
5560
5629
  } else {
5561
- status = import_chalk14.default.red("\u2717 BLOCK");
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)} ${import_chalk14.default.yellow("\u25CF \u2026")}\r`);
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 (import_fs20.default.existsSync(PID_FILE)) {
5644
+ if (import_fs21.default.existsSync(PID_FILE)) {
5576
5645
  try {
5577
- const { port } = JSON.parse(import_fs20.default.readFileSync(PID_FILE, "utf-8"));
5646
+ const { port } = JSON.parse(import_fs21.default.readFileSync(PID_FILE, "utf-8"));
5578
5647
  pidPort = port;
5579
5648
  } catch {
5580
- console.error(import_chalk14.default.dim("\u26A0\uFE0F Could not read PID file; falling back to default port."));
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(import_chalk14.default.dim("\u{1F6E1}\uFE0F Starting Node9 daemon..."));
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(import_chalk14.default.red("\u274C Daemon failed to start. Try: node9 daemon start"));
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(import_chalk14.default.green("\u2713 Flight Recorder buffer cleared."));
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
- import_fs20.default.appendFileSync(
5743
- import_path22.default.join(import_os18.default.homedir(), ".node9", "hook-debug.log"),
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" ? import_chalk14.default.green("\u2713 ALLOWED (terminal)") : import_chalk14.default.red("\u2717 DENIED (terminal)");
5751
- console.log(`${import_chalk14.default.cyan("\u25C6")} ${import_chalk14.default.bold(req2.toolName.padEnd(16))} ${decisionLabel}`);
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(import_chalk14.default.cyan.bold(`
5789
- \u{1F6F0}\uFE0F Node9 tail `) + import_chalk14.default.dim(`\u2192 ${dashboardUrl}`));
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(import_chalk14.default.dim("Interactive approvals enabled. [A] Allow [D] Deny"));
5860
+ console.log(import_chalk15.default.dim("Interactive approvals enabled. [A] Allow [D] Deny"));
5792
5861
  }
5793
5862
  if (options.history) {
5794
- console.log(import_chalk14.default.dim("Showing history + live events. Press Ctrl+C to exit.\n"));
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
- import_chalk14.default.dim("Showing live events only. Use --history to include past. Press Ctrl+C to exit.\n")
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(import_chalk14.default.dim("\n\u{1F6F0}\uFE0F Disconnected."));
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(import_chalk14.default.red(`Failed to connect: HTTP ${res.statusCode}`));
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(import_chalk14.default.red("\n\u274C Daemon disconnected."));
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(import_chalk14.default.red(`
5992
+ console.error(import_chalk15.default.red(`
5924
5993
  \u274C ${msg}`));
5925
5994
  process.exit(1);
5926
5995
  });
5927
5996
  }
5928
- var import_http2, import_chalk14, import_fs20, import_os18, import_path22, 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;
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
- import_chalk14 = __toESM(require("chalk"));
5934
- import_fs20 = __toESM(require("fs"));
5935
- import_os18 = __toESM(require("os"));
5936
- import_path22 = __toESM(require("path"));
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 = import_path22.default.join(import_os18.default.homedir(), ".node9", "daemon.pid");
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 import_fs10 = __toESM(require("fs"));
5980
- var import_path13 = __toESM(require("path"));
5981
- var import_os10 = __toESM(require("os"));
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 (import_fs10.default.existsSync(filePath)) {
5999
- return JSON.parse(import_fs10.default.readFileSync(filePath, "utf-8"));
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 = import_path13.default.dirname(filePath);
6007
- if (!import_fs10.default.existsSync(dir)) import_fs10.default.mkdirSync(dir, { recursive: true });
6008
- import_fs10.default.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n");
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 = import_os10.default.homedir();
6016
- const hooksPath = import_path13.default.join(homeDir2, ".claude", "settings.json");
6017
- const mcpPath = import_path13.default.join(homeDir2, ".claude.json");
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 = import_os10.default.homedir();
6066
- const settingsPath = import_path13.default.join(homeDir2, ".gemini", "settings.json");
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 = import_os10.default.homedir();
6105
- const mcpPath = import_path13.default.join(homeDir2, ".cursor", "mcp.json");
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 = import_os10.default.homedir();
6132
- const mcpPath = import_path13.default.join(homeDir2, ".claude.json");
6133
- const hooksPath = import_path13.default.join(homeDir2, ".claude", "settings.json");
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 = import_os10.default.homedir();
6208
- const settingsPath = import_path13.default.join(homeDir2, ".gemini", "settings.json");
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 = import_os10.default.homedir();
6291
- const mcpPath = import_path13.default.join(homeDir2, ".cursor", "mcp.json");
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 import_chalk15 = __toESM(require("chalk"));
6348
- var import_fs21 = __toESM(require("fs"));
6349
- var import_path23 = __toESM(require("path"));
6350
- var import_os19 = __toESM(require("os"));
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 import_fs15 = __toESM(require("fs"));
6572
- var import_path17 = __toESM(require("path"));
6573
- var import_os13 = __toESM(require("os"));
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 import_fs14 = __toESM(require("fs"));
6583
- var import_path16 = __toESM(require("path"));
6584
- var import_os12 = __toESM(require("os"));
6585
- var SNAPSHOT_STACK_PATH = import_path16.default.join(import_os12.default.homedir(), ".node9", "snapshots.json");
6586
- var UNDO_LATEST_PATH = import_path16.default.join(import_os12.default.homedir(), ".node9", "undo_latest.txt");
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 (import_fs14.default.existsSync(SNAPSHOT_STACK_PATH))
6592
- return JSON.parse(import_fs14.default.readFileSync(SNAPSHOT_STACK_PATH, "utf-8"));
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 = import_path16.default.dirname(SNAPSHOT_STACK_PATH);
6599
- if (!import_fs14.default.existsSync(dir)) import_fs14.default.mkdirSync(dir, { recursive: true });
6600
- import_fs14.default.writeFileSync(SNAPSHOT_STACK_PATH, JSON.stringify(stack, null, 2));
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 = import_fs14.default.realpathSync(cwd);
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 import_path16.default.join(import_os12.default.homedir(), ".node9", "snapshots", hash);
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 import_fs14.default.readdirSync(shadowDir)) {
6700
+ for (const f of import_fs15.default.readdirSync(shadowDir)) {
6632
6701
  if (f.startsWith("index_")) {
6633
- const fp = import_path16.default.join(shadowDir, f);
6702
+ const fp = import_path17.default.join(shadowDir, f);
6634
6703
  try {
6635
- if (import_fs14.default.statSync(fp).mtimeMs < cutoff) import_fs14.default.unlinkSync(fp);
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
- import_fs14.default.writeFileSync(import_path16.default.join(shadowDir, "info", "exclude"), lines + "\n", "utf8");
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 = import_path16.default.join(shadowDir, "project-path.txt");
6729
+ const ptPath = import_path17.default.join(shadowDir, "project-path.txt");
6661
6730
  try {
6662
- const stored = import_fs14.default.readFileSync(ptPath, "utf8").trim();
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
- import_fs14.default.rmSync(shadowDir, { recursive: true, force: true });
6737
+ import_fs15.default.rmSync(shadowDir, { recursive: true, force: true });
6669
6738
  } catch {
6670
6739
  try {
6671
- import_fs14.default.writeFileSync(ptPath, normalizedCwd, "utf8");
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
- import_fs14.default.mkdirSync(shadowDir, { recursive: true });
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 = import_path16.default.join(shadowDir, "config");
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
- import_fs14.default.writeFileSync(import_path16.default.join(shadowDir, "project-path.txt"), normalizedCwd, "utf8");
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 = import_path16.default.join(shadowDir, `index_${process.pid}_${Date.now()}`);
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
- import_fs14.default.writeFileSync(UNDO_LATEST_PATH, commitHash);
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
- import_fs14.default.unlinkSync(indexFile);
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 = import_path16.default.join(dir, file);
6828
- if (!snapshotFiles.has(file) && import_fs14.default.existsSync(fullPath)) {
6829
- import_fs14.default.unlinkSync(fullPath);
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 = import_path17.default.join(import_os13.default.homedir(), ".node9", "hook-debug.log");
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
- import_fs15.default.appendFileSync(
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 = import_path17.default.join(import_os13.default.homedir(), ".node9", "hook-debug.log");
6867
- if (!import_fs15.default.existsSync(import_path17.default.dirname(logPath)))
6868
- import_fs15.default.mkdirSync(import_path17.default.dirname(logPath), { recursive: true });
6869
- import_fs15.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${raw}
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 = import_fs15.default.openSync("/dev/tty", "w");
6883
- const writeTty = (line) => import_fs15.default.writeSync(ttyFd, line + "\n");
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
- import_fs15.default.closeSync(ttyFd);
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" && import_path17.default.isAbsolute(payload.cwd) ? payload.cwd : void 0;
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 = import_fs15.default.openSync("/dev/tty", "w");
6943
- import_fs15.default.writeSync(
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
- import_fs15.default.closeSync(tty);
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 = import_path17.default.join(import_os13.default.homedir(), ".node9", "hook-debug.log");
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
- import_fs15.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] ERROR: ${errMsg}
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 import_fs16 = __toESM(require("fs"));
7011
- var import_path18 = __toESM(require("path"));
7012
- var import_os14 = __toESM(require("os"));
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 = import_path18.default.join(import_os14.default.homedir(), ".node9", "audit.log");
7035
- if (!import_fs16.default.existsSync(import_path18.default.dirname(logPath)))
7036
- import_fs16.default.mkdirSync(import_path18.default.dirname(logPath), { recursive: true });
7037
- import_fs16.default.appendFileSync(logPath, JSON.stringify(entry) + "\n");
7038
- const safeCwd = typeof payload.cwd === "string" && import_path18.default.isAbsolute(payload.cwd) ? payload.cwd : void 0;
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 = import_path18.default.join(import_os14.default.homedir(), ".node9", "hook-debug.log");
7116
+ const debugPath = import_path19.default.join(import_os15.default.homedir(), ".node9", "hook-debug.log");
7048
7117
  try {
7049
- import_fs16.default.appendFileSync(debugPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] LOG_ERROR: ${msg}
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 import_fs17 = __toESM(require("fs"));
7354
- var import_path19 = __toESM(require("path"));
7355
- var import_os15 = __toESM(require("os"));
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 = import_os15.default.homedir();
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 = import_path19.default.join(homeDir2, ".node9", "config.json");
7410
- if (import_fs17.default.existsSync(globalConfigPath)) {
7478
+ const globalConfigPath = import_path20.default.join(homeDir2, ".node9", "config.json");
7479
+ if (import_fs18.default.existsSync(globalConfigPath)) {
7411
7480
  try {
7412
- JSON.parse(import_fs17.default.readFileSync(globalConfigPath, "utf-8"));
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 = import_path19.default.join(process.cwd(), "node9.config.json");
7421
- if (import_fs17.default.existsSync(projectConfigPath)) {
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(import_fs17.default.readFileSync(projectConfigPath, "utf-8"));
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 = import_path19.default.join(homeDir2, ".node9", "credentials.json");
7433
- if (import_fs17.default.existsSync(credsPath)) {
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 = import_path19.default.join(homeDir2, ".claude", "settings.json");
7443
- if (import_fs17.default.existsSync(claudeSettingsPath)) {
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(import_fs17.default.readFileSync(claudeSettingsPath, "utf-8"));
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 = import_path19.default.join(homeDir2, ".gemini", "settings.json");
7462
- if (import_fs17.default.existsSync(geminiSettingsPath)) {
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(import_fs17.default.readFileSync(geminiSettingsPath, "utf-8"));
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 = import_path19.default.join(homeDir2, ".cursor", "hooks.json");
7481
- if (import_fs17.default.existsSync(cursorHooksPath)) {
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(import_fs17.default.readFileSync(cursorHooksPath, "utf-8"));
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 import_fs18 = __toESM(require("fs"));
7522
- var import_path20 = __toESM(require("path"));
7523
- var import_os16 = __toESM(require("os"));
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 = import_path20.default.join(import_os16.default.homedir(), ".node9", "audit.log");
7537
- if (!import_fs18.default.existsSync(logPath)) {
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 = import_fs18.default.readFileSync(logPath, "utf-8");
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 import_fs19 = __toESM(require("fs"));
7662
- var import_path21 = __toESM(require("path"));
7663
- var import_os17 = __toESM(require("os"));
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 = import_path21.default.join(process.cwd(), "node9.config.json");
7701
- const globalConfig = import_path21.default.join(import_os17.default.homedir(), ".node9", "config.json");
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: ${import_fs19.default.existsSync(projectConfig) ? import_chalk10.default.green("Active (node9.config.json)") : import_chalk10.default.gray("Not present")}`
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: ${import_fs19.default.existsSync(globalConfig) ? import_chalk10.default.green("Active (~/.node9/config.json)") : import_chalk10.default.gray("Not present")}`
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
- import_fs21.default.readFileSync(import_path23.default.join(__dirname, "../package.json"), "utf-8")
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 = import_path23.default.join(import_os19.default.homedir(), ".node9", "credentials.json");
8099
- if (!import_fs21.default.existsSync(import_path23.default.dirname(credPath)))
8100
- import_fs21.default.mkdirSync(import_path23.default.dirname(credPath), { recursive: true });
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 (import_fs21.default.existsSync(credPath)) {
8105
- const raw = JSON.parse(import_fs21.default.readFileSync(credPath, "utf-8"));
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
- import_fs21.default.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), { mode: 384 });
8241
+ import_fs22.default.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), { mode: 384 });
8118
8242
  if (profileName === "default") {
8119
- const configPath = import_path23.default.join(import_os19.default.homedir(), ".node9", "config.json");
8243
+ const configPath = import_path24.default.join(import_os20.default.homedir(), ".node9", "config.json");
8120
8244
  let config = {};
8121
8245
  try {
8122
- if (import_fs21.default.existsSync(configPath))
8123
- config = JSON.parse(import_fs21.default.readFileSync(configPath, "utf-8"));
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 (!import_fs21.default.existsSync(import_path23.default.dirname(configPath)))
8139
- import_fs21.default.mkdirSync(import_path23.default.dirname(configPath), { recursive: true });
8140
- import_fs21.default.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
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(import_chalk15.default.green(`\u2705 Profile "${profileName}" saved`));
8144
- console.log(import_chalk15.default.gray(` Switch to it per-session: NODE9_PROFILE=${profileName} claude`));
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(import_chalk15.default.green(`\u2705 Privacy mode \u{1F6E1}\uFE0F`));
8147
- console.log(import_chalk15.default.gray(` All decisions stay on this machine.`));
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(import_chalk15.default.green(`\u2705 Logged in \u2014 agent mode`));
8150
- console.log(import_chalk15.default.gray(` Team policy enforced for all calls via Node9 cloud.`));
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(import_chalk15.default.red(`Unknown target: "${target}". Supported: claude, gemini, cursor`));
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(import_chalk15.default.cyan("\n\u{1F6E1}\uFE0F Node9 Setup \u2014 integrate with your AI agent\n"));
8163
- console.log(" Usage: " + import_chalk15.default.white("node9 setup <target>") + "\n");
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(" " + import_chalk15.default.green("claude") + " \u2014 Claude Code (hook mode)");
8166
- console.log(" " + import_chalk15.default.green("gemini") + " \u2014 Gemini CLI (hook mode)");
8167
- console.log(" " + import_chalk15.default.green("cursor") + " \u2014 Cursor (hook mode)");
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(import_chalk15.default.red(`Unknown target: "${target}". Supported: claude, gemini, cursor`));
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(import_chalk15.default.red(`Unknown target: "${target}". Supported: claude, gemini, cursor`));
8308
+ console.error(import_chalk16.default.red(`Unknown target: "${target}". Supported: claude, gemini, cursor`));
8185
8309
  process.exit(1);
8186
8310
  }
8187
- console.log(import_chalk15.default.cyan(`
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(import_chalk15.default.red(` \u26A0\uFE0F Failed: ${err instanceof Error ? err.message : String(err)}`));
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(import_chalk15.default.gray("\n Restart the agent for changes to take effect."));
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(import_chalk15.default.cyan("\n\u{1F6E1}\uFE0F Node9 Uninstall\n"));
8200
- console.log(import_chalk15.default.bold("Stopping daemon..."));
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(import_chalk15.default.green(" \u2705 Daemon stopped"));
8327
+ console.log(import_chalk16.default.green(" \u2705 Daemon stopped"));
8204
8328
  } catch {
8205
- console.log(import_chalk15.default.blue(" \u2139\uFE0F Daemon was not running"));
8329
+ console.log(import_chalk16.default.blue(" \u2139\uFE0F Daemon was not running"));
8206
8330
  }
8207
- console.log(import_chalk15.default.bold("\nRemoving hooks..."));
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
- import_chalk15.default.red(
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 = import_path23.default.join(import_os19.default.homedir(), ".node9");
8227
- if (import_fs21.default.existsSync(node9Dir)) {
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
- import_fs21.default.rmSync(node9Dir, { recursive: true });
8234
- if (import_fs21.default.existsSync(node9Dir)) {
8357
+ import_fs22.default.rmSync(node9Dir, { recursive: true });
8358
+ if (import_fs22.default.existsSync(node9Dir)) {
8235
8359
  console.error(
8236
- import_chalk15.default.red("\n \u26A0\uFE0F ~/.node9/ could not be fully deleted \u2014 remove it manually.")
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(import_chalk15.default.green("\n \u2705 Deleted ~/.node9/ (config, audit log, credentials)"));
8363
+ console.log(import_chalk16.default.green("\n \u2705 Deleted ~/.node9/ (config, audit log, credentials)"));
8240
8364
  }
8241
8365
  } else {
8242
- console.log(import_chalk15.default.yellow("\n Skipped \u2014 ~/.node9/ was not deleted."));
8366
+ console.log(import_chalk16.default.yellow("\n Skipped \u2014 ~/.node9/ was not deleted."));
8243
8367
  }
8244
8368
  } else {
8245
- console.log(import_chalk15.default.blue("\n \u2139\uFE0F ~/.node9/ not found \u2014 nothing to delete"));
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
- import_chalk15.default.gray("\n ~/.node9/ kept \u2014 run with --purge to delete config and audit log")
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(import_chalk15.default.red("\n \u26A0\uFE0F Some hooks could not be removed \u2014 see errors above."));
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(import_chalk15.default.green.bold("\n\u{1F6E1}\uFE0F Node9 removed. Run: npm uninstall -g @node9/proxy"));
8257
- console.log(import_chalk15.default.gray(" Restart any open AI agent sessions for changes to take effect.\n"));
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(import_chalk15.default.red(`
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(import_chalk15.default.cyan.bold("\u{1F6E1}\uFE0F Node9 Explain"));
8405
+ console.log(import_chalk16.default.cyan.bold("\u{1F6E1}\uFE0F Node9 Explain"));
8282
8406
  console.log("");
8283
- console.log(` ${import_chalk15.default.bold("Tool:")} ${import_chalk15.default.white(result.tool)}`);
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(` ${import_chalk15.default.bold("Input:")} ${import_chalk15.default.gray(preview)}`);
8410
+ console.log(` ${import_chalk16.default.bold("Input:")} ${import_chalk16.default.gray(preview)}`);
8287
8411
  }
8288
8412
  console.log("");
8289
- console.log(import_chalk15.default.bold("Config Sources (Waterfall):"));
8413
+ console.log(import_chalk16.default.bold("Config Sources (Waterfall):"));
8290
8414
  for (const tier of result.waterfall) {
8291
- const num = import_chalk15.default.gray(` ${tier.tier}.`);
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 = import_chalk15.default.gray(tier.note ?? "");
8419
+ statusStr = import_chalk16.default.gray(tier.note ?? "");
8296
8420
  } else if (tier.status === "active") {
8297
- const loc = tier.path ? import_chalk15.default.gray(tier.path) : "";
8298
- const note = tier.note ? import_chalk15.default.gray(`(${tier.note})`) : "";
8299
- statusStr = import_chalk15.default.green("\u2713 active") + (loc ? " " + loc : "") + (note ? " " + note : "");
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 = import_chalk15.default.gray("\u25CB " + (tier.note ?? "not found"));
8425
+ statusStr = import_chalk16.default.gray("\u25CB " + (tier.note ?? "not found"));
8302
8426
  }
8303
- console.log(`${num} ${import_chalk15.default.white(label)} ${statusStr}`);
8427
+ console.log(`${num} ${import_chalk16.default.white(label)} ${statusStr}`);
8304
8428
  }
8305
8429
  console.log("");
8306
- console.log(import_chalk15.default.bold("Policy Evaluation:"));
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 = import_chalk15.default.green(" \u2705");
8311
- else if (step.outcome === "review") icon = import_chalk15.default.red(" \u{1F534}");
8312
- else if (step.outcome === "skip") icon = import_chalk15.default.gray(" \u2500 ");
8313
- else icon = import_chalk15.default.gray(" \u25CB ");
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 ? import_chalk15.default.white.bold(name) : import_chalk15.default.white(name);
8316
- const detail = isFinal ? import_chalk15.default.white(step.detail) : import_chalk15.default.gray(step.detail);
8317
- const arrow = isFinal ? import_chalk15.default.yellow(" \u2190 STOP") : "";
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(import_chalk15.default.green.bold(" Decision: \u2705 ALLOW") + import_chalk15.default.gray(" \u2014 no approval needed"));
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
- import_chalk15.default.red.bold(" Decision: \u{1F534} REVIEW") + import_chalk15.default.gray(" \u2014 human approval required")
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(import_chalk15.default.gray(` Reason: ${result.blockedByLabel}`));
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 = import_path23.default.join(import_os19.default.homedir(), ".node9", "config.json");
8335
- if (import_fs21.default.existsSync(configPath) && !options.force) {
8336
- console.log(import_chalk15.default.yellow(`\u2139\uFE0F Global config already exists: ${configPath}`));
8337
- console.log(import_chalk15.default.gray(` Run with --force to overwrite.`));
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 = import_path23.default.dirname(configPath);
8350
- if (!import_fs21.default.existsSync(dir)) import_fs21.default.mkdirSync(dir, { recursive: true });
8351
- import_fs21.default.writeFileSync(configPath, JSON.stringify(configToSave, null, 2));
8352
- console.log(import_chalk15.default.green(`\u2705 Global config created: ${configPath}`));
8353
- console.log(import_chalk15.default.cyan(` Mode set to: ${safeMode}`));
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
- import_chalk15.default.gray(` Undo Engine is ENABLED by default. Use 'node9 undo' to revert AI changes.`)
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(import_chalk15.default.red(`\u274C ${err instanceof Error ? err.message : String(err)}`));
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
- import_chalk15.default.red(`
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(import_chalk15.default.yellow(`
8510
+ console.log(import_chalk16.default.yellow(`
8387
8511
  \u23F8 Node9 paused until ${expiresAt}`));
8388
- console.log(import_chalk15.default.gray(` All tool calls will be allowed without review.`));
8389
- console.log(import_chalk15.default.gray(` Run "node9 resume" to re-enable early.
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(import_chalk15.default.gray("\nNode9 is already active \u2014 nothing to resume.\n"));
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(import_chalk15.default.green("\n\u25B6 Node9 resumed \u2014 protection is active.\n"));
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
- import_chalk15.default.yellow(`
8536
+ import_chalk16.default.yellow(`
8413
8537
  \u26A0\uFE0F Node9 proxy mode does not support "${target}" directly.`)
8414
8538
  );
8415
- console.error(import_chalk15.default.white(`
8539
+ console.error(import_chalk16.default.white(`
8416
8540
  "${target}" uses its own hook system. Use:`));
8417
8541
  console.error(
8418
- import_chalk15.default.green(` node9 addto ${target} `) + import_chalk15.default.gray("# one-time setup")
8542
+ import_chalk16.default.green(` node9 addto ${target} `) + import_chalk16.default.gray("# one-time setup")
8419
8543
  );
8420
- console.error(import_chalk15.default.green(` ${target} `) + import_chalk15.default.gray("# run normally"));
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(import_chalk15.default.cyan("\n\u{1F6E1}\uFE0F Node9: Starting approval daemon automatically..."));
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
- import_chalk15.default.red(`
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(import_chalk15.default.green("\n\u2705 Approved \u2014 running command...\n"));
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 = import_path23.default.join(import_os19.default.homedir(), ".node9", "hook-debug.log");
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
- import_fs21.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
8596
+ import_fs22.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
8472
8597
  `);
8473
8598
  }
8474
8599
  process.exit(0);