@adhdev/daemon-core 1.0.18-rc.19 → 1.0.18-rc.2

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 (82) hide show
  1. package/dist/cli-adapters/cli-state-engine.d.ts +0 -77
  2. package/dist/cli-adapters/provider-cli-shared.d.ts +0 -33
  3. package/dist/cli-adapters/pty-transport.d.ts +1 -2
  4. package/dist/commands/cli-manager.d.ts +0 -9
  5. package/dist/commands/upgrade-helper.d.ts +0 -1
  6. package/dist/commands/windows-atomic-upgrade.d.ts +1 -17
  7. package/dist/config/mesh-json-config.d.ts +0 -62
  8. package/dist/index.d.ts +2 -4
  9. package/dist/index.js +4143 -6578
  10. package/dist/index.js.map +1 -1
  11. package/dist/index.mjs +4138 -6570
  12. package/dist/index.mjs.map +1 -1
  13. package/dist/mesh/coordinator-prompt.d.ts +0 -76
  14. package/dist/mesh/mesh-active-work.d.ts +1 -1
  15. package/dist/mesh/mesh-ledger.d.ts +1 -28
  16. package/dist/mesh/mesh-node-identity.d.ts +0 -23
  17. package/dist/mesh/mesh-refine-gates.d.ts +0 -89
  18. package/dist/mesh/mesh-remote-event-pull.d.ts +0 -3
  19. package/dist/mesh/mesh-runtime-store.d.ts +0 -11
  20. package/dist/mesh/mesh-work-queue.d.ts +1 -24
  21. package/dist/providers/chat-message-normalization.d.ts +0 -22
  22. package/dist/providers/cli-provider-instance-types.d.ts +0 -4
  23. package/dist/providers/cli-provider-instance.d.ts +0 -78
  24. package/dist/providers/contracts.d.ts +0 -17
  25. package/dist/providers/provider-schema.d.ts +0 -1
  26. package/dist/providers/sdk/v1/builders/cli/detect-status.d.ts +0 -14
  27. package/dist/providers/types/interactive-prompt.d.ts +0 -18
  28. package/dist/repo-mesh-types.d.ts +6 -38
  29. package/dist/shared-types.d.ts +2 -4
  30. package/package.json +3 -3
  31. package/src/boot/daemon-lifecycle.ts +0 -10
  32. package/src/cli-adapters/cli-state-engine.ts +3 -220
  33. package/src/cli-adapters/provider-cli-adapter.ts +11 -41
  34. package/src/cli-adapters/provider-cli-shared.ts +0 -41
  35. package/src/cli-adapters/pty-transport.d.ts +1 -2
  36. package/src/cli-adapters/pty-transport.ts +1 -1
  37. package/src/cli-adapters/session-host-transport.ts +3 -8
  38. package/src/commands/cli-manager.ts +8 -72
  39. package/src/commands/high-family/mesh-coordinator-launch.ts +2 -17
  40. package/src/commands/high-family/mesh-events.ts +2 -13
  41. package/src/commands/med-family/mesh-crud.ts +0 -155
  42. package/src/commands/med-family/mesh-queue.ts +1 -74
  43. package/src/commands/router-refine.ts +4 -71
  44. package/src/commands/router.ts +0 -8
  45. package/src/commands/upgrade-helper.ts +82 -106
  46. package/src/commands/windows-atomic-upgrade.ts +35 -281
  47. package/src/config/mesh-json-config.ts +0 -111
  48. package/src/index.ts +1 -21
  49. package/src/mesh/coordinator-prompt.ts +11 -258
  50. package/src/mesh/mesh-active-work.ts +1 -11
  51. package/src/mesh/mesh-completion-synthesis.ts +1 -12
  52. package/src/mesh/mesh-event-classify.ts +0 -19
  53. package/src/mesh/mesh-event-forwarding.ts +6 -64
  54. package/src/mesh/mesh-events-utils.ts +0 -42
  55. package/src/mesh/mesh-ledger.ts +0 -77
  56. package/src/mesh/mesh-node-identity.ts +0 -49
  57. package/src/mesh/mesh-queue-assignment.ts +11 -210
  58. package/src/mesh/mesh-reconcile-loop.ts +3 -306
  59. package/src/mesh/mesh-refine-gates.ts +0 -300
  60. package/src/mesh/mesh-remote-event-pull.ts +47 -68
  61. package/src/mesh/mesh-runtime-store.ts +0 -21
  62. package/src/mesh/mesh-work-queue.ts +2 -85
  63. package/src/providers/chat-message-normalization.ts +0 -53
  64. package/src/providers/cli-provider-instance-types.ts +0 -53
  65. package/src/providers/cli-provider-instance.ts +15 -497
  66. package/src/providers/contracts.ts +1 -25
  67. package/src/providers/provider-schema.ts +0 -83
  68. package/src/providers/sdk/v1/builders/cli/detect-status.ts +0 -23
  69. package/src/providers/sdk/v1/builders/cli/parse-approval.ts +0 -20
  70. package/src/providers/sdk/v1/schemas/cli/provider.schema.json +0 -44
  71. package/src/providers/types/interactive-prompt.ts +0 -77
  72. package/src/repo-mesh-types.ts +12 -112
  73. package/src/session-host/managed-host.ts +0 -34
  74. package/src/shared-types.ts +1 -9
  75. package/src/status/reporter.ts +0 -1
  76. package/src/status/snapshot.ts +0 -2
  77. package/dist/commands/process-lifecycle.d.ts +0 -43
  78. package/dist/mesh/mesh-disk-retention.d.ts +0 -105
  79. package/dist/providers/auto-approve-modes.d.ts +0 -14
  80. package/src/commands/process-lifecycle.ts +0 -248
  81. package/src/mesh/mesh-disk-retention.ts +0 -370
  82. package/src/providers/auto-approve-modes.ts +0 -97
@@ -1,43 +0,0 @@
1
- export interface ProcessLifecycleOptions {
2
- platform?: NodeJS.Platform;
3
- execFileSync?: typeof import('child_process').execFileSync;
4
- }
5
- export interface OwnedProcessInfo {
6
- pid: number;
7
- commandLine: string | null;
8
- }
9
- export declare function getProcessCommandLine(pid: number, options?: ProcessLifecycleOptions): string | null;
10
- /**
11
- * Extract the script argument (the token after the node executable) from a
12
- * command line. Handles both quoted and unquoted executables/scripts.
13
- */
14
- export declare function parseNodeScriptPath(commandLine: string | null): string | null;
15
- export declare function killProcess(pid: number, options?: ProcessLifecycleOptions): boolean;
16
- export declare function waitForPidExit(pid: number, timeoutMs: number): Promise<boolean>;
17
- /**
18
- * List Node processes whose command line places them under any of the supplied
19
- * prefixes. Windows-only — the versioned-prefix lifecycle this supports does not
20
- * exist on POSIX, so the helper is a no-op there.
21
- */
22
- export declare function listOwnedNodeProcesses(options: {
23
- prefixes: readonly string[];
24
- excludePids?: readonly number[];
25
- markers?: readonly string[];
26
- } & ProcessLifecycleOptions): OwnedProcessInfo[];
27
- export declare function stopOwnedProcesses(options: {
28
- processes: readonly OwnedProcessInfo[];
29
- waitMs?: number;
30
- } & ProcessLifecycleOptions): Promise<{
31
- stopped: number;
32
- survivors: OwnedProcessInfo[];
33
- }>;
34
- export declare function stopOwnedProcessesForPrefixes(options: {
35
- prefixes: readonly string[];
36
- excludePids?: readonly number[];
37
- markers?: readonly string[];
38
- waitMs?: number;
39
- log?: (message: string) => void;
40
- } & ProcessLifecycleOptions): Promise<{
41
- stopped: number;
42
- survivors: OwnedProcessInfo[];
43
- }>;
@@ -1,105 +0,0 @@
1
- import type { SessionHostSurfaceRecordLike } from '../session-host/runtime-surface.js';
2
- import type { LocalMeshEntry } from '../repo-mesh-types.js';
3
- export declare const DAY_MS: number;
4
- /** JSONL ledger files are legacy after the SQLite ledger — 30-day lifetime. */
5
- export declare const LEDGER_JSONL_MAX_AGE_MS: number;
6
- /** Terminated session-host runtimes: conservative 14-day retention. */
7
- export declare const SESSION_HOST_RUNTIME_MAX_AGE_MS: number;
8
- /** mesh-runtime.db.bak-* backups: 7-day retention. */
9
- export declare const DB_BAK_MAX_AGE_MS: number;
10
- /** A file candidate for age-based pruning: absolute path + last-modified epoch ms. */
11
- export interface AgedFile {
12
- path: string;
13
- mtimeMs: number;
14
- }
15
- /**
16
- * PURE. Select the JSONL ledger files whose mtime is older than `maxAgeMs`
17
- * relative to `now`. A file exactly at the threshold is KEPT (strict `>`), so a
18
- * 30-day-old file survives its 30th day and is pruned on the 31st. Deterministic:
19
- * no fs access, no clock read.
20
- */
21
- export declare function selectExpiredLedgerJsonl(files: AgedFile[], now: number, maxAgeMs?: number): AgedFile[];
22
- /** A parsed session-host runtime file: its path, mtime, and the wrapped record. */
23
- export interface SessionHostRuntimeFile {
24
- path: string;
25
- mtimeMs: number;
26
- /** The `record` object from the on-disk `{ record, snapshot, updatedAt }` file. */
27
- record: SessionHostSurfaceRecordLike | null;
28
- }
29
- /**
30
- * PURE. Select the session-host runtime files safe to delete: ONLY those whose
31
- * runtime is terminated/dead (NOT a live runtime — decided by the session-host-core
32
- * SSOT `isSessionHostLiveRuntime`) AND older than `maxAgeMs`. A live runtime, or a
33
- * dead-but-recent one, is always kept. A file whose record failed to parse is treated
34
- * as NON-live but is still age-gated, so a corrupt-but-fresh file is never removed.
35
- */
36
- export declare function selectExpiredSessionHostRuntimes(files: SessionHostRuntimeFile[], now: number, maxAgeMs?: number): SessionHostRuntimeFile[];
37
- /** True for a `mesh-runtime.db.bak-*` backup filename (basename only). */
38
- export declare function isDbBackupFileName(name: string): boolean;
39
- /** PURE. Select `.bak-*` backups older than `maxAgeMs`. Strict `>` (see ledger). */
40
- export declare function selectExpiredDbBackups(files: AgedFile[], now: number, maxAgeMs?: number): AgedFile[];
41
- /** Minimal shape needed to decide whether a worktree path is orphaned. */
42
- export interface WorktreePathLike {
43
- path: string;
44
- bare?: boolean;
45
- }
46
- /** A live mesh node's known workspace paths (self workspace + repoRoot, normalized). */
47
- export interface LiveNodeWorkspaceLike {
48
- workspace?: string;
49
- repoRoot?: string;
50
- }
51
- /**
52
- * PURE. Given the worktrees git reports on disk and the set of live-node workspace
53
- * paths, return the worktrees that have NO matching live node — the orphan
54
- * cleanup_candidates.
55
- *
56
- * SAFETY:
57
- * - `mainWorktreePath` (the primary repo checkout, i.e. worktree[0]) is NEVER an
58
- * orphan — it is the base repo, not a mesh clone.
59
- * - `bare` worktrees are skipped (git's internal bookkeeping, not a node checkout).
60
- * - Matching is path-equality after trailing-separator normalization against the
61
- * union of every live node's `workspace` and `repoRoot`.
62
- * This is DETECTION ONLY — the caller signals a cleanup_candidate; it must not delete.
63
- */
64
- export declare function detectOrphanWorktrees(worktrees: WorktreePathLike[], liveNodes: LiveNodeWorkspaceLike[], mainWorktreePath: string): WorktreePathLike[];
65
- /**
66
- * Prune expired legacy JSONL ledger files under ~/.adhdev/mesh-ledger/.
67
- * Matches only `*.jsonl` (never the SQLite .db / -wal / -shm files). Returns the
68
- * count deleted. Best-effort: individual unlink failures are logged, not thrown.
69
- */
70
- export declare function pruneExpiredLedgerJsonl(now?: number): number;
71
- /**
72
- * Prune expired `mesh-runtime.db.bak-*` backups under ~/.adhdev/mesh-ledger/.
73
- * Never touches the live DB (only names matching the .bak- prefix). Returns count.
74
- */
75
- export declare function pruneExpiredDbBackups(now?: number): number;
76
- /**
77
- * Prune terminated session-host runtime files older than 14 days across every
78
- * ~/.adhdev/session-host/<app>/runtimes/ directory. A LIVE runtime is never deleted
79
- * regardless of age (isSessionHostLiveRuntime SSOT). Returns count deleted.
80
- */
81
- export declare function pruneExpiredSessionHostRuntimes(now?: number): number;
82
- /**
83
- * Run the file-deleting retention passes (JSONL ledger, DB backups, session-host
84
- * runtimes) once. Each pass is isolated so one failing pass never blocks the others.
85
- * Orphan-worktree DETECTION is driven separately in the reconcile loop (it needs the
86
- * live mesh config + git worktree list and emits a ledger signal rather than deleting).
87
- */
88
- export declare function runDiskRetentionSweep(now?: number): {
89
- ledgerJsonl: number;
90
- dbBackups: number;
91
- sessionHostRuntimes: number;
92
- };
93
- /**
94
- * Detect orphaned worktrees for ONE mesh and emit a `worktree_cleanup_candidate`
95
- * ledger signal for each — DETECTION ONLY, never deletes. An orphan is a git worktree
96
- * on disk with no matching live mesh node (compared by path). It:
97
- * 1. picks a base (non-worktree) node owned by this mesh to anchor `git worktree list`;
98
- * 2. diffs the reported worktrees against the union of every node's workspace/repoRoot;
99
- * 3. skips the main worktree + bare entries (detectOrphanWorktrees safety);
100
- * 4. suppresses a repeat for a worktreePath already signalled within the recent window
101
- * (idempotent re-emit guard), so the hourly sweep doesn't spam the ledger.
102
- * Returns the list of newly-signalled orphan paths. Best-effort: git/ledger failures are
103
- * logged and yield an empty result, never thrown.
104
- */
105
- export declare function detectAndSignalOrphanWorktrees(mesh: LocalMeshEntry, now?: number): Promise<string[]>;
@@ -1,14 +0,0 @@
1
- import type { AutoApproveMode, AutoApproveModeRisk, AutoApproveModeStrategy, ProviderModule } from './contracts.js';
2
- export interface ResolvedAutoApproveMode {
3
- active: boolean;
4
- strategy: AutoApproveModeStrategy;
5
- modeId: string;
6
- }
7
- /** Runtime defense-in-depth for provider definitions that bypass schema validation. */
8
- export declare function deriveAutoApproveModeRisk(mode: Pick<AutoApproveMode, 'risk' | 'launchArgs'>): AutoApproveModeRisk;
9
- /**
10
- * Resolve new mode settings before the legacy boolean. A stale/unknown explicit
11
- * mode id fails closed instead of falling through to an enabled legacy setting.
12
- */
13
- export declare function resolveProviderAutoApproveMode(provider: ProviderModule, settings: Record<string, unknown> | undefined): ResolvedAutoApproveMode;
14
- export declare function findProviderAutoApproveMode(provider: ProviderModule | undefined, modeId: string): AutoApproveMode | undefined;
@@ -1,248 +0,0 @@
1
- import { execFileSync, type ExecFileSyncOptions } from 'child_process';
2
- import * as path from 'path';
3
-
4
- export interface ProcessLifecycleOptions {
5
- platform?: NodeJS.Platform;
6
- execFileSync?: typeof import('child_process').execFileSync;
7
- }
8
-
9
- export interface OwnedProcessInfo {
10
- pid: number;
11
- commandLine: string | null;
12
- }
13
-
14
- function defaultExecFileSync(): typeof import('child_process').execFileSync {
15
- return execFileSync;
16
- }
17
-
18
- function getWindowsProcessCommandLine(
19
- pid: number,
20
- exec: typeof import('child_process').execFileSync,
21
- ): string | null {
22
- const pidFilter = `ProcessId=${pid}`;
23
-
24
- try {
25
- const psOut = exec('powershell.exe', [
26
- '-NoProfile',
27
- '-NonInteractive',
28
- '-ExecutionPolicy', 'Bypass',
29
- '-Command',
30
- `(Get-CimInstance Win32_Process -Filter "${pidFilter}").CommandLine`,
31
- ], { encoding: 'utf8', timeout: 5000, stdio: ['ignore', 'pipe', 'ignore'], windowsHide: true });
32
- const text = String(psOut).trim();
33
- if (text) return text;
34
- } catch {
35
- // fall through to wmic fallback
36
- }
37
-
38
- try {
39
- const wmicOut = exec('wmic', [
40
- 'process', 'where', pidFilter, 'get', 'CommandLine',
41
- ], { encoding: 'utf8', timeout: 3000, stdio: ['ignore', 'pipe', 'ignore'], windowsHide: true });
42
- const text = String(wmicOut).trim();
43
- if (text) return text;
44
- } catch {
45
- // noop
46
- }
47
-
48
- return null;
49
- }
50
-
51
- export function getProcessCommandLine(
52
- pid: number,
53
- options: ProcessLifecycleOptions = {},
54
- ): string | null {
55
- if (!Number.isFinite(pid) || pid <= 0) return null;
56
- const exec = options.execFileSync ?? defaultExecFileSync();
57
- const platform = options.platform ?? process.platform;
58
-
59
- if (platform === 'win32') {
60
- return getWindowsProcessCommandLine(pid, exec);
61
- }
62
-
63
- try {
64
- const text = String(exec('ps', ['-o', 'command=', '-p', String(pid)], {
65
- encoding: 'utf8',
66
- timeout: 3000,
67
- stdio: ['ignore', 'pipe', 'ignore'],
68
- })).trim();
69
- return text || null;
70
- } catch {
71
- return null;
72
- }
73
- }
74
-
75
- /**
76
- * Extract the script argument (the token after the node executable) from a
77
- * command line. Handles both quoted and unquoted executables/scripts.
78
- */
79
- export function parseNodeScriptPath(commandLine: string | null): string | null {
80
- if (!commandLine) return null;
81
- let rest = commandLine.trim();
82
-
83
- // Skip the leading executable token.
84
- if (rest.startsWith('"')) {
85
- const end = rest.indexOf('"', 1);
86
- if (end === -1) return null;
87
- rest = rest.slice(end + 1).trim();
88
- } else {
89
- const idx = rest.search(/\s/);
90
- if (idx === -1) return null;
91
- rest = rest.slice(idx + 1).trim();
92
- }
93
- if (!rest) return null;
94
-
95
- if (rest.startsWith('"')) {
96
- const end = rest.indexOf('"', 1);
97
- return end === -1 ? rest.slice(1) : rest.slice(1, end);
98
- }
99
- const idx = rest.search(/\s/);
100
- return idx === -1 ? rest : rest.slice(0, idx);
101
- }
102
-
103
- function normalizeWindowsPath(value: string): string {
104
- return value.toLowerCase().replace(/\//g, '\\').replace(/\\+$/, '');
105
- }
106
-
107
- function isCommandLineUnderPrefix(commandLine: string | null, prefix: string): boolean {
108
- if (!commandLine) return false;
109
- const needle = normalizeWindowsPath(prefix);
110
- // Command-line paths may quote Windows separators as either single or
111
- // doubled backslashes; collapse doubles before matching.
112
- const haystack = commandLine.split('\\\\').join('\\').toLowerCase();
113
- return haystack.includes(needle);
114
- }
115
-
116
- export function killProcess(pid: number, options: ProcessLifecycleOptions = {}): boolean {
117
- if (!Number.isFinite(pid) || pid <= 0) return false;
118
- const exec = options.execFileSync ?? defaultExecFileSync();
119
- const platform = options.platform ?? process.platform;
120
-
121
- try {
122
- if (platform === 'win32') {
123
- exec('taskkill', ['/PID', String(pid), '/T', '/F'], {
124
- stdio: 'ignore',
125
- windowsHide: true,
126
- });
127
- } else {
128
- process.kill(pid, 'SIGTERM');
129
- }
130
- return true;
131
- } catch {
132
- return false;
133
- }
134
- }
135
-
136
- export async function waitForPidExit(pid: number, timeoutMs: number): Promise<boolean> {
137
- const start = Date.now();
138
- while (Date.now() - start < timeoutMs) {
139
- try {
140
- process.kill(pid, 0);
141
- await new Promise((resolve) => setTimeout(resolve, 250));
142
- } catch {
143
- return true;
144
- }
145
- }
146
- return false;
147
- }
148
-
149
- /**
150
- * List Node processes whose command line places them under any of the supplied
151
- * prefixes. Windows-only — the versioned-prefix lifecycle this supports does not
152
- * exist on POSIX, so the helper is a no-op there.
153
- */
154
- export function listOwnedNodeProcesses(options: {
155
- prefixes: readonly string[];
156
- excludePids?: readonly number[];
157
- markers?: readonly string[];
158
- } & ProcessLifecycleOptions): OwnedProcessInfo[] {
159
- const platform = options.platform ?? process.platform;
160
- if (platform !== 'win32') return [];
161
-
162
- const exec = options.execFileSync ?? defaultExecFileSync();
163
- const prefixes = options.prefixes.map((p) => normalizeWindowsPath(p));
164
- const exclude = new Set((options.excludePids ?? []).filter((n) => Number.isFinite(n) && n > 0));
165
- // Command-line paths use Windows separators; normalize marker separators the
166
- // same way so a marker like "dist/cli/index.js" matches "dist\cli\index.js".
167
- const markers = (options.markers ?? []).map((m) => m.toLowerCase().replace(/\//g, '\\'));
168
-
169
- let pids: number[] = [];
170
- try {
171
- const out = String(exec('powershell.exe', [
172
- '-NoProfile',
173
- '-NonInteractive',
174
- '-ExecutionPolicy', 'Bypass',
175
- '-Command',
176
- 'Get-Process node -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Id | ConvertTo-Json -Compress',
177
- ], { encoding: 'utf8', timeout: 8000, stdio: ['ignore', 'pipe', 'ignore'], windowsHide: true })).trim();
178
- if (out) {
179
- const parsed = JSON.parse(out);
180
- pids = Array.isArray(parsed) ? parsed : [parsed];
181
- }
182
- } catch {
183
- return [];
184
- }
185
-
186
- const results: OwnedProcessInfo[] = [];
187
- for (const pid of pids) {
188
- if (!Number.isFinite(pid) || pid <= 0 || exclude.has(pid)) continue;
189
- const commandLine = getProcessCommandLine(pid, { platform, execFileSync: exec });
190
- if (!commandLine) continue;
191
- const lower = commandLine.toLowerCase();
192
- const underPrefix = prefixes.some((prefix) => lower.includes(prefix));
193
- if (!underPrefix) continue;
194
- if (markers.length > 0 && !markers.some((marker) => lower.includes(marker))) continue;
195
- results.push({ pid, commandLine });
196
- }
197
- return results;
198
- }
199
-
200
- export async function stopOwnedProcesses(options: {
201
- processes: readonly OwnedProcessInfo[];
202
- waitMs?: number;
203
- } & ProcessLifecycleOptions): Promise<{ stopped: number; survivors: OwnedProcessInfo[] }> {
204
- const waitMs = options.waitMs ?? 15_000;
205
- const killed = new Set<number>();
206
-
207
- for (const p of options.processes) {
208
- if (killProcess(p.pid, options)) killed.add(p.pid);
209
- }
210
-
211
- const survivors: OwnedProcessInfo[] = [];
212
- for (const p of options.processes) {
213
- if (!killed.has(p.pid)) {
214
- survivors.push(p);
215
- continue;
216
- }
217
- const exited = await waitForPidExit(p.pid, waitMs);
218
- if (!exited) survivors.push(p);
219
- }
220
-
221
- // A process counts as stopped only when it left the process table; a failed
222
- // kill or a post-kill timeout both land it in `survivors`.
223
- return { stopped: options.processes.length - survivors.length, survivors };
224
- }
225
-
226
- export async function stopOwnedProcessesForPrefixes(options: {
227
- prefixes: readonly string[];
228
- excludePids?: readonly number[];
229
- markers?: readonly string[];
230
- waitMs?: number;
231
- log?: (message: string) => void;
232
- } & ProcessLifecycleOptions): Promise<{ stopped: number; survivors: OwnedProcessInfo[] }> {
233
- const processes = listOwnedNodeProcesses(options);
234
- if (processes.length === 0) return { stopped: 0, survivors: [] };
235
-
236
- options.log?.(`Stopping ${processes.length} owned process(es) under prefixes: ${options.prefixes.join(', ')}`);
237
- const result = await stopOwnedProcesses({
238
- processes,
239
- waitMs: options.waitMs,
240
- platform: options.platform,
241
- execFileSync: options.execFileSync,
242
- });
243
-
244
- if (result.survivors.length > 0) {
245
- options.log?.(`Could not stop ${result.survivors.length} owned process(es): ${result.survivors.map((s) => s.pid).join(', ')}`);
246
- }
247
- return result;
248
- }