@neuralnomads/codenomad-dev 0.18.0-dev-20260716-40cee071 → 0.18.0-dev-20260720-4c50829d

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 (89) hide show
  1. package/dist/auth/manager.js +1 -1
  2. package/dist/filesystem/__tests__/search-cache.test.js +5 -5
  3. package/dist/filesystem/search-cache.js +1 -1
  4. package/dist/index.js +42 -0
  5. package/dist/index.test.js +40 -10
  6. package/dist/opencode-update/service.js +150 -0
  7. package/dist/opencode-update/service.test.js +107 -0
  8. package/dist/permissions/auto-accept-manager.js +165 -0
  9. package/dist/permissions/auto-accept-manager.test.js +208 -0
  10. package/dist/permissions/auto-accept-store.js +5 -2
  11. package/dist/permissions/opencode-yolo-metadata.js +81 -0
  12. package/dist/permissions/opencode-yolo-metadata.test.js +54 -0
  13. package/dist/server/__tests__/remote-proxy.test.js +64 -56
  14. package/dist/server/http-server.js +12 -15
  15. package/dist/server/remote-proxy.js +68 -98
  16. package/dist/server/routes/opencode-update.js +37 -0
  17. package/dist/server/routes/opencode-update.test.js +34 -0
  18. package/dist/server/routes/remote-servers.js +1 -1
  19. package/dist/server/routes/workspaces.test.js +38 -0
  20. package/dist/server/routes/worktrees.js +19 -0
  21. package/dist/server/routes/yolo.js +2 -1
  22. package/dist/server/tls.js +2 -2
  23. package/dist/settings/binaries.test.js +2 -7
  24. package/dist/shutdown.js +67 -59
  25. package/dist/shutdown.test.js +115 -78
  26. package/dist/ui/remote-ui.js +2 -2
  27. package/dist/usage/providers/api-key.js +3 -3
  28. package/dist/usage/providers/google.js +1 -1
  29. package/dist/usage/providers/oauth.js +1 -36
  30. package/dist/usage/providers/special.js +3 -3
  31. package/dist/usage/service.js +1 -1
  32. package/dist/usage/service.test.js +5 -3
  33. package/dist/usage/shared.js +4 -4
  34. package/dist/usage/shared.test.js +4 -4
  35. package/dist/workspaces/__tests__/spawn.test.js +2 -2
  36. package/dist/workspaces/__tests__/workspace-identity.test.js +94 -286
  37. package/dist/workspaces/binary-path.test.js +10 -0
  38. package/dist/workspaces/instance-client.js +4 -3
  39. package/dist/workspaces/launch-cleanup.test.js +21 -25
  40. package/dist/workspaces/manager.js +300 -351
  41. package/dist/workspaces/manager.test.js +209 -237
  42. package/dist/workspaces/process-identity.darwin.test.js +88 -0
  43. package/dist/workspaces/process-identity.js +172 -149
  44. package/dist/workspaces/process-identity.test.js +86 -140
  45. package/dist/workspaces/runtime.js +261 -373
  46. package/dist/workspaces/runtime.test.js +222 -547
  47. package/package.json +1 -1
  48. package/public/assets/{FilesTab-BjztpSdj.js → FilesTab-ChfE8-8N.js} +2 -2
  49. package/public/assets/{GitChangesTab-B9XraSoR.js → GitChangesTab-ntRtkxIF.js} +2 -2
  50. package/public/assets/{SplitFilePanel-Cu_5-Z-E.js → SplitFilePanel-Dav7VCqK.js} +1 -1
  51. package/public/assets/StatusTab-D3toOefp.js +1 -0
  52. package/public/assets/{align-justify-BKS9gyRk.js → align-justify-D91sAsLc.js} +1 -1
  53. package/public/assets/{bundle-full-CXGXXlzR.js → bundle-full-CWkY9-v4.js} +1 -1
  54. package/public/assets/{diff-viewer-CSNOYoEB.js → diff-viewer-Cn_41zpc.js} +1 -1
  55. package/public/assets/index-3dx-hhNQ.js +1 -0
  56. package/public/assets/index-BKwfZLzw.js +1 -0
  57. package/public/assets/index-C5IioEFT.js +1 -0
  58. package/public/assets/index-CZYc0VcU.js +2 -0
  59. package/public/assets/index-CdCoXYXP.js +1 -0
  60. package/public/assets/index-CwAVops2.js +1 -0
  61. package/public/assets/index-CzvCOwKc.js +1 -0
  62. package/public/assets/index-D-loOWPv.js +1 -0
  63. package/public/assets/{index-DVc4ls8c.js → index-DM1cpVNo.js} +1 -1
  64. package/public/assets/{index-sLe9hEfQ.js → index-DOprq6Gt.js} +1 -1
  65. package/public/assets/index-L7fX3W8k.css +1 -0
  66. package/public/assets/index-w2KdHEME.js +1 -0
  67. package/public/assets/{loading-CDmt9OJU.js → loading-BfoDcWcg.js} +1 -1
  68. package/public/assets/main-CaTCcck1.js +68 -0
  69. package/public/assets/{markdown-1ZLSxCFT.js → markdown-D33YI2g2.js} +3 -3
  70. package/public/assets/{monaco-viewer-D6JgH76W.js → monaco-viewer-DM35aobI.js} +4 -4
  71. package/public/assets/{tool-call-CyXb63LH.js → tool-call-tV85Jv2i.js} +3 -3
  72. package/public/assets/{unified-picker-DCCdSVwI.js → unified-picker-DSRT9GMh.js} +1 -1
  73. package/public/assets/{wrap-text-pNMdTIq3.js → wrap-text-Dmrn2DKo.js} +1 -1
  74. package/public/index.html +4 -4
  75. package/public/loading.html +4 -4
  76. package/public/sw.js +1 -1
  77. package/dist/workspaces/launch-cleanup.js +0 -132
  78. package/public/assets/StatusTab-D_IE79r-.js +0 -1
  79. package/public/assets/index-B3YjNCh4.js +0 -1
  80. package/public/assets/index-B3ZD2_SU.js +0 -1
  81. package/public/assets/index-Bh2ofaQo.js +0 -1
  82. package/public/assets/index-CM0c9xRZ.js +0 -1
  83. package/public/assets/index-CdDxouJl.js +0 -2
  84. package/public/assets/index-D06Y2cFq.js +0 -1
  85. package/public/assets/index-PmE51vPh.js +0 -1
  86. package/public/assets/index-Qj63IiSj.css +0 -1
  87. package/public/assets/index-VY_zwmsG.js +0 -1
  88. package/public/assets/index-qiiH7egV.js +0 -1
  89. package/public/assets/main-CTN6Wwz1.js +0 -68
@@ -1,165 +1,111 @@
1
1
  import assert from "node:assert/strict";
2
2
  import { describe, it } from "node:test";
3
3
  import { probePosixProcesses, probeWindowsProcesses, probeWslProcesses, sameProcess, signalOwnedPosixProcessGroup, signalPosixProcesses, signalWindowsProcesses, startedNoLaterThan, } from "./process-identity";
4
- function result(stdout = "", status = 0, stderr = "") {
5
- return { pid: 1, output: [null, stdout, stderr], stdout, stderr, status, signal: null };
6
- }
7
- function b64(value) {
8
- return Buffer.from(value).toString("base64");
9
- }
4
+ const output = (stdout = "", status = 0, stderr = "") => ({ pid: 1, output: [null, stdout, stderr], stdout, stderr, status, signal: null });
5
+ const spawn = (stdout, call, status = 0, stderr = "") => ((command, args) => {
6
+ if (call)
7
+ Object.assign(call, { command, args, script: command === "powershell.exe" ? args.at(-1) ?? "" : args[args.indexOf("-c") + 1] ?? "" });
8
+ return output(stdout, status, stderr);
9
+ });
10
+ const b64 = (value) => Buffer.from(value).toString("base64");
11
+ const identity = (startTime = "123456") => ({ pid: 42, parentPid: 1, groupId: 42, startTime, bootId: "boot-a", startOrder: startTime });
10
12
  describe("process identity probes", () => {
11
- it("parses Linux PID, PGID, and kernel process start ticks", () => {
12
- let invocation;
13
- const probe = probePosixProcesses(((command, args) => {
14
- invocation = { command, args };
15
- return result("42|1|42|123456|boot-a|123456\n");
16
- }), 25, "linux");
17
- assert.equal(invocation?.command, "sh");
18
- assert.ok(invocation?.args.includes("codenomad-posix-identity"));
19
- assert.equal(probe.ok, true);
20
- if (probe.ok) {
21
- assert.deepEqual(probe.processes.get(42), {
22
- pid: 42,
23
- parentPid: 1,
24
- groupId: 42,
25
- startTime: "123456",
26
- bootId: "boot-a",
27
- startOrder: "123456",
28
- });
29
- }
30
- });
31
- it("uses one process-table query on non-Linux POSIX platforms", () => {
32
- let invocation;
33
- const probe = probePosixProcesses(((command, args) => {
34
- invocation = { command, args };
35
- return result(" 42 1 42 Fri Jul 10 12:34:56 2026 /usr/bin/opencode serve\n");
36
- }), 25, "darwin");
37
- assert.equal(invocation?.command, "ps");
38
- assert.deepEqual(invocation?.args, ["-axo", "pid=,ppid=,pgid=,lstart=,command="]);
39
- assert.equal(probe.ok && probe.processes.get(42)?.startTime, "Fri Jul 10 12:34:56 2026\t/usr/bin/opencode serve");
13
+ it("parses immutable Linux identities", () => {
14
+ const call = {};
15
+ const probe = probePosixProcesses(spawn("42|1|42|123456|boot-a|123456\n", call), 25, "linux");
16
+ assert.deepEqual([call.command, call.args.includes("codenomad-posix-identity"), call.script.trimEnd().endsWith("exit 0")], ["sh", true, true]);
17
+ assert.deepEqual(probe.ok && probe.processes.get(42), identity());
40
18
  });
41
- it("round trips delimiter-heavy non-Linux POSIX command identities", () => {
19
+ it("uses one delimiter-safe process-table query on portable POSIX", () => {
20
+ const call = {};
42
21
  const command = "/opt/opencode 'pipe|value'\t\"quoted\" café";
43
22
  const start = "Fri Jul 10 12:34:56 2026";
44
- const probe = probePosixProcesses((() => result(`42 1 42 ${start} ${command}\n`)), 25, "darwin");
23
+ const probe = probePosixProcesses(spawn(`42 1 42 ${start} ${command}\n`, call), 25, "darwin");
24
+ assert.deepEqual([call.command, call.args], ["ps", ["-axo", "pid=,ppid=,pgid=,lstart=,comm="]]);
45
25
  assert.equal(probe.ok && probe.processes.get(42)?.startTime, `${start}\t${command}`);
46
26
  });
47
- it("uses delimiter-safe identities during non-Linux POSIX escalation", () => {
48
- const command = "/opt/opencode pipe|value\nnext\t'quoted'";
49
- const startTime = `Fri Jul 10 12:34:56 2026\t${command}`;
50
- const identity = { pid: 42, parentPid: 1, groupId: 42, startTime };
51
- const guarded = signalPosixProcesses((() => result(`CODENOMAD_TARGET_B64|42|1|42|${b64("Fri Jul 10 12:34:56 2026")}|${b64(command)}\nCODENOMAD_RESULT|1||1\n`)), { leader: identity, groupId: 42, members: [identity], signal: "SIGKILL" }, 25, "darwin");
52
- assert.equal(guarded.ok, true);
53
- assert.equal(guarded.ok && guarded.signaled[0]?.startTime, startTime);
27
+ it("ignores malformed unrelated portable rows but fails for a malformed requested identity", () => {
28
+ const start = "Fri Jul 10 12:34:56 2026";
29
+ const unrelated = `77 1 77 malformed identity\n42 1 42 ${start} opencode\n`;
30
+ const filtered = probePosixProcesses(spawn(unrelated), 25, "darwin", { pids: [42], groupId: 42 });
31
+ assert.equal(filtered.ok && filtered.processes.get(42)?.startTime, `${start}\topencode`);
32
+ assert.equal(probePosixProcesses(spawn(unrelated), 25, "darwin", { pids: [77] }).ok, false);
54
33
  });
55
- it("signals and rescans a still-owned portable POSIX process group", () => {
56
- const command = "/opt/opencode pipe|value\nchild";
57
- let script = "";
58
- const cleanup = signalOwnedPosixProcessGroup(((_command, args) => {
59
- script = args[1] ?? "";
60
- return result(`CODENOMAD_TARGET_B64|42|1|42|${b64("Fri Jul 10 12:34:56 2026")}|${b64(command)}\n` +
61
- "CODENOMAD_RESULT|1||1\n");
62
- }), 42, "SIGTERM", 25);
63
- assert.equal(cleanup.ok && cleanup.matched, true);
64
- assert.equal(cleanup.ok && cleanup.signaled[0]?.startTime, `Fri Jul 10 12:34:56 2026\t${command}`);
65
- assert.ok(script.indexOf('kill "-$requested_signal"') < script.lastIndexOf("for current_pid"));
34
+ it("preserves delimiter-heavy identities through POSIX escalation and rescan", () => {
35
+ const command = "/opt/opencode pipe|value\nnext\t'quoted'";
36
+ const row = `CODENOMAD_TARGET_B64|42|1|42|${b64("Fri Jul 10 12:34:56 2026")}|${b64(command)}\nCODENOMAD_RESULT|1||1\n`;
37
+ const expected = `Fri Jul 10 12:34:56 2026\t${command}`;
38
+ const guardedCall = {};
39
+ const guarded = signalPosixProcesses(spawn(row, guardedCall), { leader: identity(expected), groupId: 42, members: [identity(expected)], signal: "SIGKILL" }, 25, "darwin");
40
+ const call = {};
41
+ const owned = signalOwnedPosixProcessGroup(spawn(row, call), 42, "SIGTERM", 25);
42
+ assert.deepEqual([guarded.ok, guarded.ok && guarded.signaled[0]?.startTime], [true, expected]);
43
+ assert.deepEqual([owned.ok && owned.matched, owned.ok && owned.signaled[0]?.startTime], [true, expected]);
44
+ assert.ok(call.script.indexOf('kill "-$requested_signal"') < call.script.lastIndexOf("for current_pid"));
45
+ assert.match(call.script, /group_pids\(\).*pid=,pgid=/);
46
+ assert.doesNotMatch(call.script, /ps -eo pid=/);
47
+ assert.match(guardedCall.script, /test "\$current_group" = "\$expected_group"/);
66
48
  });
67
- it("rejects malformed portable process rows", () => {
68
- const probe = probePosixProcesses((() => result("42 malformed process row\n")), 25, "darwin");
69
- assert.deepEqual(probe, { ok: false, error: "process identity query returned no parseable processes" });
49
+ it("marks a retained portable group request for leaderless guarded cleanup", () => {
50
+ const call = {};
51
+ const guarded = signalPosixProcesses(spawn("CODENOMAD_RESULT|1||1\n", call), {
52
+ leader: identity("gone"), groupId: 42, members: [identity("member")], signal: "SIGTERM",
53
+ allowLeaderlessGroup: true, cleanupToken: "secret-token",
54
+ }, 25, "darwin");
55
+ assert.equal(guarded.ok, true);
56
+ assert.equal(call.args[7], "1");
57
+ assert.equal(call.args[8], "secret-token");
58
+ assert.match(call.script, /anchor=0/);
59
+ assert.match(call.script, /has_cleanup_token/);
70
60
  });
71
- it("queries WSL identities inside the selected distro", () => {
72
- let invocation;
73
- const probe = probeWslProcesses(((command, args) => {
74
- invocation = { command, args };
75
- return result("99|1|99|123456|boot-a|123456\n101|99|99|123460|boot-a|123460\n");
76
- }), "Ubuntu Test", 25);
77
- assert.equal(invocation?.command, "wsl.exe");
78
- assert.deepEqual(invocation?.args.slice(0, 4), ["--distribution", "Ubuntu Test", "--exec", "sh"]);
79
- assert.ok(invocation?.args.includes("codenomad-wsl-identity"));
61
+ it("queries WSL identities in the selected distro", () => {
62
+ const call = {};
63
+ const probe = probeWslProcesses(spawn("99|1|99|123456|boot-a|123456\n101|99|99|123460|boot-a|123460\n", call), "Ubuntu Test", 25);
64
+ assert.deepEqual([call.command, call.args.slice(0, 4), call.args.includes("codenomad-wsl-identity"), call.script.trimEnd().endsWith("exit 0")], ["wsl.exe", ["--distribution", "Ubuntu Test", "--exec", "sh"], true, true]);
80
65
  assert.equal(probe.ok && probe.processes.get(101)?.startTime, "123460");
81
66
  });
82
- it("parses Windows CIM CreationDate as the immutable identity", () => {
83
- let script = "";
84
- const probe = probeWindowsProcesses(((_command, args) => {
85
- script = args.at(-1) ?? "";
86
- return result("4242|100|0|20260710123456.123456+000||20260710123456\n");
87
- }), 25);
88
- assert.match(script, /Get-CimInstance Win32_Process/);
89
- assert.match(script, /ProcessId -gt 0/);
67
+ it("uses Windows CIM CreationDate as the immutable identity", () => {
68
+ const call = {};
69
+ const probe = probeWindowsProcesses(spawn("4242|100|0|20260710123456.123456+000||20260710123456\n", call), 25);
70
+ assert.match(call.script, /Get-CimInstance Win32_Process/);
71
+ assert.match(call.script, /ProcessId -gt 0/);
90
72
  assert.equal(probe.ok && probe.processes.get(4242)?.startTime, "20260710123456.123456+000");
91
73
  });
92
- it("matches only the same numeric PID and start identity", () => {
93
- const original = { pid: 42, parentPid: 1, groupId: 42, startTime: "start-a" };
94
- assert.equal(sameProcess(original, { ...original }), true);
95
- assert.equal(sameProcess(original, { ...original, startTime: "start-b" }), false);
96
- assert.equal(sameProcess(original, { ...original, pid: 43 }), false);
74
+ it("rejects PID reuse and invalid start ordering", () => {
75
+ const original = identity("9");
76
+ for (const [candidate, expected] of [[{ ...original }, true], [{ ...original, startTime: "10" }, false], [{ ...original, pid: 43 }, false]])
77
+ assert.equal(sameProcess(original, candidate), expected);
78
+ assert.equal(startedNoLaterThan(original, "10"), true);
79
+ assert.equal(startedNoLaterThan({ ...original, startOrder: "11" }, "10"), false);
80
+ assert.equal(startedNoLaterThan({ ...original, startOrder: "Fri Jul 10" }, "10"), false);
97
81
  });
98
- it("compares Linux start ticks numerically and rejects non-numeric fallbacks", () => {
99
- const identity = { pid: 42, parentPid: 1, groupId: 42, startTime: "9", startOrder: "9" };
100
- assert.equal(startedNoLaterThan(identity, "10"), true);
101
- assert.equal(startedNoLaterThan({ ...identity, startOrder: "11" }, "10"), false);
102
- assert.equal(startedNoLaterThan({ ...identity, startOrder: "Fri Jul 10" }, "10"), false);
103
- });
104
- it("returns mismatch without scheduling a second POSIX signal command", () => {
105
- const invocations = [];
106
- const identity = {
107
- pid: 42,
108
- parentPid: 1,
109
- groupId: 42,
110
- startTime: "123456",
111
- bootId: "boot-a",
112
- startOrder: "123456",
113
- };
114
- const guarded = signalPosixProcesses(((command, args) => {
115
- invocations.push({ command, args });
116
- return result("CODENOMAD_RESULT|0||0\n");
117
- }), { leader: identity, groupId: 42, members: [identity], signal: "SIGTERM" }, 25, "linux");
82
+ it("returns a POSIX mismatch without a second signal command", () => {
83
+ const call = {};
84
+ const guarded = signalPosixProcesses(spawn("CODENOMAD_RESULT|0||0\n", call), { leader: identity(), groupId: 42, members: [identity()], signal: "SIGTERM" }, 25, "linux");
118
85
  assert.deepEqual(guarded, { ok: true, matched: false, signalSent: false, signaled: [] });
119
- assert.equal(invocations.length, 1);
120
- assert.equal(invocations[0]?.command, "sh");
121
- assert.match(invocations[0]?.args[2] ?? "", /codenomad-guarded-signal/);
122
- assert.ok(invocations[0]?.args.includes("123456"));
123
- const script = invocations[0]?.args[1] ?? "";
124
- assert.ok(script.indexOf('kill "-$requested_signal"') < script.indexOf("uptime=$(cut"));
86
+ assert.deepEqual([call.command, call.args[2], call.args.includes("123456")], ["sh", "codenomad-guarded-signal", true]);
87
+ assert.ok(call.script.indexOf('kill "-$requested_signal"') < call.script.indexOf("uptime=$(cut"));
125
88
  });
126
- it("uses one guarded Windows CIM selection and termination invocation", () => {
127
- const invocations = [];
128
- const identity = { pid: 4242, parentPid: 1, groupId: 4242, startTime: "created" };
129
- const guarded = signalWindowsProcesses(((command, args) => {
130
- invocations.push({ command, args });
131
- return result("CODENOMAD_TARGET|4242|1|0|created||99\nCODENOMAD_RESULT|1||1\n");
132
- }), { leader: identity, groupId: 4242, members: [identity], signal: "SIGKILL" }, 25);
89
+ it("selects and terminates Windows identities in one guarded CIM invocation", () => {
90
+ const call = {};
91
+ const guarded = signalWindowsProcesses(spawn("CODENOMAD_TARGET|4242|1|0|created||99\nCODENOMAD_RESULT|1||1\n", call), { leader: identity("created"), groupId: 42, members: [identity("created")], signal: "SIGKILL" }, 25);
133
92
  assert.equal(guarded.ok && guarded.matched, true);
134
- assert.equal(invocations.length, 1);
135
- assert.equal(invocations[0]?.command, "powershell.exe");
136
- const script = invocations[0]?.args.at(-1) ?? "";
137
- assert.match(script, /CreationDate/);
138
- assert.match(script, /Invoke-CimMethod -InputObject/);
139
- assert.equal(script.match(/foreach \(\$process in \$selected\)/g)?.length, 2);
140
- assert.ok(script.indexOf("CODENOMAD_TARGET|") < script.indexOf("Invoke-CimMethod"));
141
- assert.doesNotMatch(script, /taskkill/i);
93
+ assert.equal(call.command, "powershell.exe");
94
+ assert.match(call.script, /CreationDate.*Invoke-CimMethod -InputObject/s);
95
+ assert.equal(call.script.match(/foreach \(\$process in \$selected\)/g)?.length, 2);
96
+ assert.ok(call.script.indexOf("CODENOMAD_TARGET|") < call.script.indexOf("Invoke-CimMethod"));
97
+ assert.doesNotMatch(call.script, /taskkill/i);
142
98
  });
143
- it("preserves observed Windows targets when guarded termination fails partway", () => {
144
- const identity = { pid: 4242, parentPid: 1, groupId: 4242, startTime: "created" };
145
- const guarded = signalWindowsProcesses((() => result([
146
- "CODENOMAD_TARGET|4242|1|0|created||99",
147
- "CODENOMAD_TARGET|4243|4242|0|descendant||100",
148
- ].join("\n"), 1, "termination failed")), {
149
- leader: identity,
150
- groupId: 4242,
151
- members: [identity],
152
- signal: "SIGTERM",
153
- }, 25);
99
+ it("retains observed Windows identities after partial termination failure", () => {
100
+ const rows = "CODENOMAD_TARGET|4242|1|0|created||99\nCODENOMAD_TARGET|4243|4242|0|descendant||100";
101
+ const guarded = signalWindowsProcesses(spawn(rows, undefined, 1, "termination failed"), { leader: identity("created"), groupId: 42, members: [identity("created")], signal: "SIGTERM" }, 25);
154
102
  assert.equal(guarded.ok, false);
155
- assert.deepEqual(!guarded.ok && guarded.observed?.map((target) => target.pid), [4242, 4243]);
156
- });
157
- it("reports command failures without fabricating identities", () => {
158
- const probe = probeWindowsProcesses((() => result("", 1, "CIM unavailable")), 25);
159
- assert.deepEqual(probe, { ok: false, error: "CIM unavailable" });
103
+ assert.deepEqual(!guarded.ok && guarded.observed?.map(({ pid }) => pid), [4242, 4243]);
160
104
  });
161
- it("rejects successful commands with unparseable or empty output", () => {
162
- const probe = probeWslProcesses((() => result("not an identity")), "Ubuntu", 25);
163
- assert.deepEqual(probe, { ok: false, error: "process identity query returned no parseable processes" });
105
+ it("fails conservatively for command, malformed, and empty probe output", () => {
106
+ assert.deepEqual(probeWindowsProcesses(spawn("", undefined, 1, "CIM unavailable"), 25), { ok: false, error: "CIM unavailable" });
107
+ assert.deepEqual(probePosixProcesses(spawn("", undefined, 20, "proc unavailable"), 25, "linux"), { ok: false, error: "proc unavailable" });
108
+ for (const probe of [probePosixProcesses(spawn("42 malformed process row\n"), 25, "darwin"), probeWslProcesses(spawn("not an identity"), "Ubuntu", 25)])
109
+ assert.equal(probe.ok, false);
164
110
  });
165
111
  });