svamp-cli 0.1.76 → 0.1.78

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.
@@ -296,7 +296,7 @@ Service is live:`);
296
296
  }
297
297
  } else {
298
298
  console.log(`No SANDBOX_ID detected \u2014 starting reverse tunnel.`);
299
- const { runTunnel } = await import('./tunnel-C2kqST5d.mjs');
299
+ const { runTunnel } = await import('./tunnel-BDKdemh0.mjs');
300
300
  await runTunnel(name, ports);
301
301
  }
302
302
  } catch (err) {
@@ -320,7 +320,7 @@ async function serviceServe(args) {
320
320
  if (subdomain) validateSubdomain(subdomain);
321
321
  const healthInterval = healthIntervalStr ? parseInt(healthIntervalStr, 10) : void 0;
322
322
  try {
323
- const { startStaticServer } = await import('./staticServer-B-S9sl6E.mjs');
323
+ const { startStaticServer } = await import('./staticServer-CWcmMF5V.mjs');
324
324
  const resolvedDir = require("path").resolve(directory);
325
325
  console.log(`Serving ${resolvedDir}`);
326
326
  const staticServer = await startStaticServer({
@@ -345,7 +345,7 @@ Serving ${resolvedDir}:`);
345
345
  }
346
346
  } else {
347
347
  console.log(`No SANDBOX_ID detected \u2014 starting reverse tunnel.`);
348
- const { runTunnel } = await import('./tunnel-C2kqST5d.mjs');
348
+ const { runTunnel } = await import('./tunnel-BDKdemh0.mjs');
349
349
  await runTunnel(name, ports);
350
350
  }
351
351
  const cleanup = () => {
@@ -367,7 +367,7 @@ async function serviceTunnel(args) {
367
367
  console.error("Usage: svamp service tunnel <name> --port <port> [--port <port2>]");
368
368
  process.exit(1);
369
369
  }
370
- const { runTunnel } = await import('./tunnel-C2kqST5d.mjs');
370
+ const { runTunnel } = await import('./tunnel-BDKdemh0.mjs');
371
371
  await runTunnel(name, ports);
372
372
  }
373
373
  async function handleServiceCommand() {
@@ -1,11 +1,11 @@
1
1
  import { writeFileSync, readFileSync } from 'fs';
2
2
  import { resolve } from 'path';
3
- import { connectAndGetMachine } from './commands-lJ8V7MJE.mjs';
3
+ import { connectAndGetMachine } from './commands-B6FEeZeP.mjs';
4
4
  import 'node:fs';
5
5
  import 'node:child_process';
6
6
  import 'node:path';
7
7
  import 'node:os';
8
- import './run-BnFGIK0c.mjs';
8
+ import './run-DsXDjwLW.mjs';
9
9
  import 'os';
10
10
  import 'fs/promises';
11
11
  import 'url';
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- export { c as connectToHypha, d as daemonStatus, g as getHyphaServerUrl, r as registerMachineService, a as registerSessionService, s as startDaemon, b as stopDaemon } from './run-BnFGIK0c.mjs';
1
+ export { c as connectToHypha, d as daemonStatus, g as getHyphaServerUrl, r as registerMachineService, a as registerSessionService, s as startDaemon, b as stopDaemon } from './run-DsXDjwLW.mjs';
2
2
  import 'os';
3
3
  import 'fs/promises';
4
4
  import 'fs';
@@ -1,5 +1,5 @@
1
1
  var name = "svamp-cli";
2
- var version = "0.1.76";
2
+ var version = "0.1.78";
3
3
  var description = "Svamp CLI — AI workspace daemon on Hypha Cloud";
4
4
  var author = "Amun AI AB";
5
5
  var license = "SEE LICENSE IN LICENSE";
@@ -29,7 +29,7 @@ var dependencies = {
29
29
  "@agentclientprotocol/sdk": "^0.14.1",
30
30
  "@modelcontextprotocol/sdk": "^1.25.3",
31
31
  "hypha-rpc": "0.21.34",
32
- "node-pty": "^1.1.0",
32
+ "node-pty": "1.2.0-beta.11",
33
33
  ws: "^8.18.0",
34
34
  yaml: "^2.8.2",
35
35
  zod: "^3.24.4"
@@ -2,7 +2,7 @@ import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(im
2
2
  import os from 'node:os';
3
3
  import { join, resolve } from 'node:path';
4
4
  import { mkdirSync, writeFileSync, existsSync, unlinkSync, readFileSync, watch } from 'node:fs';
5
- import { c as connectToHypha, a as registerSessionService } from './run-BnFGIK0c.mjs';
5
+ import { c as connectToHypha, a as registerSessionService } from './run-DsXDjwLW.mjs';
6
6
  import { createServer } from 'node:http';
7
7
  import { spawn } from 'node:child_process';
8
8
  import { createInterface } from 'node:readline';
@@ -309,18 +309,6 @@ async function getPtyModule() {
309
309
  function generateTerminalId() {
310
310
  return `term-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
311
311
  }
312
- const ALLOWED_CONTROL_CHARS = /* @__PURE__ */ new Set([7, 8, 9, 10, 11, 12, 13, 27]);
313
- function filterForXterm(text) {
314
- if (!text) return text;
315
- let result = "";
316
- for (let i = 0; i < text.length; i++) {
317
- const code = text.charCodeAt(i);
318
- if (code >= 32 && code <= 126 || ALLOWED_CONTROL_CHARS.has(code) || code > 127) {
319
- result += text[i];
320
- }
321
- }
322
- return result;
323
- }
324
312
  function getMachineMetadataPath(svampHomeDir) {
325
313
  return join(svampHomeDir, "machine-metadata.json");
326
314
  }
@@ -705,13 +693,25 @@ async function registerMachineService(server, machineId, metadata, daemonState,
705
693
  const cwd = params.cwd || getHomedir();
706
694
  const shell = params.shell || process.env.SHELL || "bash";
707
695
  const sessionId = generateTerminalId();
696
+ const cleanEnv = { ...process.env };
697
+ const claudeEnvVars = Object.keys(cleanEnv).filter(
698
+ (k) => k.startsWith("CLAUDE_") || k.startsWith("MCP_") || k === "ANTHROPIC_API_KEY"
699
+ );
700
+ for (const k of claudeEnvVars) delete cleanEnv[k];
708
701
  const ptyProcess = pty.spawn(shell, [], {
709
702
  name: "xterm-256color",
710
703
  cols,
711
704
  rows,
712
705
  cwd,
713
- env: { ...process.env, TERM: "xterm-256color" }
706
+ env: {
707
+ ...cleanEnv,
708
+ TERM: "xterm-256color"
709
+ }
714
710
  });
711
+ if (shell.endsWith("zsh")) {
712
+ ptyProcess.write("unsetopt PROMPT_SP\n");
713
+ ptyProcess.write("clear\n");
714
+ }
715
715
  const session = {
716
716
  id: sessionId,
717
717
  pty: ptyProcess,
@@ -723,21 +723,30 @@ async function registerMachineService(server, machineId, metadata, daemonState,
723
723
  cwd
724
724
  };
725
725
  terminalSessions.set(sessionId, session);
726
- ptyProcess.onData((data) => {
727
- const filtered = filterForXterm(data);
728
- if (!filtered) return;
729
- session.outputBuffer.push(filtered);
726
+ let outputBatch = "";
727
+ let outputTimer = null;
728
+ const flushOutput = () => {
729
+ outputTimer = null;
730
+ if (!outputBatch) return;
731
+ const batch = outputBatch;
732
+ outputBatch = "";
733
+ session.outputBuffer.push(batch);
730
734
  if (session.outputBuffer.length > 1e3) {
731
735
  session.outputBuffer.splice(0, session.outputBuffer.length - 500);
732
736
  }
733
737
  try {
734
738
  server.emit({
735
739
  type: "svamp:terminal-output",
736
- data: { type: "output", content: filtered, sessionId },
740
+ data: { type: "output", content: batch, sessionId },
737
741
  to: "*"
738
742
  });
739
743
  } catch {
740
744
  }
745
+ };
746
+ ptyProcess.onData((data) => {
747
+ if (!data) return;
748
+ outputBatch += data;
749
+ if (!outputTimer) outputTimer = setTimeout(flushOutput, 16);
741
750
  });
742
751
  ptyProcess.onExit(({ exitCode, signal }) => {
743
752
  session.exited = true;
@@ -751,6 +760,11 @@ async function registerMachineService(server, machineId, metadata, daemonState,
751
760
  });
752
761
  } catch {
753
762
  }
763
+ setTimeout(() => {
764
+ if (terminalSessions.has(sessionId)) {
765
+ terminalSessions.delete(sessionId);
766
+ }
767
+ }, 6e4);
754
768
  });
755
769
  return { sessionId, cols, rows };
756
770
  },
@@ -792,12 +806,15 @@ async function registerMachineService(server, machineId, metadata, daemonState,
792
806
  }
793
807
  return result;
794
808
  },
795
- /** Stop (kill) a terminal session. */
809
+ /** Stop (kill) a terminal session. Idempotent — returns success even if already gone. */
796
810
  terminalStop: async (params, context) => {
797
811
  authorizeRequest(context, currentMetadata.sharing, "admin");
798
812
  const session = terminalSessions.get(params.sessionId);
799
- if (!session) throw new Error(`Terminal session ${params.sessionId} not found`);
800
- session.pty.kill();
813
+ if (!session) return { success: true };
814
+ try {
815
+ session.pty.kill();
816
+ } catch {
817
+ }
801
818
  terminalSessions.delete(params.sessionId);
802
819
  return { success: true };
803
820
  },
@@ -813,6 +830,19 @@ async function registerMachineService(server, machineId, metadata, daemonState,
813
830
  exited: s.exited
814
831
  }));
815
832
  },
833
+ /** Reattach to an existing terminal session. Returns buffered output for scrollback replay. */
834
+ terminalReattach: async (params, context) => {
835
+ authorizeRequest(context, currentMetadata.sharing, "admin");
836
+ const session = terminalSessions.get(params.sessionId);
837
+ if (!session) throw new Error(`Terminal session ${params.sessionId} not found`);
838
+ const scrollback = session.outputBuffer.join("");
839
+ if (params.cols && params.rows) {
840
+ session.pty.resize(params.cols, params.rows);
841
+ session.cols = params.cols;
842
+ session.rows = params.rows;
843
+ }
844
+ return { sessionId: session.id, cols: session.cols, rows: session.rows, scrollback, exited: session.exited };
845
+ },
816
846
  // Machine-level directory listing (read-only, view role)
817
847
  listDirectory: async (path, options, context) => {
818
848
  authorizeRequest(context, currentMetadata.sharing, "view");
@@ -944,6 +974,48 @@ async function registerMachineService(server, machineId, metadata, daemonState,
944
974
  const { deleteServiceGroup } = await import('./api-BRbsyqJ4.mjs');
945
975
  return deleteServiceGroup(params.name);
946
976
  },
977
+ // ── Tunnel management ────────────────────────────────────────────
978
+ /** Start a reverse tunnel for a service group (local/cloud machine). */
979
+ tunnelStart: async (params, context) => {
980
+ authorizeRequest(context, currentMetadata.sharing, "admin");
981
+ const tunnels = handlers.tunnels;
982
+ if (!tunnels) throw new Error("Tunnel management not available");
983
+ if (tunnels.has(params.name)) throw new Error(`Tunnel '${params.name}' already running`);
984
+ const { TunnelClient } = await import('./tunnel-BDKdemh0.mjs');
985
+ const client = new TunnelClient({
986
+ name: params.name,
987
+ ports: params.ports,
988
+ maxReconnectAttempts: 0,
989
+ // infinite for daemon
990
+ onError: (err) => console.error(`[TUNNEL] ${params.name}: ${err.message}`),
991
+ onConnect: () => console.log(`[TUNNEL] ${params.name}: connected`),
992
+ onDisconnect: () => console.log(`[TUNNEL] ${params.name}: disconnected, reconnecting...`)
993
+ });
994
+ await client.connect();
995
+ tunnels.set(params.name, client);
996
+ return { name: params.name, ports: params.ports, ...client.status };
997
+ },
998
+ /** Stop a tunnel. */
999
+ tunnelStop: async (params, context) => {
1000
+ authorizeRequest(context, currentMetadata.sharing, "admin");
1001
+ const tunnels = handlers.tunnels;
1002
+ if (!tunnels) throw new Error("Tunnel management not available");
1003
+ const client = tunnels.get(params.name);
1004
+ if (!client) throw new Error(`Tunnel '${params.name}' not found`);
1005
+ client.destroy();
1006
+ tunnels.delete(params.name);
1007
+ return { name: params.name, stopped: true };
1008
+ },
1009
+ /** List active tunnels with health status. */
1010
+ tunnelList: async (context) => {
1011
+ authorizeRequest(context, currentMetadata.sharing, "view");
1012
+ const tunnels = handlers.tunnels;
1013
+ if (!tunnels) return [];
1014
+ return Array.from(tunnels.entries()).map(([name, client]) => ({
1015
+ name,
1016
+ ...client.status
1017
+ }));
1018
+ },
947
1019
  // WISE voice — create ephemeral token for OpenAI Realtime API
948
1020
  wiseCreateEphemeralToken: async (params, context) => {
949
1021
  authorizeRequest(context, currentMetadata.sharing, "interact");
@@ -1350,10 +1422,10 @@ function createSessionStore(server, sessionId, initialMetadata, initialAgentStat
1350
1422
  if (!callbacks.onListDirectory) throw new Error("listDirectory not supported");
1351
1423
  return await callbacks.onListDirectory(path);
1352
1424
  },
1353
- bash: async (command, cwd, context) => {
1425
+ bash: async (command, cwd, timeout, context) => {
1354
1426
  authorizeRequest(context, metadata.sharing, "admin");
1355
1427
  if (!callbacks.onBash) throw new Error("bash not supported");
1356
- return await callbacks.onBash(command, cwd);
1428
+ return await callbacks.onBash(command, cwd, timeout);
1357
1429
  },
1358
1430
  ripgrep: async (args, cwd, context) => {
1359
1431
  authorizeRequest(context, metadata.sharing, "admin");
@@ -1399,7 +1471,8 @@ function createSessionStore(server, sessionId, initialMetadata, initialAgentStat
1399
1471
  callbacks.onSharingUpdate?.(newSharing);
1400
1472
  return { success: true, sharing: newSharing };
1401
1473
  },
1402
- /** Update security context and restart the agent process with new rules */
1474
+ /** Update security context and restart the agent process with new rules.
1475
+ * Pass '__disable__' sentinel (from frontend) or null to disable isolation entirely. */
1403
1476
  updateSecurityContext: async (newSecurityContext, context) => {
1404
1477
  authorizeRequest(context, metadata.sharing, "admin");
1405
1478
  if (metadata.sharing && context?.user?.email && metadata.sharing.owner && context.user.email.toLowerCase() !== metadata.sharing.owner.toLowerCase()) {
@@ -1408,14 +1481,15 @@ function createSessionStore(server, sessionId, initialMetadata, initialAgentStat
1408
1481
  if (!callbacks.onUpdateSecurityContext) {
1409
1482
  throw new Error("Security context updates are not supported for this session");
1410
1483
  }
1411
- metadata = { ...metadata, securityContext: newSecurityContext };
1484
+ const resolvedContext = newSecurityContext === "__disable__" ? null : newSecurityContext;
1485
+ metadata = { ...metadata, securityContext: resolvedContext };
1412
1486
  metadataVersion++;
1413
1487
  notifyListeners({
1414
1488
  type: "update-session",
1415
1489
  sessionId,
1416
1490
  metadata: { value: metadata, version: metadataVersion }
1417
1491
  });
1418
- return await callbacks.onUpdateSecurityContext(newSecurityContext);
1492
+ return await callbacks.onUpdateSecurityContext(resolvedContext);
1419
1493
  },
1420
1494
  /** Apply a new system prompt and restart the agent process */
1421
1495
  applySystemPrompt: async (prompt, context) => {
@@ -5629,6 +5703,7 @@ async function startDaemon(options) {
5629
5703
  let server = null;
5630
5704
  const supervisor = new ProcessSupervisor(join(SVAMP_HOME, "processes"));
5631
5705
  await supervisor.init();
5706
+ const tunnels = /* @__PURE__ */ new Map();
5632
5707
  ensureAutoInstalledSkills(logger).catch(() => {
5633
5708
  });
5634
5709
  preventMachineSleep(logger);
@@ -5749,6 +5824,7 @@ async function startDaemon(options) {
5749
5824
  });
5750
5825
  }, buildIsolationConfig2 = function(dir) {
5751
5826
  if (!options2.forceIsolation && !sessionMetadata.sharing?.enabled) return null;
5827
+ if (sessionMetadata.securityContext === null) return null;
5752
5828
  const method = isolationCapabilities.preferred;
5753
5829
  if (!method) return null;
5754
5830
  const detail = isolationCapabilities.details[method];
@@ -5768,7 +5844,7 @@ async function startDaemon(options) {
5768
5844
  if (sessionMetadata.sharing?.enabled && stagedCredentials) {
5769
5845
  config.credentialStagingPath = stagedCredentials.homePath;
5770
5846
  }
5771
- const activeSecurityContext = sessionMetadata.securityContext || options2.securityContext;
5847
+ const activeSecurityContext = sessionMetadata.securityContext ?? options2.securityContext;
5772
5848
  if (activeSecurityContext) {
5773
5849
  config = applySecurityContext(config, activeSecurityContext);
5774
5850
  }
@@ -6484,6 +6560,7 @@ The automated loop has finished. Review the progress above and let me know if yo
6484
6560
  }
6485
6561
  if (claudeResumeId) {
6486
6562
  spawnClaude(void 0, { permissionMode: currentPermissionMode });
6563
+ sessionService.updateMetadata(sessionMetadata);
6487
6564
  logger.log(`[Session ${sessionId}] Claude respawned with --resume ${claudeResumeId}`);
6488
6565
  return { success: true, message: "Claude process restarted successfully." };
6489
6566
  } else {
@@ -6737,11 +6814,12 @@ The automated loop has finished. Review the progress above and let me know if yo
6737
6814
  onUpdateConfig: (patch) => {
6738
6815
  writeSvampConfigPatch(patch);
6739
6816
  },
6740
- onBash: async (command, cwd) => {
6741
- logger.log(`[Session ${sessionId}] Bash: ${command} (cwd: ${cwd || directory})`);
6817
+ onBash: async (command, cwd, timeout) => {
6818
+ const execTimeout = timeout || 12e4;
6819
+ logger.log(`[Session ${sessionId}] Bash: ${command} (cwd: ${cwd || directory}, timeout: ${execTimeout}ms)`);
6742
6820
  const { exec } = await import('child_process');
6743
6821
  return new Promise((resolve2) => {
6744
- exec(command, { cwd: cwd || directory, timeout: 3e4, maxBuffer: 1024 * 1024 }, (err, stdout, stderr) => {
6822
+ exec(command, { cwd: cwd || directory, timeout: execTimeout, maxBuffer: 1024 * 1024 }, (err, stdout, stderr) => {
6745
6823
  if (err) {
6746
6824
  resolve2({ success: false, stdout: stdout || "", stderr: stderr || err.message, exitCode: err.code ?? 1 });
6747
6825
  } else {
@@ -7170,10 +7248,11 @@ The automated loop has finished. Review the progress above and let me know if yo
7170
7248
  onUpdateConfig: (patch) => {
7171
7249
  writeSvampConfigPatchAcp(patch);
7172
7250
  },
7173
- onBash: async (command, cwd) => {
7251
+ onBash: async (command, cwd, timeout) => {
7252
+ const execTimeout = timeout || 12e4;
7174
7253
  const { exec } = await import('child_process');
7175
7254
  return new Promise((resolve2) => {
7176
- exec(command, { cwd: cwd || directory, timeout: 3e4, maxBuffer: 1024 * 1024 }, (err, stdout, stderr) => {
7255
+ exec(command, { cwd: cwd || directory, timeout: execTimeout, maxBuffer: 1024 * 1024 }, (err, stdout, stderr) => {
7177
7256
  if (err) {
7178
7257
  resolve2({ success: false, stdout: stdout || "", stderr: stderr || err.message, exitCode: err.code ?? 1 });
7179
7258
  } else {
@@ -7624,7 +7703,8 @@ The automated loop has finished. Review the progress above and let me know if yo
7624
7703
  }
7625
7704
  return ids;
7626
7705
  },
7627
- supervisor
7706
+ supervisor,
7707
+ tunnels
7628
7708
  }
7629
7709
  );
7630
7710
  logger.log(`Machine service registered: svamp-machine-${machineId}`);
@@ -8003,6 +8083,11 @@ The automated loop has finished. Review the progress above and let me know if yo
8003
8083
  }
8004
8084
  await supervisor.stopAll().catch(() => {
8005
8085
  });
8086
+ for (const [name, client] of tunnels) {
8087
+ client.destroy();
8088
+ logger.log(`Tunnel '${name}' destroyed`);
8089
+ }
8090
+ tunnels.clear();
8006
8091
  artifactSync.destroy();
8007
8092
  try {
8008
8093
  await server.disconnect();
@@ -8033,33 +8118,97 @@ The automated loop has finished. Review the progress above and let me know if yo
8033
8118
  }
8034
8119
  }
8035
8120
  async function stopDaemon(options) {
8121
+ const signal = options?.cleanup ? "SIGUSR1" : "SIGTERM";
8122
+ const mode = options?.cleanup ? "cleanup (sessions will be stopped)" : "quick (sessions preserved for auto-restore)";
8123
+ const pidsToSignal = [];
8124
+ const supervisorPidFile = join(SVAMP_HOME, "supervisor.pid");
8125
+ try {
8126
+ if (existsSync$1(supervisorPidFile)) {
8127
+ const supervisorPid = parseInt(readFileSync$1(supervisorPidFile, "utf-8").trim(), 10);
8128
+ if (supervisorPid && !isNaN(supervisorPid)) {
8129
+ try {
8130
+ process.kill(supervisorPid, 0);
8131
+ pidsToSignal.push(supervisorPid);
8132
+ } catch {
8133
+ }
8134
+ }
8135
+ }
8136
+ } catch {
8137
+ }
8036
8138
  const state = readDaemonStateFile();
8037
- if (!state) {
8139
+ if (state) {
8140
+ try {
8141
+ process.kill(state.pid, 0);
8142
+ if (!pidsToSignal.includes(state.pid)) {
8143
+ pidsToSignal.push(state.pid);
8144
+ }
8145
+ } catch {
8146
+ }
8147
+ }
8148
+ if (pidsToSignal.length === 0) {
8149
+ try {
8150
+ const { execSync } = await import('child_process');
8151
+ const pgrepOutput = execSync(
8152
+ 'pgrep -f "svamp daemon start-(supervised|sync)" 2>/dev/null || true',
8153
+ { encoding: "utf-8" }
8154
+ ).trim();
8155
+ if (pgrepOutput) {
8156
+ for (const line of pgrepOutput.split("\n")) {
8157
+ const pid = parseInt(line.trim(), 10);
8158
+ if (pid && !isNaN(pid) && pid !== process.pid) {
8159
+ pidsToSignal.push(pid);
8160
+ }
8161
+ }
8162
+ }
8163
+ } catch {
8164
+ }
8165
+ }
8166
+ if (pidsToSignal.length === 0) {
8038
8167
  console.log("No daemon running");
8168
+ cleanupDaemonStateFile();
8039
8169
  return;
8040
8170
  }
8041
- const signal = options?.cleanup ? "SIGUSR1" : "SIGTERM";
8042
- const mode = options?.cleanup ? "cleanup (sessions will be stopped)" : "quick (sessions preserved for auto-restore)";
8043
- try {
8044
- process.kill(state.pid, 0);
8045
- process.kill(state.pid, signal);
8046
- console.log(`Sent ${signal} to daemon PID ${state.pid} \u2014 ${mode}`);
8047
- for (let i = 0; i < 100; i++) {
8048
- await new Promise((r) => setTimeout(r, 100));
8171
+ for (const pid of pidsToSignal) {
8172
+ try {
8173
+ process.kill(pid, signal);
8174
+ console.log(`Sent ${signal} to PID ${pid} \u2014 ${mode}`);
8175
+ } catch {
8176
+ console.log(`PID ${pid} already gone`);
8177
+ }
8178
+ }
8179
+ pidsToSignal[0];
8180
+ for (let i = 0; i < 100; i++) {
8181
+ await new Promise((r) => setTimeout(r, 100));
8182
+ const anyAlive = pidsToSignal.some((pid) => {
8049
8183
  try {
8050
- process.kill(state.pid, 0);
8184
+ process.kill(pid, 0);
8185
+ return true;
8186
+ } catch {
8187
+ return false;
8188
+ }
8189
+ });
8190
+ if (!anyAlive) {
8191
+ console.log("Daemon stopped");
8192
+ cleanupDaemonStateFile();
8193
+ try {
8194
+ if (existsSync$1(supervisorPidFile)) await import('fs').then((fs2) => fs2.promises.unlink(supervisorPidFile));
8051
8195
  } catch {
8052
- console.log("Daemon stopped");
8053
- cleanupDaemonStateFile();
8054
- return;
8055
8196
  }
8197
+ return;
8198
+ }
8199
+ }
8200
+ console.log("Daemon did not stop in time, sending SIGKILL");
8201
+ for (const pid of pidsToSignal) {
8202
+ try {
8203
+ process.kill(pid, "SIGKILL");
8204
+ } catch {
8056
8205
  }
8057
- console.log("Daemon did not stop in time, sending SIGKILL");
8058
- process.kill(state.pid, "SIGKILL");
8059
- } catch {
8060
- console.log("Daemon is not running (stale state file)");
8061
8206
  }
8062
8207
  cleanupDaemonStateFile();
8208
+ try {
8209
+ if (existsSync$1(supervisorPidFile)) await import('fs').then((fs2) => fs2.promises.unlink(supervisorPidFile));
8210
+ } catch {
8211
+ }
8063
8212
  }
8064
8213
  function daemonStatus() {
8065
8214
  const state = readDaemonStateFile();