@ai-development-environment/control-agent 0.0.77 → 0.0.79
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/control-agent.js +265 -114
- package/package.json +1 -1
package/dist/control-agent.js
CHANGED
|
@@ -2276,7 +2276,7 @@ var require_websocket = __commonJS({
|
|
|
2276
2276
|
var http = __require("http");
|
|
2277
2277
|
var net = __require("net");
|
|
2278
2278
|
var tls = __require("tls");
|
|
2279
|
-
var { randomBytes, createHash:
|
|
2279
|
+
var { randomBytes, createHash: createHash7 } = __require("crypto");
|
|
2280
2280
|
var { Duplex, Readable: Readable2 } = __require("stream");
|
|
2281
2281
|
var { URL: URL2 } = __require("url");
|
|
2282
2282
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
@@ -2944,7 +2944,7 @@ var require_websocket = __commonJS({
|
|
|
2944
2944
|
abortHandshake(websocket, socket, "Invalid Upgrade header");
|
|
2945
2945
|
return;
|
|
2946
2946
|
}
|
|
2947
|
-
const digest =
|
|
2947
|
+
const digest = createHash7("sha1").update(key + GUID).digest("base64");
|
|
2948
2948
|
if (res.headers["sec-websocket-accept"] !== digest) {
|
|
2949
2949
|
abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
|
|
2950
2950
|
return;
|
|
@@ -3313,7 +3313,7 @@ var require_websocket_server = __commonJS({
|
|
|
3313
3313
|
var EventEmitter = __require("events");
|
|
3314
3314
|
var http = __require("http");
|
|
3315
3315
|
var { Duplex } = __require("stream");
|
|
3316
|
-
var { createHash:
|
|
3316
|
+
var { createHash: createHash7 } = __require("crypto");
|
|
3317
3317
|
var extension2 = require_extension();
|
|
3318
3318
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
3319
3319
|
var subprotocol2 = require_subprotocol();
|
|
@@ -3620,7 +3620,7 @@ var require_websocket_server = __commonJS({
|
|
|
3620
3620
|
);
|
|
3621
3621
|
}
|
|
3622
3622
|
if (this._state > RUNNING) return abortHandshake(socket, 503);
|
|
3623
|
-
const digest =
|
|
3623
|
+
const digest = createHash7("sha1").update(key + GUID).digest("base64");
|
|
3624
3624
|
const headers = [
|
|
3625
3625
|
"HTTP/1.1 101 Switching Protocols",
|
|
3626
3626
|
"Upgrade: websocket",
|
|
@@ -8283,10 +8283,183 @@ function collectInventory() {
|
|
|
8283
8283
|
}
|
|
8284
8284
|
|
|
8285
8285
|
// src/handlers/commands.ts
|
|
8286
|
-
import { spawn } from "node:child_process";
|
|
8286
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
8287
8287
|
import { chmod as chmod2, mkdtemp, realpath, rm as rm2, writeFile as writeFile2 } from "node:fs/promises";
|
|
8288
8288
|
import { homedir as homedir2, tmpdir } from "node:os";
|
|
8289
8289
|
import { join as join2 } from "node:path";
|
|
8290
|
+
|
|
8291
|
+
// src/command-processes.ts
|
|
8292
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
8293
|
+
|
|
8294
|
+
// src/capture-command.ts
|
|
8295
|
+
import { spawn } from "node:child_process";
|
|
8296
|
+
var MAX_OUTPUT_BYTES = 2 * 1024 * 1024;
|
|
8297
|
+
function captureCommand(options) {
|
|
8298
|
+
if (options.signal.aborted || options.timeoutMs <= 0) {
|
|
8299
|
+
return Promise.resolve({
|
|
8300
|
+
exitCode: null,
|
|
8301
|
+
signal: null,
|
|
8302
|
+
timedOut: !options.signal.aborted,
|
|
8303
|
+
cancelled: options.signal.aborted,
|
|
8304
|
+
stdout: "",
|
|
8305
|
+
stderr: ""
|
|
8306
|
+
});
|
|
8307
|
+
}
|
|
8308
|
+
return new Promise((resolve8, reject) => {
|
|
8309
|
+
let stdout = "";
|
|
8310
|
+
let stderr = "";
|
|
8311
|
+
let timedOut = false;
|
|
8312
|
+
let cancelled = false;
|
|
8313
|
+
let settled = false;
|
|
8314
|
+
let killTimer = null;
|
|
8315
|
+
const child = spawn(options.command, options.args, {
|
|
8316
|
+
shell: false,
|
|
8317
|
+
stdio: [
|
|
8318
|
+
"ignore",
|
|
8319
|
+
options.stdoutFileDescriptor === void 0 ? "pipe" : options.stdoutFileDescriptor,
|
|
8320
|
+
"pipe"
|
|
8321
|
+
],
|
|
8322
|
+
env: options.env ?? process.env,
|
|
8323
|
+
cwd: options.cwd
|
|
8324
|
+
});
|
|
8325
|
+
const append = (current, chunk) => `${current}${String(chunk)}`.slice(0, MAX_OUTPUT_BYTES);
|
|
8326
|
+
child.stdout?.on("data", (chunk) => {
|
|
8327
|
+
stdout = append(stdout, chunk);
|
|
8328
|
+
});
|
|
8329
|
+
child.stderr?.on("data", (chunk) => {
|
|
8330
|
+
stderr = append(stderr, chunk);
|
|
8331
|
+
});
|
|
8332
|
+
const terminate = () => {
|
|
8333
|
+
if (child.exitCode !== null || child.killed) return;
|
|
8334
|
+
child.kill("SIGTERM");
|
|
8335
|
+
killTimer = setTimeout(() => {
|
|
8336
|
+
if (child.exitCode === null) child.kill("SIGKILL");
|
|
8337
|
+
}, 5e3);
|
|
8338
|
+
killTimer.unref();
|
|
8339
|
+
};
|
|
8340
|
+
const timeout = setTimeout(() => {
|
|
8341
|
+
timedOut = true;
|
|
8342
|
+
terminate();
|
|
8343
|
+
}, options.timeoutMs);
|
|
8344
|
+
timeout.unref();
|
|
8345
|
+
const abort = () => {
|
|
8346
|
+
cancelled = true;
|
|
8347
|
+
terminate();
|
|
8348
|
+
};
|
|
8349
|
+
options.signal.addEventListener("abort", abort, { once: true });
|
|
8350
|
+
if (options.signal.aborted) abort();
|
|
8351
|
+
child.once("error", (error) => {
|
|
8352
|
+
if (settled) return;
|
|
8353
|
+
settled = true;
|
|
8354
|
+
clearTimeout(timeout);
|
|
8355
|
+
if (killTimer) clearTimeout(killTimer);
|
|
8356
|
+
options.signal.removeEventListener("abort", abort);
|
|
8357
|
+
reject(error);
|
|
8358
|
+
});
|
|
8359
|
+
child.once("close", (exitCode, signal) => {
|
|
8360
|
+
if (settled) return;
|
|
8361
|
+
settled = true;
|
|
8362
|
+
clearTimeout(timeout);
|
|
8363
|
+
if (killTimer) clearTimeout(killTimer);
|
|
8364
|
+
options.signal.removeEventListener("abort", abort);
|
|
8365
|
+
resolve8({
|
|
8366
|
+
exitCode,
|
|
8367
|
+
signal,
|
|
8368
|
+
timedOut,
|
|
8369
|
+
cancelled,
|
|
8370
|
+
stdout,
|
|
8371
|
+
stderr
|
|
8372
|
+
});
|
|
8373
|
+
});
|
|
8374
|
+
});
|
|
8375
|
+
}
|
|
8376
|
+
|
|
8377
|
+
// src/command-processes.ts
|
|
8378
|
+
var COMMAND_SCRIPT_PREFIX = "aide-command-";
|
|
8379
|
+
var TERMINATE_GRACE_MS = 5e3;
|
|
8380
|
+
function commandScriptPrefix(agentId) {
|
|
8381
|
+
const marker = createHash3("sha256").update(agentId).digest("hex").slice(0, 16);
|
|
8382
|
+
return `${COMMAND_SCRIPT_PREFIX}${marker}-`;
|
|
8383
|
+
}
|
|
8384
|
+
var scriptPattern = (agentId) => `${commandScriptPrefix(agentId)}[^/]*/command\\.sh`;
|
|
8385
|
+
function parseCommandProcesses(output, ownPid) {
|
|
8386
|
+
const groups = /* @__PURE__ */ new Map();
|
|
8387
|
+
for (const line of output.split("\n")) {
|
|
8388
|
+
const [rawPid, rawGroup] = line.trim().split(/\s+/);
|
|
8389
|
+
const pid = Number(rawPid);
|
|
8390
|
+
const processGroup = Number(rawGroup);
|
|
8391
|
+
if (!Number.isInteger(pid) || !Number.isInteger(processGroup)) continue;
|
|
8392
|
+
if (processGroup <= 1 || pid <= 1) continue;
|
|
8393
|
+
if (pid === ownPid || processGroup === ownPid) continue;
|
|
8394
|
+
const known = groups.get(processGroup);
|
|
8395
|
+
if (!known || pid === processGroup) {
|
|
8396
|
+
groups.set(processGroup, { pid, processGroup });
|
|
8397
|
+
}
|
|
8398
|
+
}
|
|
8399
|
+
return [...groups.values()];
|
|
8400
|
+
}
|
|
8401
|
+
async function listCommandProcesses(agentId, signal) {
|
|
8402
|
+
const found = await captureCommand({
|
|
8403
|
+
command: "/usr/bin/pgrep",
|
|
8404
|
+
args: ["-f", scriptPattern(agentId)],
|
|
8405
|
+
timeoutMs: 5e3,
|
|
8406
|
+
signal
|
|
8407
|
+
});
|
|
8408
|
+
const pids = found.stdout.split("\n").map((line) => line.trim()).filter((line) => /^\d+$/.test(line));
|
|
8409
|
+
if (!pids.length) return [];
|
|
8410
|
+
const described = await captureCommand({
|
|
8411
|
+
command: "/bin/ps",
|
|
8412
|
+
args: ["-o", "pid=,pgid=", "-p", pids.join(",")],
|
|
8413
|
+
timeoutMs: 5e3,
|
|
8414
|
+
signal
|
|
8415
|
+
});
|
|
8416
|
+
return parseCommandProcesses(described.stdout, process.pid);
|
|
8417
|
+
}
|
|
8418
|
+
function signalGroup(processGroup, name) {
|
|
8419
|
+
try {
|
|
8420
|
+
process.kill(-processGroup, name);
|
|
8421
|
+
return true;
|
|
8422
|
+
} catch (error) {
|
|
8423
|
+
if (error instanceof Error && "code" in error && error.code === "ESRCH") {
|
|
8424
|
+
return false;
|
|
8425
|
+
}
|
|
8426
|
+
console.error(
|
|
8427
|
+
`Could not signal orphaned command process group ${processGroup}:`,
|
|
8428
|
+
error instanceof Error ? error.message : error
|
|
8429
|
+
);
|
|
8430
|
+
return false;
|
|
8431
|
+
}
|
|
8432
|
+
}
|
|
8433
|
+
async function reapOrphanedCommandProcesses(agentId, signal = new AbortController().signal) {
|
|
8434
|
+
if (process.platform === "win32") return [];
|
|
8435
|
+
let orphans;
|
|
8436
|
+
try {
|
|
8437
|
+
orphans = await listCommandProcesses(agentId, signal);
|
|
8438
|
+
} catch (error) {
|
|
8439
|
+
console.error(
|
|
8440
|
+
"Could not look for orphaned command processes:",
|
|
8441
|
+
error instanceof Error ? error.message : error
|
|
8442
|
+
);
|
|
8443
|
+
return [];
|
|
8444
|
+
}
|
|
8445
|
+
const signalled = orphans.filter(
|
|
8446
|
+
(orphan) => signalGroup(orphan.processGroup, "SIGTERM")
|
|
8447
|
+
);
|
|
8448
|
+
if (!signalled.length) return [];
|
|
8449
|
+
console.error(
|
|
8450
|
+
`Terminating ${signalled.length} command process(es) left by a previous agent run.`
|
|
8451
|
+
);
|
|
8452
|
+
await new Promise((resolve8) => {
|
|
8453
|
+
const timer = setTimeout(resolve8, TERMINATE_GRACE_MS);
|
|
8454
|
+
timer.unref();
|
|
8455
|
+
if (signal.aborted) resolve8();
|
|
8456
|
+
else signal.addEventListener("abort", () => resolve8(), { once: true });
|
|
8457
|
+
});
|
|
8458
|
+
for (const orphan of signalled) signalGroup(orphan.processGroup, "SIGKILL");
|
|
8459
|
+
return signalled;
|
|
8460
|
+
}
|
|
8461
|
+
|
|
8462
|
+
// src/handlers/commands.ts
|
|
8290
8463
|
var MAX_BATCH_BYTES = 128 * 1024;
|
|
8291
8464
|
var FLUSH_DELAY_MS = 25;
|
|
8292
8465
|
var MAX_UPLOAD_ATTEMPTS = 8;
|
|
@@ -8295,7 +8468,9 @@ var runCommand = async (rawPayload, _timeoutMs, signal, _onLog, context) => {
|
|
|
8295
8468
|
if (!context?.appendCommandOutput) {
|
|
8296
8469
|
throw new Error("Command output uploader is unavailable");
|
|
8297
8470
|
}
|
|
8298
|
-
const directory = await mkdtemp(
|
|
8471
|
+
const directory = await mkdtemp(
|
|
8472
|
+
join2(tmpdir(), commandScriptPrefix(context.agentId))
|
|
8473
|
+
);
|
|
8299
8474
|
const scriptPath = join2(directory, "command.sh");
|
|
8300
8475
|
await writeFile2(scriptPath, payload.script, {
|
|
8301
8476
|
encoding: "utf8",
|
|
@@ -8382,7 +8557,7 @@ var runCommand = async (rawPayload, _timeoutMs, signal, _onLog, context) => {
|
|
|
8382
8557
|
return await new Promise((resolve8, reject) => {
|
|
8383
8558
|
let cancelled = false;
|
|
8384
8559
|
let settled = false;
|
|
8385
|
-
const child =
|
|
8560
|
+
const child = spawn2(process.env.SHELL || "/bin/sh", ["-l", scriptPath], {
|
|
8386
8561
|
cwd,
|
|
8387
8562
|
detached: process.platform !== "win32",
|
|
8388
8563
|
env: {
|
|
@@ -8466,7 +8641,7 @@ import { readFile as readFile3, realpath as realpath3, stat as stat3 } from "nod
|
|
|
8466
8641
|
import { join as join4, relative as relative4, sep as sep4 } from "node:path";
|
|
8467
8642
|
|
|
8468
8643
|
// src/handlers/builds.ts
|
|
8469
|
-
import { createHash as
|
|
8644
|
+
import { createHash as createHash5, randomUUID } from "node:crypto";
|
|
8470
8645
|
import {
|
|
8471
8646
|
cp,
|
|
8472
8647
|
mkdir as mkdir2,
|
|
@@ -8643,91 +8818,8 @@ function parsePlist(xml) {
|
|
|
8643
8818
|
return parseValue(xml, from, 0)[0];
|
|
8644
8819
|
}
|
|
8645
8820
|
|
|
8646
|
-
// src/capture-command.ts
|
|
8647
|
-
import { spawn as spawn2 } from "node:child_process";
|
|
8648
|
-
var MAX_OUTPUT_BYTES = 2 * 1024 * 1024;
|
|
8649
|
-
function captureCommand(options) {
|
|
8650
|
-
if (options.signal.aborted || options.timeoutMs <= 0) {
|
|
8651
|
-
return Promise.resolve({
|
|
8652
|
-
exitCode: null,
|
|
8653
|
-
signal: null,
|
|
8654
|
-
timedOut: !options.signal.aborted,
|
|
8655
|
-
cancelled: options.signal.aborted,
|
|
8656
|
-
stdout: "",
|
|
8657
|
-
stderr: ""
|
|
8658
|
-
});
|
|
8659
|
-
}
|
|
8660
|
-
return new Promise((resolve8, reject) => {
|
|
8661
|
-
let stdout = "";
|
|
8662
|
-
let stderr = "";
|
|
8663
|
-
let timedOut = false;
|
|
8664
|
-
let cancelled = false;
|
|
8665
|
-
let settled = false;
|
|
8666
|
-
let killTimer = null;
|
|
8667
|
-
const child = spawn2(options.command, options.args, {
|
|
8668
|
-
shell: false,
|
|
8669
|
-
stdio: [
|
|
8670
|
-
"ignore",
|
|
8671
|
-
options.stdoutFileDescriptor === void 0 ? "pipe" : options.stdoutFileDescriptor,
|
|
8672
|
-
"pipe"
|
|
8673
|
-
],
|
|
8674
|
-
env: options.env ?? process.env,
|
|
8675
|
-
cwd: options.cwd
|
|
8676
|
-
});
|
|
8677
|
-
const append = (current, chunk) => `${current}${String(chunk)}`.slice(0, MAX_OUTPUT_BYTES);
|
|
8678
|
-
child.stdout?.on("data", (chunk) => {
|
|
8679
|
-
stdout = append(stdout, chunk);
|
|
8680
|
-
});
|
|
8681
|
-
child.stderr?.on("data", (chunk) => {
|
|
8682
|
-
stderr = append(stderr, chunk);
|
|
8683
|
-
});
|
|
8684
|
-
const terminate = () => {
|
|
8685
|
-
if (child.exitCode !== null || child.killed) return;
|
|
8686
|
-
child.kill("SIGTERM");
|
|
8687
|
-
killTimer = setTimeout(() => {
|
|
8688
|
-
if (child.exitCode === null) child.kill("SIGKILL");
|
|
8689
|
-
}, 5e3);
|
|
8690
|
-
killTimer.unref();
|
|
8691
|
-
};
|
|
8692
|
-
const timeout = setTimeout(() => {
|
|
8693
|
-
timedOut = true;
|
|
8694
|
-
terminate();
|
|
8695
|
-
}, options.timeoutMs);
|
|
8696
|
-
timeout.unref();
|
|
8697
|
-
const abort = () => {
|
|
8698
|
-
cancelled = true;
|
|
8699
|
-
terminate();
|
|
8700
|
-
};
|
|
8701
|
-
options.signal.addEventListener("abort", abort, { once: true });
|
|
8702
|
-
if (options.signal.aborted) abort();
|
|
8703
|
-
child.once("error", (error) => {
|
|
8704
|
-
if (settled) return;
|
|
8705
|
-
settled = true;
|
|
8706
|
-
clearTimeout(timeout);
|
|
8707
|
-
if (killTimer) clearTimeout(killTimer);
|
|
8708
|
-
options.signal.removeEventListener("abort", abort);
|
|
8709
|
-
reject(error);
|
|
8710
|
-
});
|
|
8711
|
-
child.once("close", (exitCode, signal) => {
|
|
8712
|
-
if (settled) return;
|
|
8713
|
-
settled = true;
|
|
8714
|
-
clearTimeout(timeout);
|
|
8715
|
-
if (killTimer) clearTimeout(killTimer);
|
|
8716
|
-
options.signal.removeEventListener("abort", abort);
|
|
8717
|
-
resolve8({
|
|
8718
|
-
exitCode,
|
|
8719
|
-
signal,
|
|
8720
|
-
timedOut,
|
|
8721
|
-
cancelled,
|
|
8722
|
-
stdout,
|
|
8723
|
-
stderr
|
|
8724
|
-
});
|
|
8725
|
-
});
|
|
8726
|
-
});
|
|
8727
|
-
}
|
|
8728
|
-
|
|
8729
8821
|
// src/git-code-state.ts
|
|
8730
|
-
import { createHash as
|
|
8822
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
8731
8823
|
import { createReadStream as createReadStream2 } from "node:fs";
|
|
8732
8824
|
import { lstat, readlink } from "node:fs/promises";
|
|
8733
8825
|
import { isAbsolute as isAbsolute2, relative as relative2, resolve as resolve2, sep as sep2 } from "node:path";
|
|
@@ -8854,7 +8946,7 @@ async function worktreeCodeStateHash(folder, timeoutMs, signal) {
|
|
|
8854
8946
|
if (head.exitCode !== 0 || untracked.exitCode !== 0 || submodules.exitCode !== 0 || operation.signal.aborted) {
|
|
8855
8947
|
return null;
|
|
8856
8948
|
}
|
|
8857
|
-
const hash =
|
|
8949
|
+
const hash = createHash4("sha256");
|
|
8858
8950
|
hash.update("head\0");
|
|
8859
8951
|
hash.update(head.stdout.trim());
|
|
8860
8952
|
hash.update("\0diff\0");
|
|
@@ -10209,7 +10301,7 @@ async function fileChecksum(path) {
|
|
|
10209
10301
|
try {
|
|
10210
10302
|
const information = await stat2(path);
|
|
10211
10303
|
if (!information.isFile()) return null;
|
|
10212
|
-
const hash =
|
|
10304
|
+
const hash = createHash5("sha256");
|
|
10213
10305
|
await pipeline(createReadStream3(path), hash);
|
|
10214
10306
|
return hash.digest("hex");
|
|
10215
10307
|
} catch {
|
|
@@ -11563,7 +11655,7 @@ var importCoverageReport = async (payloadValue, timeoutMs, signal, onLog) => {
|
|
|
11563
11655
|
};
|
|
11564
11656
|
|
|
11565
11657
|
// src/handlers/signing.ts
|
|
11566
|
-
import { createHash as
|
|
11658
|
+
import { createHash as createHash6, randomUUID as randomUUID2, X509Certificate } from "node:crypto";
|
|
11567
11659
|
import {
|
|
11568
11660
|
mkdir as mkdir3,
|
|
11569
11661
|
mkdtemp as mkdtemp3,
|
|
@@ -11656,7 +11748,7 @@ function certificateFingerprints(value) {
|
|
|
11656
11748
|
try {
|
|
11657
11749
|
const der = Buffer.from(encoded.replace(/\s+/g, ""), "base64");
|
|
11658
11750
|
if (!der.length) return [];
|
|
11659
|
-
return [
|
|
11751
|
+
return [createHash6("sha1").update(der).digest("hex").toUpperCase()];
|
|
11660
11752
|
} catch {
|
|
11661
11753
|
return [];
|
|
11662
11754
|
}
|
|
@@ -11737,7 +11829,7 @@ function profileAsset(decoded, now) {
|
|
|
11737
11829
|
const createdAt = text(decoded.raw.CreationDate);
|
|
11738
11830
|
return {
|
|
11739
11831
|
uuid: profile.uuid,
|
|
11740
|
-
contentHash:
|
|
11832
|
+
contentHash: createHash6("sha256").update(decoded.content).digest("hex").toUpperCase(),
|
|
11741
11833
|
name: profile.name,
|
|
11742
11834
|
profileType: profile.type,
|
|
11743
11835
|
bundleId: profile.bundleId,
|
|
@@ -12031,7 +12123,7 @@ var readSigningProfile = async (payload, timeoutMs, signal) => {
|
|
|
12031
12123
|
...successfulProcess3,
|
|
12032
12124
|
uuid,
|
|
12033
12125
|
contentBase64: profile.content.toString("base64"),
|
|
12034
|
-
sha256:
|
|
12126
|
+
sha256: createHash6("sha256").update(profile.content).digest("hex").toUpperCase()
|
|
12035
12127
|
};
|
|
12036
12128
|
};
|
|
12037
12129
|
var installSigningProfile = async (payload, timeoutMs, signal) => {
|
|
@@ -12073,7 +12165,7 @@ var importSigningIdentity = async (payload, timeoutMs, signal, _onLog, context)
|
|
|
12073
12165
|
}
|
|
12074
12166
|
const secret = await context.claimSigningSecretTransfer(input.transferId);
|
|
12075
12167
|
const p12 = Buffer.from(secret.p12Base64, "base64");
|
|
12076
|
-
const hash =
|
|
12168
|
+
const hash = createHash6("sha256").update(p12).digest("hex").toUpperCase();
|
|
12077
12169
|
if (hash !== input.sha256)
|
|
12078
12170
|
throw new Error("Signing identity transfer changed");
|
|
12079
12171
|
const temporaryRoot = await mkdtemp3(join5(tmpdir3(), "ade-identity-"));
|
|
@@ -12136,20 +12228,28 @@ function isExecutable(path) {
|
|
|
12136
12228
|
return false;
|
|
12137
12229
|
}
|
|
12138
12230
|
}
|
|
12139
|
-
function
|
|
12140
|
-
const env = options.env ?? process.env;
|
|
12231
|
+
function toolDirectories(options = {}) {
|
|
12141
12232
|
const home = options.home ?? homedir4();
|
|
12142
12233
|
const platform2 = options.platform ?? process.platform;
|
|
12143
|
-
|
|
12144
|
-
const directories = [
|
|
12145
|
-
...(env.PATH ?? "").split(delimiter).filter(Boolean),
|
|
12234
|
+
return [
|
|
12146
12235
|
join6(home, ".local", "bin"),
|
|
12147
12236
|
join6(home, ".bun", "bin"),
|
|
12148
12237
|
join6(home, ".npm-global", "bin"),
|
|
12238
|
+
// Where the opencode.ai install script puts its binary.
|
|
12239
|
+
join6(home, ".opencode", "bin"),
|
|
12149
12240
|
// Global npm installs land next to the node binary running this agent.
|
|
12150
12241
|
options.nodeDirectory ?? dirname3(process.execPath),
|
|
12151
12242
|
...platform2 === "darwin" ? ["/opt/homebrew/bin", "/usr/local/bin"] : []
|
|
12152
12243
|
];
|
|
12244
|
+
}
|
|
12245
|
+
function findExecutable(name, options = {}) {
|
|
12246
|
+
const env = options.env ?? process.env;
|
|
12247
|
+
const platform2 = options.platform ?? process.platform;
|
|
12248
|
+
const executable = platform2 === "win32" ? `${name}.exe` : name;
|
|
12249
|
+
const directories = [
|
|
12250
|
+
...(env.PATH ?? "").split(delimiter).filter(Boolean),
|
|
12251
|
+
...toolDirectories(options)
|
|
12252
|
+
];
|
|
12153
12253
|
const override = options.overrideVariable ? env[options.overrideVariable]?.trim() : void 0;
|
|
12154
12254
|
const candidates = [
|
|
12155
12255
|
...override ? [override] : [],
|
|
@@ -12158,6 +12258,26 @@ function findExecutable(name, options = {}) {
|
|
|
12158
12258
|
const canExecute = options.isExecutable ?? isExecutable;
|
|
12159
12259
|
return [...new Set(candidates)].find(canExecute);
|
|
12160
12260
|
}
|
|
12261
|
+
function extendedPath(options = {}) {
|
|
12262
|
+
const env = options.env ?? process.env;
|
|
12263
|
+
const existing = (env.PATH ?? "").split(delimiter).filter(Boolean);
|
|
12264
|
+
const additions = toolDirectories(options).filter(
|
|
12265
|
+
(directory) => !existing.includes(directory)
|
|
12266
|
+
);
|
|
12267
|
+
return [...existing, ...additions].join(delimiter);
|
|
12268
|
+
}
|
|
12269
|
+
function repairToolPath(options = {}) {
|
|
12270
|
+
const env = options.env ?? process.env;
|
|
12271
|
+
env.PATH = extendedPath(options);
|
|
12272
|
+
}
|
|
12273
|
+
function prependPathDirectory(directory, env = process.env) {
|
|
12274
|
+
const existing = (env.PATH ?? "").split(delimiter).filter(Boolean);
|
|
12275
|
+
if (existing[0] === directory) return;
|
|
12276
|
+
env.PATH = [
|
|
12277
|
+
directory,
|
|
12278
|
+
...existing.filter((entry) => entry !== directory)
|
|
12279
|
+
].join(delimiter);
|
|
12280
|
+
}
|
|
12161
12281
|
|
|
12162
12282
|
// src/process-runner.ts
|
|
12163
12283
|
import { spawn as spawn5 } from "node:child_process";
|
|
@@ -16726,6 +16846,7 @@ var JobExecutor = class {
|
|
|
16726
16846
|
controller.signal,
|
|
16727
16847
|
(log) => this.client.appendLog(claimed.id, log).then(() => void 0),
|
|
16728
16848
|
{
|
|
16849
|
+
agentId: claimed.agentId,
|
|
16729
16850
|
reportWorktreeActivity: (input) => this.client.reportWorktreeActivity(input),
|
|
16730
16851
|
reportBuildProgress: (input) => this.client.reportBuildProgress(input),
|
|
16731
16852
|
appendBuildLogChunks: (buildId, chunks) => this.client.appendBuildLogChunks(buildId, chunks),
|
|
@@ -16909,7 +17030,7 @@ var CodebaseMonitor = class {
|
|
|
16909
17030
|
// src/runs/run-manager.ts
|
|
16910
17031
|
import { execFile as execFile4 } from "node:child_process";
|
|
16911
17032
|
import { mkdir as mkdir6, rm as rm13 } from "node:fs/promises";
|
|
16912
|
-
import { dirname as
|
|
17033
|
+
import { dirname as dirname10, join as join17 } from "node:path";
|
|
16913
17034
|
import { promisify as promisify4 } from "node:util";
|
|
16914
17035
|
|
|
16915
17036
|
// src/runs/journal.ts
|
|
@@ -43291,10 +43412,24 @@ var CodexAppServer = class {
|
|
|
43291
43412
|
this.initialized ??= this.start();
|
|
43292
43413
|
return this.initialized;
|
|
43293
43414
|
}
|
|
43415
|
+
// The service PATH omits Homebrew and npm, so resolve the install once and
|
|
43416
|
+
// use the absolute path for every codex invocation.
|
|
43417
|
+
executable() {
|
|
43418
|
+
const executable = findExecutable("codex", {
|
|
43419
|
+
overrideVariable: "CONTROL_AGENT_CODEX_EXECUTABLE"
|
|
43420
|
+
});
|
|
43421
|
+
if (!executable) {
|
|
43422
|
+
throw new Error(
|
|
43423
|
+
"codex was not found. Install it (brew install codex, or npm install -g @openai/codex) or set CONTROL_AGENT_CODEX_EXECUTABLE to its full path."
|
|
43424
|
+
);
|
|
43425
|
+
}
|
|
43426
|
+
return executable;
|
|
43427
|
+
}
|
|
43294
43428
|
async start() {
|
|
43295
|
-
const
|
|
43429
|
+
const executable = this.executable();
|
|
43430
|
+
const modelCatalogPath = await this.createBundledModelCatalog(executable);
|
|
43296
43431
|
const modelCatalogDirectory = this.modelCatalogDirectory;
|
|
43297
|
-
const child = spawn7(
|
|
43432
|
+
const child = spawn7(executable, codexAppServerArgs(modelCatalogPath), {
|
|
43298
43433
|
stdio: ["pipe", "pipe", "pipe"],
|
|
43299
43434
|
env: process.env
|
|
43300
43435
|
});
|
|
@@ -43362,11 +43497,11 @@ var CodexAppServer = class {
|
|
|
43362
43497
|
);
|
|
43363
43498
|
}
|
|
43364
43499
|
}
|
|
43365
|
-
async createBundledModelCatalog() {
|
|
43500
|
+
async createBundledModelCatalog(executable) {
|
|
43366
43501
|
const directory = await mkdtemp7(join16(tmpdir7(), "aide-codex-models-"));
|
|
43367
43502
|
try {
|
|
43368
43503
|
const { stdout } = await execFileAsync2(
|
|
43369
|
-
|
|
43504
|
+
executable,
|
|
43370
43505
|
["debug", "models", "--bundled"],
|
|
43371
43506
|
{
|
|
43372
43507
|
encoding: "utf8",
|
|
@@ -43804,6 +43939,7 @@ var CodexAdapter = class {
|
|
|
43804
43939
|
};
|
|
43805
43940
|
|
|
43806
43941
|
// src/runs/opencode-adapter.ts
|
|
43942
|
+
import { dirname as dirname9 } from "node:path";
|
|
43807
43943
|
import { pathToFileURL } from "node:url";
|
|
43808
43944
|
|
|
43809
43945
|
// ../../node_modules/@opencode-ai/sdk/dist/v2/gen/core/serverSentEvents.gen.js
|
|
@@ -49475,7 +49611,18 @@ var OpenCodeAdapter = class {
|
|
|
49475
49611
|
};
|
|
49476
49612
|
runtime;
|
|
49477
49613
|
async client() {
|
|
49478
|
-
this.runtime
|
|
49614
|
+
if (!this.runtime) {
|
|
49615
|
+
const executable = findExecutable("opencode", {
|
|
49616
|
+
overrideVariable: "CONTROL_AGENT_OPENCODE_EXECUTABLE"
|
|
49617
|
+
});
|
|
49618
|
+
if (!executable) {
|
|
49619
|
+
throw new Error(
|
|
49620
|
+
"opencode was not found. Install it (brew install sst/tap/opencode, or npm install -g opencode-ai) or set CONTROL_AGENT_OPENCODE_EXECUTABLE to its full path."
|
|
49621
|
+
);
|
|
49622
|
+
}
|
|
49623
|
+
prependPathDirectory(dirname9(executable));
|
|
49624
|
+
this.runtime = await createOpencode();
|
|
49625
|
+
}
|
|
49479
49626
|
return this.runtime.client;
|
|
49480
49627
|
}
|
|
49481
49628
|
async catalog() {
|
|
@@ -50097,7 +50244,7 @@ var RunManager = class {
|
|
|
50097
50244
|
5
|
|
50098
50245
|
);
|
|
50099
50246
|
await this.completeCommand(command3.id, "SUCCEEDED");
|
|
50100
|
-
await rm13(join17(
|
|
50247
|
+
await rm13(join17(dirname10(configPath()), "runs", run.id, "attachments"), {
|
|
50101
50248
|
recursive: true,
|
|
50102
50249
|
force: true
|
|
50103
50250
|
}).catch(() => void 0);
|
|
@@ -50279,7 +50426,7 @@ var RunManager = class {
|
|
|
50279
50426
|
this.active.delete(run.id);
|
|
50280
50427
|
this.interruptionRequests.delete(run.id);
|
|
50281
50428
|
this.executingCommands.delete(command3.id);
|
|
50282
|
-
await rm13(join17(
|
|
50429
|
+
await rm13(join17(dirname10(configPath()), "runs", run.id, "attachments"), {
|
|
50283
50430
|
recursive: true,
|
|
50284
50431
|
force: true
|
|
50285
50432
|
}).catch(() => void 0);
|
|
@@ -50507,7 +50654,7 @@ var RunManager = class {
|
|
|
50507
50654
|
}
|
|
50508
50655
|
async stageAttachments(runId, attachments) {
|
|
50509
50656
|
if (!attachments.length) return [];
|
|
50510
|
-
const directory = join17(
|
|
50657
|
+
const directory = join17(dirname10(configPath()), "runs", runId, "attachments");
|
|
50511
50658
|
await mkdir6(directory, { recursive: true, mode: 448 });
|
|
50512
50659
|
return Promise.all(
|
|
50513
50660
|
attachments.map(async (attachment) => {
|
|
@@ -51043,6 +51190,7 @@ async function prepareDevelopmentAgent(options, signal, dependencies = defaultDe
|
|
|
51043
51190
|
}
|
|
51044
51191
|
async function runDevelopmentAgent(options, signal) {
|
|
51045
51192
|
const config = await prepareDevelopmentAgent(options, signal);
|
|
51193
|
+
await reapOrphanedCommandProcesses(config.agentId, signal);
|
|
51046
51194
|
await runAgent(config, signal);
|
|
51047
51195
|
}
|
|
51048
51196
|
|
|
@@ -51285,6 +51433,7 @@ async function doctor() {
|
|
|
51285
51433
|
if (checks.some((check) => !check.ok)) process.exitCode = 1;
|
|
51286
51434
|
}
|
|
51287
51435
|
async function main() {
|
|
51436
|
+
repairToolPath();
|
|
51288
51437
|
const [command3, ...args] = process.argv.slice(2);
|
|
51289
51438
|
if (!command3 || command3 === "help" || command3 === "--help") {
|
|
51290
51439
|
usage();
|
|
@@ -51314,7 +51463,9 @@ async function main() {
|
|
|
51314
51463
|
controller.signal
|
|
51315
51464
|
);
|
|
51316
51465
|
}
|
|
51317
|
-
|
|
51466
|
+
const config = await loadConfig();
|
|
51467
|
+
await reapOrphanedCommandProcesses(config.agentId, controller.signal);
|
|
51468
|
+
return await runAgent(config, controller.signal);
|
|
51318
51469
|
} finally {
|
|
51319
51470
|
for (const signal of shutdownSignals) process.off(signal, stop2);
|
|
51320
51471
|
}
|