@moxt-ai/mobius 0.0.195 → 0.0.197
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.
- package/dist/acp/agent-connection.d.ts.map +1 -1
- package/dist/acp/agent-connection.js +4 -3
- package/dist/acp/agent-connection.js.map +1 -1
- package/dist/acp/codex/app-server-client.d.ts.map +1 -1
- package/dist/acp/codex/app-server-client.js +4 -2
- package/dist/acp/codex/app-server-client.js.map +1 -1
- package/dist/agent-catalog/acp-readiness-probe.d.ts.map +1 -1
- package/dist/agent-catalog/acp-readiness-probe.js +4 -3
- package/dist/agent-catalog/acp-readiness-probe.js.map +1 -1
- package/dist/cli.js +532 -292
- package/dist/cli.js.map +4 -4
- package/dist/daemon-manager.d.ts.map +1 -1
- package/dist/daemon-manager.js +7 -17
- package/dist/daemon-manager.js.map +1 -1
- package/dist/filesystem/path-error.d.ts +5 -0
- package/dist/filesystem/path-error.d.ts.map +1 -0
- package/dist/filesystem/path-error.js +8 -0
- package/dist/filesystem/path-error.js.map +1 -0
- package/dist/filesystem/private-path.d.ts +3 -0
- package/dist/filesystem/private-path.d.ts.map +1 -0
- package/dist/filesystem/private-path.js +550 -0
- package/dist/filesystem/private-path.js.map +7 -0
- package/dist/index.js +504 -255
- package/dist/index.js.map +4 -4
- package/dist/native-sessions/codex-history.d.ts.map +1 -1
- package/dist/native-sessions/codex-history.js +4 -3
- package/dist/native-sessions/codex-history.js.map +1 -1
- package/dist/platform/contracts.d.ts +21 -0
- package/dist/platform/contracts.d.ts.map +1 -1
- package/dist/platform/index.d.ts.map +1 -1
- package/dist/platform/index.js +5 -3
- package/dist/platform/index.js.map +1 -1
- package/dist/platform/linux.d.ts.map +1 -1
- package/dist/platform/linux.js +2 -1
- package/dist/platform/linux.js.map +1 -1
- package/dist/platform/macos.d.ts.map +1 -1
- package/dist/platform/macos.js +2 -1
- package/dist/platform/macos.js.map +1 -1
- package/dist/platform/posix.d.ts +28 -1
- package/dist/platform/posix.d.ts.map +1 -1
- package/dist/platform/posix.js +91 -1
- package/dist/platform/posix.js.map +1 -1
- package/dist/platform/windows.d.ts +3 -0
- package/dist/platform/windows.d.ts.map +1 -0
- package/dist/platform/windows.js +174 -0
- package/dist/platform/windows.js.map +1 -0
- package/dist/process/executable.d.ts +7 -5
- package/dist/process/executable.d.ts.map +1 -1
- package/dist/process/executable.js +844 -386
- package/dist/process/executable.js.map +7 -1
- package/dist/process/process-error.d.ts +5 -0
- package/dist/process/process-error.d.ts.map +1 -0
- package/dist/process/process-error.js +9 -0
- package/dist/process/process-error.js.map +1 -0
- package/dist/runtime.js +504 -255
- package/dist/runtime.js.map +4 -4
- package/dist/storage/runtime-installation-store.d.ts.map +1 -1
- package/dist/storage/runtime-installation-store.js +3 -0
- package/dist/storage/runtime-installation-store.js.map +1 -1
- package/package.json +76 -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,
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
-
|
|
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
|
-
|
|
110
|
-
|
|
111
|
-
return
|
|
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
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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
|
-
|
|
119
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
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
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
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
|
-
|
|
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
|
|
226
|
-
|
|
227
|
-
|
|
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
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
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
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
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
|
-
|
|
246
|
-
|
|
247
|
-
if (childExited && processes.length === 0) {
|
|
248
|
-
return;
|
|
828
|
+
if (interruptionFailure !== null) {
|
|
829
|
+
throw interruptionFailure;
|
|
249
830
|
}
|
|
250
|
-
|
|
251
|
-
|
|
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
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
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
|
-
|
|
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
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
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
|