@moxt-ai/mobius 0.0.195 → 0.0.196

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 (60) hide show
  1. package/dist/acp/agent-connection.d.ts.map +1 -1
  2. package/dist/acp/agent-connection.js +4 -3
  3. package/dist/acp/agent-connection.js.map +1 -1
  4. package/dist/acp/codex/app-server-client.d.ts.map +1 -1
  5. package/dist/acp/codex/app-server-client.js +4 -2
  6. package/dist/acp/codex/app-server-client.js.map +1 -1
  7. package/dist/agent-catalog/acp-readiness-probe.d.ts.map +1 -1
  8. package/dist/agent-catalog/acp-readiness-probe.js +4 -3
  9. package/dist/agent-catalog/acp-readiness-probe.js.map +1 -1
  10. package/dist/cli.js +526 -290
  11. package/dist/cli.js.map +4 -4
  12. package/dist/daemon-manager.d.ts.map +1 -1
  13. package/dist/daemon-manager.js +7 -17
  14. package/dist/daemon-manager.js.map +1 -1
  15. package/dist/filesystem/path-error.d.ts +5 -0
  16. package/dist/filesystem/path-error.d.ts.map +1 -0
  17. package/dist/filesystem/path-error.js +8 -0
  18. package/dist/filesystem/path-error.js.map +1 -0
  19. package/dist/filesystem/private-path.d.ts +3 -0
  20. package/dist/filesystem/private-path.d.ts.map +1 -0
  21. package/dist/filesystem/private-path.js +550 -0
  22. package/dist/filesystem/private-path.js.map +7 -0
  23. package/dist/index.js +498 -253
  24. package/dist/index.js.map +4 -4
  25. package/dist/native-sessions/codex-history.d.ts.map +1 -1
  26. package/dist/native-sessions/codex-history.js +4 -3
  27. package/dist/native-sessions/codex-history.js.map +1 -1
  28. package/dist/platform/contracts.d.ts +21 -0
  29. package/dist/platform/contracts.d.ts.map +1 -1
  30. package/dist/platform/index.d.ts.map +1 -1
  31. package/dist/platform/index.js +5 -3
  32. package/dist/platform/index.js.map +1 -1
  33. package/dist/platform/linux.d.ts.map +1 -1
  34. package/dist/platform/linux.js +2 -1
  35. package/dist/platform/linux.js.map +1 -1
  36. package/dist/platform/macos.d.ts.map +1 -1
  37. package/dist/platform/macos.js +2 -1
  38. package/dist/platform/macos.js.map +1 -1
  39. package/dist/platform/posix.d.ts +28 -1
  40. package/dist/platform/posix.d.ts.map +1 -1
  41. package/dist/platform/posix.js +91 -1
  42. package/dist/platform/posix.js.map +1 -1
  43. package/dist/platform/windows.d.ts +3 -0
  44. package/dist/platform/windows.d.ts.map +1 -0
  45. package/dist/platform/windows.js +174 -0
  46. package/dist/platform/windows.js.map +1 -0
  47. package/dist/process/executable.d.ts +7 -5
  48. package/dist/process/executable.d.ts.map +1 -1
  49. package/dist/process/executable.js +844 -386
  50. package/dist/process/executable.js.map +7 -1
  51. package/dist/process/process-error.d.ts +5 -0
  52. package/dist/process/process-error.d.ts.map +1 -0
  53. package/dist/process/process-error.js +9 -0
  54. package/dist/process/process-error.js.map +1 -0
  55. package/dist/runtime.js +498 -253
  56. package/dist/runtime.js.map +4 -4
  57. package/dist/storage/runtime-installation-store.d.ts.map +1 -1
  58. package/dist/storage/runtime-installation-store.js +3 -0
  59. package/dist/storage/runtime-installation-store.js.map +1 -1
  60. package/package.json +72 -70
@@ -1,414 +1,872 @@
1
+ // src/process/executable.ts
1
2
  import { spawn } from "node:child_process";
3
+ import { stat as stat2 } from "node:fs/promises";
4
+ import { join as join5, resolve as resolve2 } from "node:path";
5
+
6
+ // src/platform/linux.ts
7
+ import { constants as constants2, readdirSync, readFileSync } from "node:fs";
8
+ import { access as access2, readdir, readFile, realpath as realpath2, stat } from "node:fs/promises";
9
+ import { join as join2 } from "node:path";
10
+
11
+ // src/platform/posix.ts
12
+ import { execFile } from "node:child_process";
2
13
  import { constants } from "node:fs";
3
- import { access, realpath, stat } from "node:fs/promises";
4
- import { join, resolve } from "node:path";
5
- import { platformSupportFor } from "../platform/index.js";
6
- const hostPlatform = platformSupportFor(process.platform);
7
- const DEFAULT_OUTPUT_LIMIT_BYTES = 16 * 1024;
8
- const PROCESS_GRACEFUL_STOP_TIMEOUT_MILLISECONDS = 1_000;
9
- const PROCESS_FORCED_STOP_TIMEOUT_MILLISECONDS = 5_000;
10
- const PROCESS_EXIT_CHECK_ATTEMPTS = 16;
11
- const GRACEFUL_STOP_SIGNAL = 15;
12
- const FORCED_STOP_SIGNAL = 9;
13
- export class ExecutableIdentity {
14
- changedAtMilliseconds;
15
- device;
16
- inode;
17
- realPath;
18
- requestedPath;
19
- size;
20
- constructor(requestedPath, realPath_, device, inode, size, changedAtMilliseconds) {
21
- this.changedAtMilliseconds = changedAtMilliseconds;
22
- this.device = device;
23
- this.inode = inode;
24
- this.realPath = realPath_;
25
- this.requestedPath = requestedPath;
26
- this.size = size;
27
- }
28
- matches = (candidate) => this.requestedPath === candidate.requestedPath &&
29
- this.realPath === candidate.realPath &&
30
- this.device === candidate.device &&
31
- this.inode === candidate.inode &&
32
- this.size === candidate.size &&
33
- this.changedAtMilliseconds === candidate.changedAtMilliseconds;
34
- }
35
- export class ResolvedExecutable {
36
- identity;
37
- path;
38
- constructor(identity) {
39
- this.identity = identity;
40
- this.path = identity.realPath;
41
- }
42
- }
43
- export class ProcessExecutionError extends Error {
44
- code;
45
- constructor(code, message, options = {}) {
46
- super(message, options);
47
- this.code = code;
48
- this.name = "ProcessExecutionError";
49
- }
50
- }
51
- export class ProcessResult {
52
- stderr;
53
- stdout;
54
- constructor(stdout, stderr) {
55
- this.stderr = stderr;
56
- this.stdout = stdout;
57
- }
58
- }
59
- function executableExtensions(executableName, environment, platform) {
60
- if (platform !== "win32") {
61
- return [""];
62
- }
63
- if (/\.[a-zA-Z0-9]+$/.test(executableName)) {
64
- return [""];
65
- }
66
- const configured = environment["PATHEXT"] ?? ".COM;.EXE;.BAT;.CMD";
67
- const extensions = configured
68
- .split(";")
69
- .map((extension) => extension.trim())
70
- .filter((extension) => extension.length > 0)
71
- .map((extension) => (extension.startsWith(".") ? extension : `.${extension}`));
72
- return extensions.length > 0 ? extensions : [".EXE"];
73
- }
74
- export async function resolveExecutablePath(requestedPath, platform = process.platform) {
75
- const absoluteRequestedPath = resolve(requestedPath);
14
+ import { access, chmod, lstat, realpath, unlink } from "node:fs/promises";
15
+ import { tmpdir } from "node:os";
16
+ import { join } from "node:path";
17
+
18
+ // src/filesystem/path-error.ts
19
+ var LocalPathError = class extends Error {
20
+ code = "unsafe_local_path";
21
+ constructor(message, options = {}) {
22
+ super(message, options);
23
+ this.name = "LocalPathError";
24
+ }
25
+ };
26
+
27
+ // src/platform/posix.ts
28
+ var PROCESS_TABLE_TIMEOUT_MILLISECONDS = 1e3;
29
+ var PROCESS_TABLE_OUTPUT_LIMIT_BYTES = 1024 * 1024;
30
+ function parseProcessTable(output) {
31
+ const entries = [];
32
+ for (const line of output.split("\n")) {
33
+ const groups = /^\s*(?<pid>\d+)\s+(?<ppid>\d+)\s+(?<pgid>\d+)\s*$/.exec(line)?.groups;
34
+ if (groups === void 0) {
35
+ continue;
36
+ }
37
+ const processId = Number(groups["pid"]);
38
+ const parentProcessId = Number(groups["ppid"]);
39
+ const processGroupId = Number(groups["pgid"]);
40
+ if (!Number.isSafeInteger(processId) || processId <= 0 || !Number.isSafeInteger(parentProcessId) || parentProcessId < 0 || !Number.isSafeInteger(processGroupId) || processGroupId <= 0) {
41
+ continue;
42
+ }
43
+ entries.push({ parentProcessId, processGroupId, processId });
44
+ }
45
+ return entries;
46
+ }
47
+ function readProcessTableWithPs(executable) {
48
+ return new Promise((resolveTable) => {
49
+ execFile(
50
+ executable,
51
+ ["-axo", "pid=,ppid=,pgid="],
52
+ {
53
+ encoding: "utf8",
54
+ maxBuffer: PROCESS_TABLE_OUTPUT_LIMIT_BYTES,
55
+ signal: AbortSignal.timeout(PROCESS_TABLE_TIMEOUT_MILLISECONDS),
56
+ timeout: PROCESS_TABLE_TIMEOUT_MILLISECONDS
57
+ },
58
+ (error, stdout) => {
59
+ resolveTable(error === null ? parseProcessTable(stdout) : []);
60
+ }
61
+ );
62
+ });
63
+ }
64
+ function processExists(processId) {
65
+ try {
66
+ process.kill(processId, 0);
67
+ return true;
68
+ } catch {
69
+ return false;
70
+ }
71
+ }
72
+ function signalCapturedProcesses(processes, rootProcessId, signal) {
73
+ const processIds = new Set(processes.map((identity) => identity.processId));
74
+ const signalledGroups = /* @__PURE__ */ new Set();
75
+ for (const identity of processes) {
76
+ const isRootProcessGroup = identity.processGroupId === rootProcessId;
77
+ if (!isRootProcessGroup && !processIds.has(identity.processGroupId) || signalledGroups.has(identity.processGroupId)) {
78
+ continue;
79
+ }
76
80
  try {
77
- const resolvedPath = await realpath(absoluteRequestedPath);
78
- const metadata = await stat(resolvedPath);
79
- if (!metadata.isFile()) {
80
- return null;
81
- }
82
- if (platform !== "win32") {
83
- await access(resolvedPath, constants.X_OK);
84
- }
85
- return new ResolvedExecutable(new ExecutableIdentity(absoluteRequestedPath, resolvedPath, metadata.dev, metadata.ino, metadata.size, metadata.ctimeMs));
86
- }
87
- catch {
88
- return null;
89
- }
90
- }
91
- export async function findExecutableOnPath(executableName, environment, platform = process.platform) {
92
- const encodedPath = environment["PATH"] ?? "";
93
- const separator = platform === "win32" ? ";" : ":";
94
- const extensions = executableExtensions(executableName, environment, platform);
95
- for (const entry of encodedPath.split(separator)) {
96
- if (entry.length === 0) {
97
- continue;
98
- }
99
- for (const extension of extensions) {
100
- const requestedPath = resolve(join(entry, `${executableName}${extension}`));
101
- const executable = await resolveExecutablePath(requestedPath, platform);
102
- if (executable !== null) {
103
- return executable;
104
- }
105
- }
81
+ process.kill(-identity.processGroupId, signal);
82
+ signalledGroups.add(identity.processGroupId);
83
+ } catch {
106
84
  }
107
- return null;
85
+ }
86
+ for (const identity of processes) {
87
+ if (identity.processId === rootProcessId || identity.processGroupId === rootProcessId || signalledGroups.has(identity.processGroupId)) {
88
+ continue;
89
+ }
90
+ try {
91
+ process.kill(identity.processId, signal);
92
+ } catch {
93
+ }
94
+ }
108
95
  }
109
- export async function revalidateExecutable(identity, platform = process.platform) {
110
- const current = await resolveExecutablePath(identity.requestedPath, platform);
111
- return current !== null && identity.matches(current.identity);
96
+ function terminateProcess(child, signal) {
97
+ if (child.exitCode !== null || child.signalCode !== null || child.pid === void 0) {
98
+ return;
99
+ }
100
+ try {
101
+ process.kill(-child.pid, signal);
102
+ return;
103
+ } catch {
104
+ }
105
+ child.kill(signal);
112
106
  }
113
- function appendBounded(chunks, chunk, currentBytes, limitBytes) {
114
- const nextBytes = currentBytes + chunk.byteLength;
115
- if (nextBytes > limitBytes) {
116
- throw new ProcessExecutionError("process_output_limit", "The executable produced too much output");
107
+ async function removeControlSocket(path) {
108
+ try {
109
+ await unlink(path);
110
+ } catch (cause) {
111
+ if (!(cause instanceof Error) || !("code" in cause) || cause.code !== "ENOENT") {
112
+ throw cause;
113
+ }
114
+ }
115
+ }
116
+ var posixOperations = {
117
+ controlSocketPath: (installationHash) => join(tmpdir(), `mobius-${installationHash}.sock`),
118
+ prepareControlSocket: removeControlSocket,
119
+ secureControlSocket: async (path) => {
120
+ await chmod(path, 384);
121
+ },
122
+ removeControlSocket,
123
+ pathDelimiter: ":",
124
+ environmentValue: (environment, name) => environment[name],
125
+ executableExtensions: () => [""],
126
+ resolveExecutableTarget: async (path) => {
127
+ const target = await realpath(path);
128
+ await access(target, constants.X_OK);
129
+ return target;
130
+ },
131
+ executableLaunch: (command, args) => ({ command, arguments: args }),
132
+ spawnOptions: { detached: true, windowsHide: true },
133
+ signalCapturedProcesses,
134
+ terminateProcess,
135
+ validatePathPermissions: async (path, directory, privateAccess, signal) => {
136
+ signal.throwIfAborted();
137
+ const metadata = await lstat(path);
138
+ if (process.getuid === void 0 || metadata.uid !== process.getuid()) {
139
+ throw new LocalPathError("Path is not user-owned");
140
+ }
141
+ if (privateAccess) {
142
+ await chmod(path, directory ? 448 : 384);
143
+ }
144
+ signal.throwIfAborted();
145
+ }
146
+ };
147
+
148
+ // src/platform/linux.ts
149
+ function appendLinuxProcessTableEntry(entries, processId, encoded) {
150
+ const closingParenthesis = encoded.lastIndexOf(")");
151
+ if (!encoded.startsWith(`${processId.toString()} (`) || closingParenthesis < 0) {
152
+ return;
153
+ }
154
+ const fields = encoded.slice(closingParenthesis + 1).trim().split(/\s+/);
155
+ const parentProcessId = Number(fields[1]);
156
+ const processGroupId = Number(fields[2]);
157
+ if (!Number.isSafeInteger(parentProcessId) || parentProcessId < 0 || !Number.isSafeInteger(processGroupId) || processGroupId <= 0) {
158
+ return;
159
+ }
160
+ entries.push({ parentProcessId, processGroupId, processId });
161
+ }
162
+ async function readLinuxProcessTable() {
163
+ const entries = [];
164
+ try {
165
+ const directories = await readdir("/proc", { withFileTypes: true });
166
+ for (const directory of directories) {
167
+ if (!directory.isDirectory() || !/^\d+$/.test(directory.name)) {
168
+ continue;
169
+ }
170
+ const processId = Number(directory.name);
171
+ if (!Number.isSafeInteger(processId) || processId <= 0) {
172
+ continue;
173
+ }
174
+ try {
175
+ appendLinuxProcessTableEntry(
176
+ entries,
177
+ processId,
178
+ await readFile(join2("/proc", directory.name, "stat"), "utf8")
179
+ );
180
+ } catch {
181
+ }
182
+ }
183
+ } catch {
184
+ }
185
+ return entries;
186
+ }
187
+ function readLinuxProcessTableSynchronously() {
188
+ const entries = [];
189
+ try {
190
+ const directories = readdirSync("/proc", { withFileTypes: true });
191
+ for (const directory of directories) {
192
+ if (!directory.isDirectory() || !/^\d+$/.test(directory.name)) {
193
+ continue;
194
+ }
195
+ const processId = Number(directory.name);
196
+ if (!Number.isSafeInteger(processId) || processId <= 0) {
197
+ continue;
198
+ }
199
+ try {
200
+ appendLinuxProcessTableEntry(
201
+ entries,
202
+ processId,
203
+ readFileSync(join2("/proc", directory.name, "stat"), "utf8")
204
+ );
205
+ } catch {
206
+ }
207
+ }
208
+ } catch {
209
+ }
210
+ return entries;
211
+ }
212
+ async function readLinuxProcessTableWithFallback() {
213
+ const procfsEntries = await readLinuxProcessTable();
214
+ if (procfsEntries.length > 0) {
215
+ return procfsEntries;
216
+ }
217
+ for (const candidate of ["/usr/bin/ps", "/bin/ps", "/run/current-system/sw/bin/ps"]) {
218
+ try {
219
+ const resolvedPath = await realpath2(candidate);
220
+ if (!(await stat(resolvedPath)).isFile()) {
221
+ continue;
222
+ }
223
+ await access2(resolvedPath, constants2.X_OK);
224
+ } catch {
225
+ continue;
226
+ }
227
+ const entries = await readProcessTableWithPs(candidate);
228
+ if (entries.length > 0) {
229
+ return entries;
117
230
  }
118
- chunks.push(chunk);
119
- return nextBytes;
231
+ }
232
+ return [];
233
+ }
234
+ function processExists2(processId) {
235
+ try {
236
+ const encoded = readFileSync(join2("/proc", processId.toString(), "stat"), "utf8");
237
+ const closingParenthesis = encoded.lastIndexOf(")");
238
+ if (closingParenthesis >= 0) {
239
+ const processState = encoded.slice(closingParenthesis + 1).trimStart().charAt(0);
240
+ if (processState === "Z" || processState === "X") {
241
+ return false;
242
+ }
243
+ }
244
+ } catch {
245
+ }
246
+ return processExists(processId);
247
+ }
248
+ var linuxPlatform = {
249
+ ...posixOperations,
250
+ readProcessTable: readLinuxProcessTableWithFallback,
251
+ readProcessTableSynchronously: readLinuxProcessTableSynchronously,
252
+ processExists: processExists2,
253
+ workBuddyExecutablePaths: () => [],
254
+ openCodeArchives: {
255
+ arm64: {
256
+ asset: "opencode-linux-arm64.tar.gz",
257
+ sha256: "70baf769395ca4e7a68924026530c390eace194f3b7e4919d4efcb2aa2eed3c0",
258
+ format: "tar.gz"
259
+ },
260
+ x64: {
261
+ asset: "opencode-linux-x64.tar.gz",
262
+ sha256: "ea800b7ff56226b70952126c9fc1e2517ca4c4b5682fd9d3f9e87449697a1194",
263
+ format: "tar.gz"
264
+ }
265
+ },
266
+ grokDependencies: {
267
+ arm64: {
268
+ name: "@xai-official/grok-linux-arm64",
269
+ integrity: "sha512-Jlwz9XtZPNKz/V/ydifC8OVu3hIZz7pmoStt8c4KCqkSWjWiZ/PBIC0iJiq8rvByhx5Z0EdSyq8ZXVwtocceGA==",
270
+ tarballUrl: "https://registry.npmjs.org/@xai-official/grok-linux-arm64/-/grok-linux-arm64-1.0.21.tgz"
271
+ },
272
+ x64: {
273
+ name: "@xai-official/grok-linux-x64",
274
+ integrity: "sha512-AjDV5ifEz5LFG2Jt5IlhAeEkJati/wOvCchVbR4eIj2ogjvmXoXlzBrYRXuAJpXBy5q0se/Yuez5FxDU9Ju81w==",
275
+ tarballUrl: "https://registry.npmjs.org/@xai-official/grok-linux-x64/-/grok-linux-x64-1.0.21.tgz"
276
+ }
277
+ }
278
+ };
279
+
280
+ // src/platform/macos.ts
281
+ import { isAbsolute, join as join3 } from "node:path";
282
+ var WORKBUDDY_MACOS_EXECUTABLE_PATH = Object.freeze([
283
+ "WorkBuddy.app",
284
+ "Contents",
285
+ "Resources",
286
+ "app.asar.unpacked",
287
+ "cli",
288
+ "bin",
289
+ "codebuddy"
290
+ ]);
291
+ var macosPlatform = {
292
+ ...posixOperations,
293
+ readProcessTable: () => readProcessTableWithPs("/bin/ps"),
294
+ readProcessTableSynchronously: () => [],
295
+ processExists,
296
+ workBuddyExecutablePaths: (environment) => {
297
+ const paths = [join3("/Applications", ...WORKBUDDY_MACOS_EXECUTABLE_PATH)];
298
+ const homeDirectory = environment["HOME"];
299
+ if (homeDirectory !== void 0 && isAbsolute(homeDirectory)) {
300
+ paths.unshift(join3(homeDirectory, "Applications", ...WORKBUDDY_MACOS_EXECUTABLE_PATH));
301
+ }
302
+ return paths;
303
+ },
304
+ openCodeArchives: {
305
+ arm64: {
306
+ asset: "opencode-darwin-arm64.zip",
307
+ sha256: "fe764f7f360c584a83e18dd5f23fb1a6b2725f5ee8854b0252fe558f7798e946",
308
+ format: "zip"
309
+ },
310
+ x64: {
311
+ asset: "opencode-darwin-x64.zip",
312
+ sha256: "9858853e7bacdbbd22c2d70c377e009dc4b354dd04f5588705411e7afb89fd2d",
313
+ format: "zip"
314
+ }
315
+ },
316
+ grokDependencies: {
317
+ arm64: {
318
+ name: "@xai-official/grok-darwin-arm64",
319
+ integrity: "sha512-I4pKZGnR6Mv4MJOJsB9B/i2o0ODF4eZtp4jHN5feoTqKIeAUVg38OzBtE8aN/wZa7Xg+qhnf6Jd1tcAFvMoFtQ==",
320
+ tarballUrl: "https://registry.npmjs.org/@xai-official/grok-darwin-arm64/-/grok-darwin-arm64-1.0.21.tgz"
321
+ },
322
+ x64: {
323
+ name: "@xai-official/grok-darwin-x64",
324
+ integrity: "sha512-h0nFj3NZ9Kz8w6fQ6UDxxeSIhC1WjwwoRE92iNbG//tZy6YGkppR3s7oRVtt4Xl0CJANKUt0EusyIYnnAvKLzw==",
325
+ tarballUrl: "https://registry.npmjs.org/@xai-official/grok-darwin-x64/-/grok-darwin-x64-1.0.21.tgz"
326
+ }
327
+ }
328
+ };
329
+
330
+ // src/platform/windows.ts
331
+ import { execFile as execFile2 } from "node:child_process";
332
+ import { readFile as readFile2, realpath as realpath3 } from "node:fs/promises";
333
+ import { dirname, extname, join as join4, resolve } from "node:path";
334
+ import { promisify } from "node:util";
335
+
336
+ // src/process/process-error.ts
337
+ var ProcessExecutionError = class extends Error {
338
+ code;
339
+ constructor(code, message, options = {}) {
340
+ super(message, options);
341
+ this.code = code;
342
+ this.name = "ProcessExecutionError";
343
+ }
344
+ };
345
+
346
+ // src/platform/windows.ts
347
+ var execute = promisify(execFile2);
348
+ function environmentValue(environment, name) {
349
+ return Object.entries(environment).find(([key]) => key.toUpperCase() === name.toUpperCase())?.[1];
350
+ }
351
+ function executableExtensions(executableName, environment) {
352
+ if (/\.[a-zA-Z0-9]+$/.test(executableName)) {
353
+ return [""];
354
+ }
355
+ const configured = environmentValue(environment, "PATHEXT") ?? ".COM;.EXE;.BAT;.CMD";
356
+ const extensions = configured.split(";").map((extension) => extension.trim()).filter((extension) => extension.length > 0).map((extension) => extension.startsWith(".") ? extension : `.${extension}`);
357
+ return extensions.length > 0 ? extensions : [".EXE"];
358
+ }
359
+ async function resolveExecutableTarget(path) {
360
+ let resolvedPath;
361
+ try {
362
+ resolvedPath = await realpath3(extname(path) === "" ? `${path}.cmd` : path);
363
+ } catch (cause) {
364
+ if (extname(path) !== "" || !(cause instanceof Error) || !("code" in cause) || cause.code !== "ENOENT") {
365
+ throw cause;
366
+ }
367
+ resolvedPath = await realpath3(path);
368
+ }
369
+ if (extname(resolvedPath).toLowerCase() === ".cmd") {
370
+ const shim = await readFile2(resolvedPath, "utf8");
371
+ const script = /"%_prog%"\s+"%dp0%[\\/]([^"\r\n]+\.(?:js|mjs|cjs))"\s+%\*/iu.exec(shim)?.[1];
372
+ if (script !== void 0 && shim.includes("SET dp0=%~dp0")) {
373
+ resolvedPath = await realpath3(resolve(dirname(resolvedPath), script));
374
+ } else {
375
+ const pnpmScript = /^\s*node\s+"%~dp0[\\/]([^"\r\n]+\.(?:js|mjs|cjs))"\s+%\*\s*$/imu.exec(shim)?.[1];
376
+ if (pnpmScript === void 0 || !shim.includes("@SETLOCAL")) {
377
+ throw new ProcessExecutionError(
378
+ "process_unsupported_script",
379
+ "Use a native executable or a verified Node entrypoint"
380
+ );
381
+ }
382
+ resolvedPath = await realpath3(resolve(dirname(resolvedPath), pnpmScript));
383
+ }
384
+ }
385
+ return resolvedPath;
386
+ }
387
+ function executableLaunch(command, args) {
388
+ if (/\.(?:js|mjs|cjs)$/iu.test(command)) {
389
+ return { command: process.execPath, arguments: [command, ...args] };
390
+ }
391
+ if (/\.(?:cmd|bat)$/iu.test(command)) {
392
+ throw new ProcessExecutionError(
393
+ "process_unsupported_script",
394
+ "Use a native executable or a verified Node entrypoint"
395
+ );
396
+ }
397
+ return { command, arguments: args };
120
398
  }
121
399
  function readProcessTable() {
122
- return hostPlatform.readProcessTable();
400
+ const systemRoot = environmentValue(process.env, "SystemRoot");
401
+ if (systemRoot === void 0) {
402
+ return Promise.resolve([]);
403
+ }
404
+ return new Promise((resolveTable) => {
405
+ execFile2(
406
+ join4(systemRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe"),
407
+ [
408
+ "-NoLogo",
409
+ "-NoProfile",
410
+ "-NonInteractive",
411
+ "-Command",
412
+ "Get-CimInstance Win32_Process | ForEach-Object { '{0} {1} {0}' -f $_.ProcessId, $_.ParentProcessId }"
413
+ ],
414
+ {
415
+ encoding: "utf8",
416
+ windowsHide: true,
417
+ timeout: 1e4,
418
+ signal: AbortSignal.timeout(1e4),
419
+ maxBuffer: 1024 * 1024
420
+ },
421
+ (error, stdout) => resolveTable(error === null ? parseProcessTable(stdout) : [])
422
+ );
423
+ });
424
+ }
425
+ function signalCapturedProcesses2(processes, _rootProcessId, signal) {
426
+ for (const identity of [...processes].reverse()) {
427
+ try {
428
+ process.kill(identity.processId, signal);
429
+ } catch {
430
+ }
431
+ }
432
+ }
433
+ function terminateProcess2(child, signal) {
434
+ if (child.exitCode !== null || child.signalCode !== null || child.pid === void 0) {
435
+ return;
436
+ }
437
+ child.kill(signal);
438
+ }
439
+ var windowsPermissions = `
440
+ $ErrorActionPreference = 'Stop'
441
+ Import-Module (Join-Path $PSHOME 'Modules/Microsoft.PowerShell.Security/Microsoft.PowerShell.Security.psd1')
442
+ $item = Get-Item -LiteralPath $env:MOBIUS_PERMISSION_PATH -Force
443
+ if (($item.Attributes -band [IO.FileAttributes]::ReparsePoint) -ne 0) { throw 'Unsafe path' }
444
+ $sid = [Security.Principal.WindowsIdentity]::GetCurrent().User
445
+ $acl = Get-Acl -LiteralPath $item.FullName
446
+ $owner = $acl.GetOwner([Security.Principal.SecurityIdentifier])
447
+ if ($owner -ne $sid -and -not ($owner.Value -eq 'S-1-5-32-544' -and ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator))) { throw 'Path is not user-owned' }
448
+ if ($env:MOBIUS_PERMISSION_PRIVATE -eq '1') {
449
+ if ($item.PSIsContainer) {
450
+ $private = New-Object Security.AccessControl.DirectorySecurity
451
+ $rule = New-Object Security.AccessControl.FileSystemAccessRule($sid, 'FullControl', 'ContainerInherit,ObjectInherit', 'None', 'Allow')
452
+ } else {
453
+ $private = New-Object Security.AccessControl.FileSecurity
454
+ $rule = New-Object Security.AccessControl.FileSystemAccessRule($sid, 'FullControl', 'Allow')
455
+ }
456
+ $private.SetOwner($sid)
457
+ $private.SetAccessRuleProtection($true, $false)
458
+ $private.AddAccessRule($rule)
459
+ Set-Acl -LiteralPath $item.FullName -AclObject $private
460
+ }
461
+ `;
462
+ async function validatePathPermissions(path, _directory, privateAccess, signal) {
463
+ signal.throwIfAborted();
464
+ const systemRoot = environmentValue(process.env, "SystemRoot");
465
+ if (systemRoot === void 0) {
466
+ throw new LocalPathError("Windows system directory is unavailable");
467
+ }
468
+ try {
469
+ await execute(
470
+ join4(systemRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe"),
471
+ [
472
+ "-NoLogo",
473
+ "-NoProfile",
474
+ "-NonInteractive",
475
+ "-EncodedCommand",
476
+ Buffer.from(windowsPermissions, "utf16le").toString("base64")
477
+ ],
478
+ {
479
+ env: {
480
+ ...process.env,
481
+ MOBIUS_PERMISSION_PATH: path,
482
+ MOBIUS_PERMISSION_PRIVATE: privateAccess ? "1" : "0"
483
+ },
484
+ windowsHide: true,
485
+ signal: AbortSignal.any([signal, AbortSignal.timeout(1e4)]),
486
+ timeout: 1e4,
487
+ maxBuffer: 16384
488
+ }
489
+ );
490
+ } catch (cause) {
491
+ signal.throwIfAborted();
492
+ throw new LocalPathError("The local path permissions could not be secured", { cause });
493
+ }
494
+ }
495
+ var windowsPlatform = {
496
+ controlSocketPath: (installationHash) => `\\\\.\\pipe\\mobius-${installationHash}`,
497
+ prepareControlSocket: async () => {
498
+ },
499
+ secureControlSocket: async () => {
500
+ },
501
+ removeControlSocket: async () => {
502
+ },
503
+ pathDelimiter: ";",
504
+ environmentValue,
505
+ executableExtensions,
506
+ resolveExecutableTarget,
507
+ executableLaunch,
508
+ spawnOptions: { detached: false, windowsHide: true },
509
+ signalCapturedProcesses: signalCapturedProcesses2,
510
+ terminateProcess: terminateProcess2,
511
+ validatePathPermissions,
512
+ readProcessTable,
513
+ readProcessTableSynchronously: () => [],
514
+ processExists,
515
+ workBuddyExecutablePaths: () => [],
516
+ openCodeArchives: {},
517
+ grokDependencies: {}
518
+ };
519
+
520
+ // src/platform/index.ts
521
+ var fallbackPlatform = {
522
+ ...posixOperations,
523
+ readProcessTable: () => readProcessTableWithPs("/usr/bin/ps"),
524
+ readProcessTableSynchronously: () => [],
525
+ processExists,
526
+ workBuddyExecutablePaths: () => [],
527
+ openCodeArchives: {},
528
+ grokDependencies: {}
529
+ };
530
+ var platforms = /* @__PURE__ */ new Map([
531
+ ["linux", linuxPlatform],
532
+ ["darwin", macosPlatform],
533
+ ["win32", windowsPlatform]
534
+ ]);
535
+ function platformSupportFor(platform) {
536
+ return platforms.get(platform) ?? fallbackPlatform;
537
+ }
538
+
539
+ // src/process/executable.ts
540
+ var hostPlatform = platformSupportFor(process.platform);
541
+ var DEFAULT_OUTPUT_LIMIT_BYTES = 16 * 1024;
542
+ var PROCESS_GRACEFUL_STOP_TIMEOUT_MILLISECONDS = 1e3;
543
+ var PROCESS_FORCED_STOP_TIMEOUT_MILLISECONDS = 5e3;
544
+ var PROCESS_EXIT_CHECK_ATTEMPTS = 16;
545
+ var GRACEFUL_STOP_SIGNAL = 15;
546
+ var FORCED_STOP_SIGNAL = 9;
547
+ var ExecutableIdentity = class {
548
+ changedAtMilliseconds;
549
+ device;
550
+ inode;
551
+ realPath;
552
+ requestedPath;
553
+ size;
554
+ constructor(requestedPath, realPath_, device, inode, size, changedAtMilliseconds) {
555
+ this.changedAtMilliseconds = changedAtMilliseconds;
556
+ this.device = device;
557
+ this.inode = inode;
558
+ this.realPath = realPath_;
559
+ this.requestedPath = requestedPath;
560
+ this.size = size;
561
+ }
562
+ matches = (candidate) => this.requestedPath === candidate.requestedPath && this.realPath === candidate.realPath && this.device === candidate.device && this.inode === candidate.inode && this.size === candidate.size && this.changedAtMilliseconds === candidate.changedAtMilliseconds;
563
+ };
564
+ var ResolvedExecutable = class {
565
+ identity;
566
+ path;
567
+ constructor(identity) {
568
+ this.identity = identity;
569
+ this.path = identity.realPath;
570
+ }
571
+ };
572
+ var ProcessResult = class {
573
+ stderr;
574
+ stdout;
575
+ constructor(stdout, stderr) {
576
+ this.stderr = stderr;
577
+ this.stdout = stdout;
578
+ }
579
+ };
580
+ async function resolveExecutablePath(requestedPath, platform = process.platform) {
581
+ const absoluteRequestedPath = resolve2(requestedPath);
582
+ try {
583
+ const resolvedPath = await platformSupportFor(platform).resolveExecutableTarget(absoluteRequestedPath);
584
+ const metadata = await stat2(resolvedPath);
585
+ if (!metadata.isFile()) {
586
+ return null;
587
+ }
588
+ return new ResolvedExecutable(
589
+ new ExecutableIdentity(
590
+ absoluteRequestedPath,
591
+ resolvedPath,
592
+ metadata.dev,
593
+ metadata.ino,
594
+ metadata.size,
595
+ metadata.ctimeMs
596
+ )
597
+ );
598
+ } catch {
599
+ return null;
600
+ }
601
+ }
602
+ async function findExecutableOnPath(executableName, environment, platform = process.platform) {
603
+ const support = platformSupportFor(platform);
604
+ const encodedPath = support.environmentValue(environment, "PATH") ?? "";
605
+ const separator = support.pathDelimiter;
606
+ const extensions = support.executableExtensions(executableName, environment);
607
+ for (const entry of encodedPath.split(separator)) {
608
+ if (entry.length === 0) {
609
+ continue;
610
+ }
611
+ for (const extension of extensions) {
612
+ const requestedPath = resolve2(join5(entry, `${executableName}${extension}`));
613
+ const executable = await resolveExecutablePath(requestedPath, platform);
614
+ if (executable !== null) {
615
+ return executable;
616
+ }
617
+ }
618
+ }
619
+ return null;
620
+ }
621
+ async function revalidateExecutable(identity, platform = process.platform) {
622
+ const current = await resolveExecutablePath(identity.requestedPath, platform);
623
+ return current !== null && identity.matches(current.identity);
624
+ }
625
+ function appendBounded(chunks, chunk, currentBytes, limitBytes) {
626
+ const nextBytes = currentBytes + chunk.byteLength;
627
+ if (nextBytes > limitBytes) {
628
+ throw new ProcessExecutionError("process_output_limit", "The executable produced too much output");
629
+ }
630
+ chunks.push(chunk);
631
+ return nextBytes;
632
+ }
633
+ function readProcessTable2() {
634
+ return hostPlatform.readProcessTable();
123
635
  }
124
636
  function selectProcessTree(entries, rootProcessId) {
125
- const processIds = new Set([rootProcessId]);
637
+ const processIds = /* @__PURE__ */ new Set([rootProcessId]);
638
+ for (const entry of entries) {
639
+ if (entry.processGroupId === rootProcessId) {
640
+ processIds.add(entry.processId);
641
+ }
642
+ }
643
+ let previousSize = 0;
644
+ while (previousSize !== processIds.size) {
645
+ previousSize = processIds.size;
126
646
  for (const entry of entries) {
127
- if (entry.processGroupId === rootProcessId) {
128
- processIds.add(entry.processId);
129
- }
130
- }
131
- let previousSize = 0;
132
- while (previousSize !== processIds.size) {
133
- previousSize = processIds.size;
134
- for (const entry of entries) {
135
- if (processIds.has(entry.parentProcessId)) {
136
- processIds.add(entry.processId);
137
- }
138
- }
139
- }
140
- return entries
141
- .filter((entry) => processIds.has(entry.processId))
142
- .map((entry) => ({
143
- processGroupId: entry.processGroupId,
144
- processId: entry.processId,
145
- }));
647
+ if (processIds.has(entry.parentProcessId)) {
648
+ processIds.add(entry.processId);
649
+ }
650
+ }
651
+ }
652
+ return entries.filter((entry) => processIds.has(entry.processId)).map((entry) => ({
653
+ processGroupId: entry.processGroupId,
654
+ processId: entry.processId
655
+ }));
146
656
  }
147
657
  function mergeProcessTrees(captured, current) {
148
- const processes = new Map();
149
- for (const identity of captured) {
150
- processes.set(identity.processId, identity);
151
- }
152
- for (const identity of current) {
153
- processes.set(identity.processId, identity);
154
- }
155
- return [...processes.values()];
658
+ const processes = /* @__PURE__ */ new Map();
659
+ for (const identity of captured) {
660
+ processes.set(identity.processId, identity);
661
+ }
662
+ for (const identity of current) {
663
+ processes.set(identity.processId, identity);
664
+ }
665
+ return [...processes.values()];
156
666
  }
157
- export async function captureProcessTree(child) {
158
- if (process.platform === "win32" || child.pid === undefined) {
159
- return [];
160
- }
161
- const entries = hostPlatform.readProcessTableSynchronously();
162
- if (entries.length > 0) {
163
- return selectProcessTree(entries, child.pid);
164
- }
165
- return selectProcessTree(await readProcessTable(), child.pid);
166
- }
167
- function signalCapturedProcessGroups(processes, rootProcessId, signal) {
168
- const processIds = new Set(processes.map((identity) => identity.processId));
169
- const signalledGroups = new Set();
170
- for (const identity of processes) {
171
- const isRootProcessGroup = identity.processGroupId === rootProcessId;
172
- if ((!isRootProcessGroup && !processIds.has(identity.processGroupId)) ||
173
- signalledGroups.has(identity.processGroupId)) {
174
- continue;
175
- }
176
- try {
177
- process.kill(-identity.processGroupId, signal);
178
- signalledGroups.add(identity.processGroupId);
179
- }
180
- catch {
181
- // A concurrently exited group no longer needs cleanup.
182
- }
183
- }
184
- for (const identity of processes) {
185
- if (identity.processId === rootProcessId ||
186
- identity.processGroupId === rootProcessId ||
187
- signalledGroups.has(identity.processGroupId)) {
188
- continue;
189
- }
190
- try {
191
- process.kill(identity.processId, signal);
192
- }
193
- catch {
194
- // A concurrently exited process no longer needs cleanup.
195
- }
196
- }
667
+ async function captureProcessTree(child) {
668
+ if (child.pid === void 0) {
669
+ return [];
670
+ }
671
+ const entries = hostPlatform.readProcessTableSynchronously();
672
+ if (entries.length > 0) {
673
+ return selectProcessTree(entries, child.pid);
674
+ }
675
+ return selectProcessTree(await readProcessTable2(), child.pid);
197
676
  }
198
677
  async function waitForCapturedProcessesExit(processes, rootProcessId) {
199
- const processIds = processes
200
- .map((identity) => identity.processId)
201
- .filter((processId) => processId !== rootProcessId);
202
- for (let attempt = 0; attempt < PROCESS_EXIT_CHECK_ATTEMPTS; attempt += 1) {
203
- if (processIds.every((processId) => !hostPlatform.processExists(processId))) {
204
- return true;
205
- }
206
- await readProcessTable();
207
- }
208
- return processIds.every((processId) => !hostPlatform.processExists(processId));
209
- }
210
- function terminateProcess(child) {
211
- if (child.exitCode !== null || child.signalCode !== null || child.pid === undefined) {
678
+ const processIds = processes.map((identity) => identity.processId).filter((processId) => processId !== rootProcessId);
679
+ for (let attempt = 0; attempt < PROCESS_EXIT_CHECK_ATTEMPTS; attempt += 1) {
680
+ if (processIds.every((processId) => !hostPlatform.processExists(processId))) {
681
+ return true;
682
+ }
683
+ await readProcessTable2();
684
+ }
685
+ return processIds.every((processId) => !hostPlatform.processExists(processId));
686
+ }
687
+ async function stopProcessTree(child, capturedProcesses = []) {
688
+ const childProcessId = child.pid;
689
+ if (childProcessId === void 0) {
690
+ return;
691
+ }
692
+ const childExited = child.exitCode !== null || child.signalCode !== null;
693
+ const processes = mergeProcessTrees(capturedProcesses, await captureProcessTree(child));
694
+ if (childExited && processes.length === 0) {
695
+ return;
696
+ }
697
+ hostPlatform.signalCapturedProcesses(processes, childProcessId, GRACEFUL_STOP_SIGNAL);
698
+ hostPlatform.terminateProcess(child, "SIGTERM");
699
+ const [exitedAfterTermination, descendantsExitedAfterTermination] = await Promise.all([
700
+ waitForProcessExit(child, PROCESS_GRACEFUL_STOP_TIMEOUT_MILLISECONDS),
701
+ waitForCapturedProcessesExit(processes, childProcessId)
702
+ ]);
703
+ if (exitedAfterTermination && descendantsExitedAfterTermination) {
704
+ return;
705
+ }
706
+ hostPlatform.signalCapturedProcesses(processes, childProcessId, FORCED_STOP_SIGNAL);
707
+ hostPlatform.terminateProcess(child, "SIGKILL");
708
+ const [exitedAfterForce, descendantsExitedAfterForce] = await Promise.all([
709
+ waitForProcessExit(child, PROCESS_FORCED_STOP_TIMEOUT_MILLISECONDS),
710
+ waitForCapturedProcessesExit(processes, childProcessId)
711
+ ]);
712
+ if (!exitedAfterForce || !descendantsExitedAfterForce) {
713
+ throw new ProcessExecutionError("process_termination_timeout", "The executable process could not be stopped");
714
+ }
715
+ }
716
+ function waitForProcessExit(child, timeoutMilliseconds) {
717
+ if (child.exitCode !== null || child.signalCode !== null) {
718
+ return Promise.resolve(true);
719
+ }
720
+ return new Promise((resolveExit) => {
721
+ let settled = false;
722
+ const finish = (exited) => {
723
+ if (settled) {
212
724
  return;
725
+ }
726
+ settled = true;
727
+ clearTimeout(timeout);
728
+ child.removeListener("exit", handleExit);
729
+ resolveExit(exited);
730
+ };
731
+ const handleExit = () => finish(true);
732
+ const timeout = setTimeout(() => finish(false), timeoutMilliseconds);
733
+ child.once("exit", handleExit);
734
+ if (child.exitCode !== null || child.signalCode !== null) {
735
+ finish(true);
213
736
  }
214
- if (process.platform !== "win32") {
215
- try {
216
- process.kill(-child.pid, "SIGTERM");
217
- return;
218
- }
219
- catch {
220
- // Fall through when the process did not create a process group.
221
- }
222
- }
223
- child.kill("SIGTERM");
737
+ });
224
738
  }
225
- function forceTerminateProcess(child) {
226
- if (child.exitCode !== null || child.signalCode !== null || child.pid === undefined) {
227
- return;
739
+ async function runBoundedProcess(options) {
740
+ options.signal.throwIfAborted();
741
+ const outputLimitBytes = options.outputLimitBytes ?? DEFAULT_OUTPUT_LIMIT_BYTES;
742
+ const launch = executableLaunch2(options.command, options.arguments);
743
+ const child = spawn(launch.command, [...launch.arguments], {
744
+ cwd: options.cwd,
745
+ ...hostPlatform.spawnOptions,
746
+ env: { ...options.environment },
747
+ shell: false,
748
+ stdio: ["ignore", "pipe", "pipe"]
749
+ });
750
+ const stdout = [];
751
+ const stderr = [];
752
+ let stdoutBytes = 0;
753
+ let stderrBytes = 0;
754
+ let interruptionFailure = null;
755
+ let interruptionCleanupFailure = null;
756
+ let stopping = false;
757
+ const interrupted = Promise.withResolvers();
758
+ const interrupt = (failure) => {
759
+ if (stopping) {
760
+ return;
761
+ }
762
+ stopping = true;
763
+ interruptionFailure = failure;
764
+ const stop = async () => {
765
+ try {
766
+ await stopProcessTree(child);
767
+ } catch (error) {
768
+ interruptionCleanupFailure = error instanceof ProcessExecutionError ? error : new ProcessExecutionError(
769
+ "process_termination_failed",
770
+ "The executable process could not be stopped",
771
+ { cause: error }
772
+ );
773
+ } finally {
774
+ interrupted.resolve();
775
+ }
776
+ };
777
+ void stop();
778
+ };
779
+ const collectStdout = (chunk) => {
780
+ if (interruptionFailure !== null) {
781
+ return;
228
782
  }
229
- if (process.platform !== "win32") {
230
- try {
231
- process.kill(-child.pid, "SIGKILL");
232
- return;
233
- }
234
- catch {
235
- // Fall through when the process did not create a process group.
236
- }
783
+ try {
784
+ stdoutBytes = appendBounded(stdout, chunk, stdoutBytes, outputLimitBytes);
785
+ } catch (error) {
786
+ const failure = error instanceof ProcessExecutionError ? error : new ProcessExecutionError("process_output_limit", "The executable produced too much output", {
787
+ cause: error
788
+ });
789
+ interrupt(failure);
790
+ }
791
+ };
792
+ const collectStderr = (chunk) => {
793
+ if (interruptionFailure !== null) {
794
+ return;
237
795
  }
238
- child.kill("SIGKILL");
239
- }
240
- export async function stopProcessTree(child, capturedProcesses = []) {
241
- const childProcessId = child.pid;
242
- if (childProcessId === undefined) {
243
- return;
796
+ try {
797
+ stderrBytes = appendBounded(stderr, chunk, stderrBytes, outputLimitBytes);
798
+ } catch (error) {
799
+ const failure = error instanceof ProcessExecutionError ? error : new ProcessExecutionError("process_output_limit", "The executable produced too much output", {
800
+ cause: error
801
+ });
802
+ interrupt(failure);
803
+ }
804
+ };
805
+ child.stdout?.on("data", collectStdout);
806
+ child.stderr?.on("data", collectStderr);
807
+ const timeout = setTimeout(() => {
808
+ interrupt(new ProcessExecutionError("process_timeout", "The executable probe timed out"));
809
+ }, options.timeoutMilliseconds);
810
+ const abort = () => interrupt(
811
+ new ProcessExecutionError("process_cancelled", "The executable probe was cancelled", {
812
+ cause: options.signal.reason
813
+ })
814
+ );
815
+ options.signal.addEventListener("abort", abort, { once: true });
816
+ if (options.signal.aborted) {
817
+ abort();
818
+ }
819
+ try {
820
+ const exit = new Promise((resolveExit, rejectExit) => {
821
+ child.once("error", rejectExit);
822
+ child.once("exit", (code) => resolveExit(code ?? 1));
823
+ });
824
+ const exitCode = await Promise.race([exit, interrupted.promise.then(() => -1)]);
825
+ if (interruptionCleanupFailure !== null) {
826
+ throw interruptionCleanupFailure;
244
827
  }
245
- const childExited = child.exitCode !== null || child.signalCode !== null;
246
- const processes = mergeProcessTrees(capturedProcesses, await captureProcessTree(child));
247
- if (childExited && processes.length === 0) {
248
- return;
828
+ if (interruptionFailure !== null) {
829
+ throw interruptionFailure;
249
830
  }
250
- signalCapturedProcessGroups(processes, childProcessId, GRACEFUL_STOP_SIGNAL);
251
- terminateProcess(child);
252
- const [exitedAfterTermination, descendantsExitedAfterTermination] = await Promise.all([
253
- waitForProcessExit(child, PROCESS_GRACEFUL_STOP_TIMEOUT_MILLISECONDS),
254
- waitForCapturedProcessesExit(processes, childProcessId),
255
- ]);
256
- if (exitedAfterTermination && descendantsExitedAfterTermination) {
257
- return;
831
+ if (exitCode !== 0) {
832
+ throw new ProcessExecutionError("process_exit_failed", "The executable probe failed");
258
833
  }
259
- signalCapturedProcessGroups(processes, childProcessId, FORCED_STOP_SIGNAL);
260
- forceTerminateProcess(child);
261
- const [exitedAfterForce, descendantsExitedAfterForce] = await Promise.all([
262
- waitForProcessExit(child, PROCESS_FORCED_STOP_TIMEOUT_MILLISECONDS),
263
- waitForCapturedProcessesExit(processes, childProcessId),
264
- ]);
265
- if (!exitedAfterForce || !descendantsExitedAfterForce) {
266
- throw new ProcessExecutionError("process_termination_timeout", "The executable process could not be stopped");
834
+ return new ProcessResult(Buffer.concat(stdout).toString("utf8"), Buffer.concat(stderr).toString("utf8"));
835
+ } catch (error) {
836
+ if (error instanceof ProcessExecutionError) {
837
+ throw error;
267
838
  }
268
- }
269
- function waitForProcessExit(child, timeoutMilliseconds) {
270
- if (child.exitCode !== null || child.signalCode !== null) {
271
- return Promise.resolve(true);
272
- }
273
- return new Promise((resolveExit) => {
274
- let settled = false;
275
- const finish = (exited) => {
276
- if (settled) {
277
- return;
278
- }
279
- settled = true;
280
- clearTimeout(timeout);
281
- child.removeListener("exit", handleExit);
282
- resolveExit(exited);
283
- };
284
- const handleExit = () => finish(true);
285
- const timeout = setTimeout(() => finish(false), timeoutMilliseconds);
286
- child.once("exit", handleExit);
287
- if (child.exitCode !== null || child.signalCode !== null) {
288
- finish(true);
289
- }
839
+ throw new ProcessExecutionError("process_spawn_failed", "The executable could not be started", {
840
+ cause: error
290
841
  });
842
+ } finally {
843
+ clearTimeout(timeout);
844
+ options.signal.removeEventListener("abort", abort);
845
+ child.stdout?.removeListener("data", collectStdout);
846
+ child.stderr?.removeListener("data", collectStderr);
847
+ if (stopping) {
848
+ await interrupted.promise;
849
+ } else {
850
+ await stopProcessTree(child);
851
+ }
852
+ }
291
853
  }
292
- export async function runBoundedProcess(options) {
293
- options.signal.throwIfAborted();
294
- const outputLimitBytes = options.outputLimitBytes ?? DEFAULT_OUTPUT_LIMIT_BYTES;
295
- const child = spawn(options.command, [...options.arguments], {
296
- cwd: options.cwd,
297
- detached: process.platform !== "win32",
298
- env: { ...options.environment },
299
- shell: false,
300
- stdio: ["ignore", "pipe", "pipe"],
301
- });
302
- const stdout = [];
303
- const stderr = [];
304
- let stdoutBytes = 0;
305
- let stderrBytes = 0;
306
- let interruptionFailure = null;
307
- let interruptionCleanupFailure = null;
308
- let stopping = false;
309
- const interrupted = Promise.withResolvers();
310
- const interrupt = (failure) => {
311
- if (stopping) {
312
- return;
313
- }
314
- stopping = true;
315
- interruptionFailure = failure;
316
- const stop = async () => {
317
- try {
318
- await stopProcessTree(child);
319
- }
320
- catch (error) {
321
- interruptionCleanupFailure =
322
- error instanceof ProcessExecutionError
323
- ? error
324
- : new ProcessExecutionError("process_termination_failed", "The executable process could not be stopped", { cause: error });
325
- }
326
- finally {
327
- interrupted.resolve();
328
- }
329
- };
330
- void stop();
331
- };
332
- const collectStdout = (chunk) => {
333
- if (interruptionFailure !== null) {
334
- return;
335
- }
336
- try {
337
- stdoutBytes = appendBounded(stdout, chunk, stdoutBytes, outputLimitBytes);
338
- }
339
- catch (error) {
340
- const failure = error instanceof ProcessExecutionError
341
- ? error
342
- : new ProcessExecutionError("process_output_limit", "The executable produced too much output", {
343
- cause: error,
344
- });
345
- interrupt(failure);
346
- }
347
- };
348
- const collectStderr = (chunk) => {
349
- if (interruptionFailure !== null) {
350
- return;
351
- }
352
- try {
353
- stderrBytes = appendBounded(stderr, chunk, stderrBytes, outputLimitBytes);
354
- }
355
- catch (error) {
356
- const failure = error instanceof ProcessExecutionError
357
- ? error
358
- : new ProcessExecutionError("process_output_limit", "The executable produced too much output", {
359
- cause: error,
360
- });
361
- interrupt(failure);
362
- }
363
- };
364
- child.stdout?.on("data", collectStdout);
365
- child.stderr?.on("data", collectStderr);
366
- const timeout = setTimeout(() => {
367
- interrupt(new ProcessExecutionError("process_timeout", "The executable probe timed out"));
368
- }, options.timeoutMilliseconds);
369
- const abort = () => interrupt(new ProcessExecutionError("process_cancelled", "The executable probe was cancelled", {
370
- cause: options.signal.reason,
371
- }));
372
- options.signal.addEventListener("abort", abort, { once: true });
373
- if (options.signal.aborted) {
374
- abort();
375
- }
376
- try {
377
- const exit = new Promise((resolveExit, rejectExit) => {
378
- child.once("error", rejectExit);
379
- child.once("exit", (code) => resolveExit(code ?? 1));
380
- });
381
- const exitCode = await Promise.race([exit, interrupted.promise.then(() => -1)]);
382
- if (interruptionCleanupFailure !== null) {
383
- throw interruptionCleanupFailure;
384
- }
385
- if (interruptionFailure !== null) {
386
- throw interruptionFailure;
387
- }
388
- if (exitCode !== 0) {
389
- throw new ProcessExecutionError("process_exit_failed", "The executable probe failed");
390
- }
391
- return new ProcessResult(Buffer.concat(stdout).toString("utf8"), Buffer.concat(stderr).toString("utf8"));
392
- }
393
- catch (error) {
394
- if (error instanceof ProcessExecutionError) {
395
- throw error;
396
- }
397
- throw new ProcessExecutionError("process_spawn_failed", "The executable could not be started", {
398
- cause: error,
399
- });
400
- }
401
- finally {
402
- clearTimeout(timeout);
403
- options.signal.removeEventListener("abort", abort);
404
- child.stdout?.removeListener("data", collectStdout);
405
- child.stderr?.removeListener("data", collectStderr);
406
- if (stopping) {
407
- await interrupted.promise;
408
- }
409
- else {
410
- await stopProcessTree(child);
411
- }
412
- }
413
- }
414
- //# sourceMappingURL=executable.js.map
854
+ function executableLaunch2(command, args) {
855
+ return hostPlatform.executableLaunch(command, args);
856
+ }
857
+ var platformSpawnOptions = hostPlatform.spawnOptions;
858
+ export {
859
+ ExecutableIdentity,
860
+ ProcessExecutionError,
861
+ ProcessResult,
862
+ ResolvedExecutable,
863
+ captureProcessTree,
864
+ executableLaunch2 as executableLaunch,
865
+ findExecutableOnPath,
866
+ platformSpawnOptions,
867
+ resolveExecutablePath,
868
+ revalidateExecutable,
869
+ runBoundedProcess,
870
+ stopProcessTree
871
+ };
872
+ //# sourceMappingURL=executable.js.map