@node9/proxy 2.2.1 → 2.3.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.
Files changed (3) hide show
  1. package/dist/cli.js +147 -20
  2. package/dist/cli.mjs +147 -20
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -21561,6 +21561,75 @@ function uninstallSystemd() {
21561
21561
  function isSystemdInstalled() {
21562
21562
  return import_fs43.default.existsSync(SYSTEMD_UNIT);
21563
21563
  }
21564
+ function windowsLauncherVbs(nodePath, scriptPath) {
21565
+ for (const p of [nodePath, scriptPath]) {
21566
+ if (p.includes('"')) throw new Error(`Illegal quote in path: ${p}`);
21567
+ }
21568
+ return [
21569
+ "' Auto-generated by node9 \u2014 starts the approval daemon with no console window.",
21570
+ "' Recreated on every `node9 daemon install`; safe to delete (uninstall does).",
21571
+ 'Set sh = CreateObject("Wscript.Shell")',
21572
+ 'sh.Environment("PROCESS")("NODE9_AUTO_STARTED") = "1"',
21573
+ `sh.Run """${nodePath}"" ""${scriptPath}"" daemon", 0, False`,
21574
+ ""
21575
+ ].join("\r\n");
21576
+ }
21577
+ function schtasksCreateArgs(launcherPath) {
21578
+ return [
21579
+ "/Create",
21580
+ "/TN",
21581
+ SCHTASKS_TASK,
21582
+ "/TR",
21583
+ `wscript.exe //B "${launcherPath}"`,
21584
+ "/SC",
21585
+ "ONLOGON",
21586
+ "/F"
21587
+ ];
21588
+ }
21589
+ function installSchtasks(binaryPath) {
21590
+ const launcher = WIN_LAUNCHER();
21591
+ const dir = import_path42.default.dirname(launcher);
21592
+ if (!import_fs43.default.existsSync(dir)) import_fs43.default.mkdirSync(dir, { recursive: true });
21593
+ import_fs43.default.writeFileSync(launcher, windowsLauncherVbs(process.execPath, binaryPath), "utf-8");
21594
+ const create = (0, import_child_process3.spawnSync)("schtasks", schtasksCreateArgs(launcher), {
21595
+ encoding: "utf8",
21596
+ timeout: 1e4
21597
+ });
21598
+ if (create.status !== 0) {
21599
+ throw new Error(
21600
+ `schtasks /Create failed: ${create.stderr || create.stdout || "unknown error"}`
21601
+ );
21602
+ }
21603
+ (0, import_child_process3.spawnSync)("schtasks", ["/Run", "/TN", SCHTASKS_TASK], { encoding: "utf8", timeout: 1e4 });
21604
+ }
21605
+ function uninstallSchtasks() {
21606
+ (0, import_child_process3.spawnSync)("schtasks", ["/Delete", "/TN", SCHTASKS_TASK, "/F"], {
21607
+ encoding: "utf8",
21608
+ timeout: 1e4
21609
+ });
21610
+ const launcher = WIN_LAUNCHER();
21611
+ if (import_fs43.default.existsSync(launcher)) {
21612
+ try {
21613
+ import_fs43.default.unlinkSync(launcher);
21614
+ } catch {
21615
+ }
21616
+ }
21617
+ }
21618
+ function isSchtasksInstalled() {
21619
+ const r = (0, import_child_process3.spawnSync)("schtasks", ["/Query", "/TN", SCHTASKS_TASK], {
21620
+ encoding: "utf8",
21621
+ timeout: 5e3
21622
+ });
21623
+ return r.status === 0;
21624
+ }
21625
+ function isSchtasksEnabled() {
21626
+ const r = (0, import_child_process3.spawnSync)("schtasks", ["/Query", "/TN", SCHTASKS_TASK, "/XML"], {
21627
+ encoding: "utf8",
21628
+ timeout: 5e3
21629
+ });
21630
+ if (r.status !== 0) return false;
21631
+ return !/<Enabled>\s*false\s*<\/Enabled>/i.test(r.stdout ?? "");
21632
+ }
21564
21633
  function stopRunningDaemon() {
21565
21634
  const pidFile = import_path42.default.join(import_os39.default.homedir(), ".node9", "daemon.pid");
21566
21635
  if (!import_fs43.default.existsSync(pidFile)) return;
@@ -21572,15 +21641,15 @@ function stopRunningDaemon() {
21572
21641
  try {
21573
21642
  process.kill(pid, "SIGTERM");
21574
21643
  const deadline = Date.now() + 3e3;
21575
- const pollStop = (0, import_child_process3.spawnSync)(
21576
- "sh",
21577
- ["-c", `while kill -0 ${pid} 2>/dev/null; do sleep 0.1; done`],
21578
- {
21579
- timeout: 3100
21644
+ const sleeper = new Int32Array(new SharedArrayBuffer(4));
21645
+ while (Date.now() < deadline) {
21646
+ try {
21647
+ process.kill(pid, 0);
21648
+ } catch {
21649
+ break;
21580
21650
  }
21581
- );
21582
- void pollStop;
21583
- void deadline;
21651
+ Atomics.wait(sleeper, 0, 0, 100);
21652
+ }
21584
21653
  } catch {
21585
21654
  }
21586
21655
  }
@@ -21618,6 +21687,11 @@ function installDaemonService() {
21618
21687
  installSystemd(binary);
21619
21688
  return { ok: true, platform: "systemd", alreadyInstalled };
21620
21689
  }
21690
+ if (process.platform === "win32") {
21691
+ const alreadyInstalled = isSchtasksInstalled();
21692
+ installSchtasks(binary);
21693
+ return { ok: true, platform: "schtasks", alreadyInstalled };
21694
+ }
21621
21695
  return {
21622
21696
  ok: false,
21623
21697
  reason: `Automatic service install is not supported on ${process.platform}. Start the daemon manually with: node9 daemon start`
@@ -21639,6 +21713,10 @@ function uninstallDaemonService() {
21639
21713
  uninstallSystemd();
21640
21714
  return { ok: true, platform: "systemd", alreadyInstalled: false };
21641
21715
  }
21716
+ if (process.platform === "win32") {
21717
+ uninstallSchtasks();
21718
+ return { ok: true, platform: "schtasks", alreadyInstalled: false };
21719
+ }
21642
21720
  return {
21643
21721
  ok: false,
21644
21722
  reason: `Service management not supported on ${process.platform}.`
@@ -21653,11 +21731,14 @@ function uninstallDaemonService() {
21653
21731
  function isDaemonServiceInstalled() {
21654
21732
  if (process.platform === "darwin") return isLaunchdInstalled();
21655
21733
  if (process.platform === "linux") return isSystemdInstalled();
21734
+ if (process.platform === "win32") return isSchtasksInstalled();
21656
21735
  return false;
21657
21736
  }
21658
21737
  function autostartRepairDecision(opts) {
21659
21738
  if (!opts.autoStartDaemon) return "skip";
21660
- if (process.platform !== "linux" && process.platform !== "darwin") return "unsupported";
21739
+ if (process.platform !== "linux" && process.platform !== "darwin" && process.platform !== "win32") {
21740
+ return "unsupported";
21741
+ }
21661
21742
  if (!opts.installed) return "skip";
21662
21743
  return opts.enabled ? "ok" : "repair";
21663
21744
  }
@@ -21670,6 +21751,13 @@ function enableDaemonServiceQuiet() {
21670
21751
  });
21671
21752
  return r.status === 0;
21672
21753
  }
21754
+ if (process.platform === "win32") {
21755
+ const r = (0, import_child_process3.spawnSync)("schtasks", ["/Change", "/TN", SCHTASKS_TASK, "/ENABLE"], {
21756
+ encoding: "utf8",
21757
+ timeout: 5e3
21758
+ });
21759
+ return r.status === 0;
21760
+ }
21673
21761
  return process.platform === "darwin";
21674
21762
  } catch {
21675
21763
  return false;
@@ -21684,10 +21772,13 @@ function ensureAutostartHealthy(autoStartDaemon) {
21684
21772
  if (decision === "repair") return enableDaemonServiceQuiet() ? "repaired" : "skipped";
21685
21773
  return decision === "ok" ? "ok" : decision === "unsupported" ? "unsupported" : "skipped";
21686
21774
  }
21775
+ function autostartInstallHint() {
21776
+ return process.platform === "linux" ? "Run: systemctl --user enable --now node9-daemon (or: node9 daemon install)" : "Run: node9 daemon install";
21777
+ }
21687
21778
  function autostartAdvice(opts) {
21688
- const installable = process.platform === "linux" || process.platform === "darwin";
21779
+ const installable = process.platform === "linux" || process.platform === "darwin" || process.platform === "win32";
21689
21780
  if (!opts.cloudEnabled || !installable) return null;
21690
- const installHint = process.platform === "linux" ? "Run: systemctl --user enable --now node9-daemon (or: node9 daemon install)" : "Run: node9 daemon install";
21781
+ const installHint = autostartInstallHint();
21691
21782
  if (opts.installed && !opts.enabled) {
21692
21783
  return {
21693
21784
  level: "warn",
@@ -21720,11 +21811,14 @@ function isDaemonServiceEnabled() {
21720
21811
  });
21721
21812
  return r.status === 0;
21722
21813
  }
21814
+ if (process.platform === "win32") {
21815
+ return isSchtasksEnabled();
21816
+ }
21723
21817
  } catch {
21724
21818
  }
21725
21819
  return false;
21726
21820
  }
21727
- var import_fs43, import_path42, import_os39, import_child_process3, LAUNCHD_LABEL, LAUNCHD_PLIST, SYSTEMD_UNIT_DIR, SYSTEMD_UNIT;
21821
+ var import_fs43, import_path42, import_os39, import_child_process3, LAUNCHD_LABEL, LAUNCHD_PLIST, SYSTEMD_UNIT_DIR, SYSTEMD_UNIT, SCHTASKS_TASK, WIN_LAUNCHER;
21728
21822
  var init_service = __esm({
21729
21823
  "src/daemon/service.ts"() {
21730
21824
  "use strict";
@@ -21736,6 +21830,8 @@ var init_service = __esm({
21736
21830
  LAUNCHD_PLIST = import_path42.default.join(import_os39.default.homedir(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
21737
21831
  SYSTEMD_UNIT_DIR = import_path42.default.join(import_os39.default.homedir(), ".config", "systemd", "user");
21738
21832
  SYSTEMD_UNIT = import_path42.default.join(SYSTEMD_UNIT_DIR, "node9-daemon.service");
21833
+ SCHTASKS_TASK = "Node9Daemon";
21834
+ WIN_LAUNCHER = () => import_path42.default.join(import_os39.default.homedir(), ".node9", "daemon-launcher.vbs");
21739
21835
  }
21740
21836
  });
21741
21837
 
@@ -48029,6 +48125,20 @@ function buildReviewMessage(blockedByLabel, ruleDescription, reason) {
48029
48125
  return `Node9 flagged this for your review: ${why}. Approve to proceed, or deny to cancel.`;
48030
48126
  }
48031
48127
 
48128
+ // src/utils/platform-shell.ts
48129
+ function locatorCommand() {
48130
+ return process.platform === "win32" ? "where" : "which";
48131
+ }
48132
+ function shellInvocation(command) {
48133
+ if (process.platform === "win32") {
48134
+ return {
48135
+ file: process.env.ComSpec || "cmd.exe",
48136
+ args: ["/d", "/s", "/c", command]
48137
+ };
48138
+ }
48139
+ return { file: "/bin/bash", args: ["-c", command] };
48140
+ }
48141
+
48032
48142
  // src/proxy/index.ts
48033
48143
  function sanitize(value) {
48034
48144
  return value.replace(/[\x00-\x1F\x7F]/g, "");
@@ -48040,14 +48150,16 @@ async function runProxy(targetCommand) {
48040
48150
  let executable = cmd;
48041
48151
  let useShell = false;
48042
48152
  try {
48043
- const { stdout } = await (0, import_execa.execa)("which", [cmd]);
48044
- if (stdout) executable = stdout.trim();
48153
+ const { stdout } = await (0, import_execa.execa)(locatorCommand(), [cmd]);
48154
+ const first = stdout.split(/\r?\n/)[0]?.trim();
48155
+ if (first) executable = first;
48045
48156
  } catch {
48046
48157
  useShell = true;
48047
48158
  }
48048
48159
  console.error(import_chalk8.default.green(`\u{1F680} Node9 Proxy Active: Monitoring [${targetCommand}]`));
48049
48160
  const spawnEnv = { ...process.env, FORCE_COLOR: "1" };
48050
- const child = useShell ? (0, import_child_process4.spawn)("/bin/bash", ["-c", targetCommand], {
48161
+ const shell = shellInvocation(targetCommand);
48162
+ const child = useShell ? (0, import_child_process4.spawn)(shell.file, shell.args, {
48051
48163
  stdio: ["pipe", "pipe", "inherit"],
48052
48164
  shell: false,
48053
48165
  env: spawnEnv
@@ -48111,6 +48223,17 @@ async function runProxy(targetCommand) {
48111
48223
  child.stdin.write(line + "\n");
48112
48224
  });
48113
48225
  child.stdout.pipe(process.stdout);
48226
+ child.on("error", (err2) => {
48227
+ const what = useShell ? shell.file : executable;
48228
+ console.error(
48229
+ import_chalk8.default.red(
48230
+ err2.code === "ENOENT" ? `
48231
+ \u274C Node9 could not run "${targetCommand}": ${what} was not found on this machine.` : `
48232
+ \u274C Node9 could not run "${targetCommand}": ${err2.message}`
48233
+ )
48234
+ );
48235
+ process.exit(127);
48236
+ });
48114
48237
  child.on("exit", (code) => process.exit(code || 0));
48115
48238
  }
48116
48239
 
@@ -48256,7 +48379,7 @@ async function onboardMachine(apiKey, opts = {}) {
48256
48379
  push.ok ? { name: "register", ok: true, detail: "machine visible in the dashboard" } : { name: "register", ok: false, detail: push.reason }
48257
48380
  );
48258
48381
  const healed = ensureAutostartHealthy(!!getConfig().settings.autoStartDaemon);
48259
- const detail = healed === "repaired" ? "autostart re-enabled (survives reboot)" : isDaemonRunning() ? "running" : healed === "unsupported" ? "no background service on this platform: starts on agent activity" : "starts on agent activity";
48382
+ const detail = healed === "repaired" ? "autostart re-enabled (survives reboot)" : isDaemonRunning() ? "running" : healed === "unsupported" ? "no background service on this platform: starts on agent activity" : "starts on agent activity (make it survive reboots: node9 daemon install)";
48260
48383
  steps.push({ name: "daemon", ok: true, detail });
48261
48384
  }
48262
48385
  const required = ["credentials", "policy-sync", "register"];
@@ -50232,8 +50355,10 @@ function registerDoctorCommand(program2, version2) {
50232
50355
  `));
50233
50356
  section("Binary");
50234
50357
  try {
50235
- const locator = process.platform === "win32" ? "where node9" : "which node9";
50236
- const found = (0, import_child_process8.execSync)(locator, { encoding: "utf-8", timeout: 3e3 }).split(/\r?\n/)[0].trim();
50358
+ const found = (0, import_child_process8.execSync)(`${locatorCommand()} node9`, {
50359
+ encoding: "utf-8",
50360
+ timeout: 3e3
50361
+ }).split(/\r?\n/)[0].trim();
50237
50362
  pass(`node9 found at ${found}`);
50238
50363
  } catch {
50239
50364
  warn("node9 not found in $PATH \u2014 hooks may not find it", "Run: npm install -g node9-ai");
@@ -50360,7 +50485,9 @@ function registerDoctorCommand(program2, version2) {
50360
50485
  const fails = health.consecutiveFailures > 0 ? ` (${health.consecutiveFailures} consecutive failure${health.consecutiveFailures === 1 ? "" : "s"}${health.lastError ? `: ${health.lastError}` : ""})` : "";
50361
50486
  warn(
50362
50487
  `Cloud policy is STALE \u2014 ${when}${fails}. The cached policy is still enforced, but changes from the dashboard are not reaching this machine.`,
50363
- "Run: node9 policy sync (and ensure the daemon autostarts: systemctl --user enable --now node9-daemon)"
50488
+ // Platform-aware: this hint hardcoded systemctl and told a Windows
50489
+ // founder to run a Linux command (QA 2026-08-28).
50490
+ `Run: node9 policy sync (then keep it fresh across reboots \u2014 ${autostartInstallHint()})`
50364
50491
  );
50365
50492
  } else if (health.lastCheckedAt) {
50366
50493
  pass(`Cloud policy fresh \u2014 last synced ${agoLabel(health.lastCheckedAt)}`);
@@ -52102,7 +52229,7 @@ function registerInitCommand(program2) {
52102
52229
  await setupAgent(agent);
52103
52230
  console.log("");
52104
52231
  }
52105
- if ((process.platform === "darwin" || process.platform === "linux") && process.stdout.isTTY) {
52232
+ if ((process.platform === "darwin" || process.platform === "linux" || process.platform === "win32") && process.stdout.isTTY) {
52106
52233
  const alreadyInstalled = isDaemonServiceInstalled();
52107
52234
  if (!alreadyInstalled) {
52108
52235
  const { confirm: confirm4 } = await import("@inquirer/prompts");
package/dist/cli.mjs CHANGED
@@ -21558,6 +21558,75 @@ function uninstallSystemd() {
21558
21558
  function isSystemdInstalled() {
21559
21559
  return fs44.existsSync(SYSTEMD_UNIT);
21560
21560
  }
21561
+ function windowsLauncherVbs(nodePath, scriptPath) {
21562
+ for (const p of [nodePath, scriptPath]) {
21563
+ if (p.includes('"')) throw new Error(`Illegal quote in path: ${p}`);
21564
+ }
21565
+ return [
21566
+ "' Auto-generated by node9 \u2014 starts the approval daemon with no console window.",
21567
+ "' Recreated on every `node9 daemon install`; safe to delete (uninstall does).",
21568
+ 'Set sh = CreateObject("Wscript.Shell")',
21569
+ 'sh.Environment("PROCESS")("NODE9_AUTO_STARTED") = "1"',
21570
+ `sh.Run """${nodePath}"" ""${scriptPath}"" daemon", 0, False`,
21571
+ ""
21572
+ ].join("\r\n");
21573
+ }
21574
+ function schtasksCreateArgs(launcherPath) {
21575
+ return [
21576
+ "/Create",
21577
+ "/TN",
21578
+ SCHTASKS_TASK,
21579
+ "/TR",
21580
+ `wscript.exe //B "${launcherPath}"`,
21581
+ "/SC",
21582
+ "ONLOGON",
21583
+ "/F"
21584
+ ];
21585
+ }
21586
+ function installSchtasks(binaryPath) {
21587
+ const launcher = WIN_LAUNCHER();
21588
+ const dir = path43.dirname(launcher);
21589
+ if (!fs44.existsSync(dir)) fs44.mkdirSync(dir, { recursive: true });
21590
+ fs44.writeFileSync(launcher, windowsLauncherVbs(process.execPath, binaryPath), "utf-8");
21591
+ const create = spawnSync2("schtasks", schtasksCreateArgs(launcher), {
21592
+ encoding: "utf8",
21593
+ timeout: 1e4
21594
+ });
21595
+ if (create.status !== 0) {
21596
+ throw new Error(
21597
+ `schtasks /Create failed: ${create.stderr || create.stdout || "unknown error"}`
21598
+ );
21599
+ }
21600
+ spawnSync2("schtasks", ["/Run", "/TN", SCHTASKS_TASK], { encoding: "utf8", timeout: 1e4 });
21601
+ }
21602
+ function uninstallSchtasks() {
21603
+ spawnSync2("schtasks", ["/Delete", "/TN", SCHTASKS_TASK, "/F"], {
21604
+ encoding: "utf8",
21605
+ timeout: 1e4
21606
+ });
21607
+ const launcher = WIN_LAUNCHER();
21608
+ if (fs44.existsSync(launcher)) {
21609
+ try {
21610
+ fs44.unlinkSync(launcher);
21611
+ } catch {
21612
+ }
21613
+ }
21614
+ }
21615
+ function isSchtasksInstalled() {
21616
+ const r = spawnSync2("schtasks", ["/Query", "/TN", SCHTASKS_TASK], {
21617
+ encoding: "utf8",
21618
+ timeout: 5e3
21619
+ });
21620
+ return r.status === 0;
21621
+ }
21622
+ function isSchtasksEnabled() {
21623
+ const r = spawnSync2("schtasks", ["/Query", "/TN", SCHTASKS_TASK, "/XML"], {
21624
+ encoding: "utf8",
21625
+ timeout: 5e3
21626
+ });
21627
+ if (r.status !== 0) return false;
21628
+ return !/<Enabled>\s*false\s*<\/Enabled>/i.test(r.stdout ?? "");
21629
+ }
21561
21630
  function stopRunningDaemon() {
21562
21631
  const pidFile = path43.join(os40.homedir(), ".node9", "daemon.pid");
21563
21632
  if (!fs44.existsSync(pidFile)) return;
@@ -21569,15 +21638,15 @@ function stopRunningDaemon() {
21569
21638
  try {
21570
21639
  process.kill(pid, "SIGTERM");
21571
21640
  const deadline = Date.now() + 3e3;
21572
- const pollStop = spawnSync2(
21573
- "sh",
21574
- ["-c", `while kill -0 ${pid} 2>/dev/null; do sleep 0.1; done`],
21575
- {
21576
- timeout: 3100
21641
+ const sleeper = new Int32Array(new SharedArrayBuffer(4));
21642
+ while (Date.now() < deadline) {
21643
+ try {
21644
+ process.kill(pid, 0);
21645
+ } catch {
21646
+ break;
21577
21647
  }
21578
- );
21579
- void pollStop;
21580
- void deadline;
21648
+ Atomics.wait(sleeper, 0, 0, 100);
21649
+ }
21581
21650
  } catch {
21582
21651
  }
21583
21652
  }
@@ -21615,6 +21684,11 @@ function installDaemonService() {
21615
21684
  installSystemd(binary);
21616
21685
  return { ok: true, platform: "systemd", alreadyInstalled };
21617
21686
  }
21687
+ if (process.platform === "win32") {
21688
+ const alreadyInstalled = isSchtasksInstalled();
21689
+ installSchtasks(binary);
21690
+ return { ok: true, platform: "schtasks", alreadyInstalled };
21691
+ }
21618
21692
  return {
21619
21693
  ok: false,
21620
21694
  reason: `Automatic service install is not supported on ${process.platform}. Start the daemon manually with: node9 daemon start`
@@ -21636,6 +21710,10 @@ function uninstallDaemonService() {
21636
21710
  uninstallSystemd();
21637
21711
  return { ok: true, platform: "systemd", alreadyInstalled: false };
21638
21712
  }
21713
+ if (process.platform === "win32") {
21714
+ uninstallSchtasks();
21715
+ return { ok: true, platform: "schtasks", alreadyInstalled: false };
21716
+ }
21639
21717
  return {
21640
21718
  ok: false,
21641
21719
  reason: `Service management not supported on ${process.platform}.`
@@ -21650,11 +21728,14 @@ function uninstallDaemonService() {
21650
21728
  function isDaemonServiceInstalled() {
21651
21729
  if (process.platform === "darwin") return isLaunchdInstalled();
21652
21730
  if (process.platform === "linux") return isSystemdInstalled();
21731
+ if (process.platform === "win32") return isSchtasksInstalled();
21653
21732
  return false;
21654
21733
  }
21655
21734
  function autostartRepairDecision(opts) {
21656
21735
  if (!opts.autoStartDaemon) return "skip";
21657
- if (process.platform !== "linux" && process.platform !== "darwin") return "unsupported";
21736
+ if (process.platform !== "linux" && process.platform !== "darwin" && process.platform !== "win32") {
21737
+ return "unsupported";
21738
+ }
21658
21739
  if (!opts.installed) return "skip";
21659
21740
  return opts.enabled ? "ok" : "repair";
21660
21741
  }
@@ -21667,6 +21748,13 @@ function enableDaemonServiceQuiet() {
21667
21748
  });
21668
21749
  return r.status === 0;
21669
21750
  }
21751
+ if (process.platform === "win32") {
21752
+ const r = spawnSync2("schtasks", ["/Change", "/TN", SCHTASKS_TASK, "/ENABLE"], {
21753
+ encoding: "utf8",
21754
+ timeout: 5e3
21755
+ });
21756
+ return r.status === 0;
21757
+ }
21670
21758
  return process.platform === "darwin";
21671
21759
  } catch {
21672
21760
  return false;
@@ -21681,10 +21769,13 @@ function ensureAutostartHealthy(autoStartDaemon) {
21681
21769
  if (decision === "repair") return enableDaemonServiceQuiet() ? "repaired" : "skipped";
21682
21770
  return decision === "ok" ? "ok" : decision === "unsupported" ? "unsupported" : "skipped";
21683
21771
  }
21772
+ function autostartInstallHint() {
21773
+ return process.platform === "linux" ? "Run: systemctl --user enable --now node9-daemon (or: node9 daemon install)" : "Run: node9 daemon install";
21774
+ }
21684
21775
  function autostartAdvice(opts) {
21685
- const installable = process.platform === "linux" || process.platform === "darwin";
21776
+ const installable = process.platform === "linux" || process.platform === "darwin" || process.platform === "win32";
21686
21777
  if (!opts.cloudEnabled || !installable) return null;
21687
- const installHint = process.platform === "linux" ? "Run: systemctl --user enable --now node9-daemon (or: node9 daemon install)" : "Run: node9 daemon install";
21778
+ const installHint = autostartInstallHint();
21688
21779
  if (opts.installed && !opts.enabled) {
21689
21780
  return {
21690
21781
  level: "warn",
@@ -21717,11 +21808,14 @@ function isDaemonServiceEnabled() {
21717
21808
  });
21718
21809
  return r.status === 0;
21719
21810
  }
21811
+ if (process.platform === "win32") {
21812
+ return isSchtasksEnabled();
21813
+ }
21720
21814
  } catch {
21721
21815
  }
21722
21816
  return false;
21723
21817
  }
21724
- var LAUNCHD_LABEL, LAUNCHD_PLIST, SYSTEMD_UNIT_DIR, SYSTEMD_UNIT;
21818
+ var LAUNCHD_LABEL, LAUNCHD_PLIST, SYSTEMD_UNIT_DIR, SYSTEMD_UNIT, SCHTASKS_TASK, WIN_LAUNCHER;
21725
21819
  var init_service = __esm({
21726
21820
  "src/daemon/service.ts"() {
21727
21821
  "use strict";
@@ -21729,6 +21823,8 @@ var init_service = __esm({
21729
21823
  LAUNCHD_PLIST = path43.join(os40.homedir(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
21730
21824
  SYSTEMD_UNIT_DIR = path43.join(os40.homedir(), ".config", "systemd", "user");
21731
21825
  SYSTEMD_UNIT = path43.join(SYSTEMD_UNIT_DIR, "node9-daemon.service");
21826
+ SCHTASKS_TASK = "Node9Daemon";
21827
+ WIN_LAUNCHER = () => path43.join(os40.homedir(), ".node9", "daemon-launcher.vbs");
21732
21828
  }
21733
21829
  });
21734
21830
 
@@ -48022,6 +48118,20 @@ function buildReviewMessage(blockedByLabel, ruleDescription, reason) {
48022
48118
  return `Node9 flagged this for your review: ${why}. Approve to proceed, or deny to cancel.`;
48023
48119
  }
48024
48120
 
48121
+ // src/utils/platform-shell.ts
48122
+ function locatorCommand() {
48123
+ return process.platform === "win32" ? "where" : "which";
48124
+ }
48125
+ function shellInvocation(command) {
48126
+ if (process.platform === "win32") {
48127
+ return {
48128
+ file: process.env.ComSpec || "cmd.exe",
48129
+ args: ["/d", "/s", "/c", command]
48130
+ };
48131
+ }
48132
+ return { file: "/bin/bash", args: ["-c", command] };
48133
+ }
48134
+
48025
48135
  // src/proxy/index.ts
48026
48136
  function sanitize(value) {
48027
48137
  return value.replace(/[\x00-\x1F\x7F]/g, "");
@@ -48033,14 +48143,16 @@ async function runProxy(targetCommand) {
48033
48143
  let executable = cmd;
48034
48144
  let useShell = false;
48035
48145
  try {
48036
- const { stdout } = await execa("which", [cmd]);
48037
- if (stdout) executable = stdout.trim();
48146
+ const { stdout } = await execa(locatorCommand(), [cmd]);
48147
+ const first = stdout.split(/\r?\n/)[0]?.trim();
48148
+ if (first) executable = first;
48038
48149
  } catch {
48039
48150
  useShell = true;
48040
48151
  }
48041
48152
  console.error(chalk8.green(`\u{1F680} Node9 Proxy Active: Monitoring [${targetCommand}]`));
48042
48153
  const spawnEnv = { ...process.env, FORCE_COLOR: "1" };
48043
- const child = useShell ? spawn2("/bin/bash", ["-c", targetCommand], {
48154
+ const shell = shellInvocation(targetCommand);
48155
+ const child = useShell ? spawn2(shell.file, shell.args, {
48044
48156
  stdio: ["pipe", "pipe", "inherit"],
48045
48157
  shell: false,
48046
48158
  env: spawnEnv
@@ -48104,6 +48216,17 @@ async function runProxy(targetCommand) {
48104
48216
  child.stdin.write(line + "\n");
48105
48217
  });
48106
48218
  child.stdout.pipe(process.stdout);
48219
+ child.on("error", (err2) => {
48220
+ const what = useShell ? shell.file : executable;
48221
+ console.error(
48222
+ chalk8.red(
48223
+ err2.code === "ENOENT" ? `
48224
+ \u274C Node9 could not run "${targetCommand}": ${what} was not found on this machine.` : `
48225
+ \u274C Node9 could not run "${targetCommand}": ${err2.message}`
48226
+ )
48227
+ );
48228
+ process.exit(127);
48229
+ });
48107
48230
  child.on("exit", (code) => process.exit(code || 0));
48108
48231
  }
48109
48232
 
@@ -48249,7 +48372,7 @@ async function onboardMachine(apiKey, opts = {}) {
48249
48372
  push.ok ? { name: "register", ok: true, detail: "machine visible in the dashboard" } : { name: "register", ok: false, detail: push.reason }
48250
48373
  );
48251
48374
  const healed = ensureAutostartHealthy(!!getConfig().settings.autoStartDaemon);
48252
- const detail = healed === "repaired" ? "autostart re-enabled (survives reboot)" : isDaemonRunning() ? "running" : healed === "unsupported" ? "no background service on this platform: starts on agent activity" : "starts on agent activity";
48375
+ const detail = healed === "repaired" ? "autostart re-enabled (survives reboot)" : isDaemonRunning() ? "running" : healed === "unsupported" ? "no background service on this platform: starts on agent activity" : "starts on agent activity (make it survive reboots: node9 daemon install)";
48253
48376
  steps.push({ name: "daemon", ok: true, detail });
48254
48377
  }
48255
48378
  const required = ["credentials", "policy-sync", "register"];
@@ -50225,8 +50348,10 @@ function registerDoctorCommand(program2, version2) {
50225
50348
  `));
50226
50349
  section("Binary");
50227
50350
  try {
50228
- const locator = process.platform === "win32" ? "where node9" : "which node9";
50229
- const found = execSync(locator, { encoding: "utf-8", timeout: 3e3 }).split(/\r?\n/)[0].trim();
50351
+ const found = execSync(`${locatorCommand()} node9`, {
50352
+ encoding: "utf-8",
50353
+ timeout: 3e3
50354
+ }).split(/\r?\n/)[0].trim();
50230
50355
  pass(`node9 found at ${found}`);
50231
50356
  } catch {
50232
50357
  warn("node9 not found in $PATH \u2014 hooks may not find it", "Run: npm install -g node9-ai");
@@ -50353,7 +50478,9 @@ function registerDoctorCommand(program2, version2) {
50353
50478
  const fails = health.consecutiveFailures > 0 ? ` (${health.consecutiveFailures} consecutive failure${health.consecutiveFailures === 1 ? "" : "s"}${health.lastError ? `: ${health.lastError}` : ""})` : "";
50354
50479
  warn(
50355
50480
  `Cloud policy is STALE \u2014 ${when}${fails}. The cached policy is still enforced, but changes from the dashboard are not reaching this machine.`,
50356
- "Run: node9 policy sync (and ensure the daemon autostarts: systemctl --user enable --now node9-daemon)"
50481
+ // Platform-aware: this hint hardcoded systemctl and told a Windows
50482
+ // founder to run a Linux command (QA 2026-08-28).
50483
+ `Run: node9 policy sync (then keep it fresh across reboots \u2014 ${autostartInstallHint()})`
50357
50484
  );
50358
50485
  } else if (health.lastCheckedAt) {
50359
50486
  pass(`Cloud policy fresh \u2014 last synced ${agoLabel(health.lastCheckedAt)}`);
@@ -52095,7 +52222,7 @@ function registerInitCommand(program2) {
52095
52222
  await setupAgent(agent);
52096
52223
  console.log("");
52097
52224
  }
52098
- if ((process.platform === "darwin" || process.platform === "linux") && process.stdout.isTTY) {
52225
+ if ((process.platform === "darwin" || process.platform === "linux" || process.platform === "win32") && process.stdout.isTTY) {
52099
52226
  const alreadyInstalled = isDaemonServiceInstalled();
52100
52227
  if (!alreadyInstalled) {
52101
52228
  const { confirm: confirm4 } = await import("@inquirer/prompts");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@node9/proxy",
3
- "version": "2.2.1",
3
+ "version": "2.3.0",
4
4
  "description": "The Sudo Command for AI Agents. Execution Security for Claude Code, Codex, Gemini, Cursor, Opencode, Pi, and any MCP server.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",