@ai-development-environment/control-agent 0.0.78 → 0.0.80
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 +374 -103
- 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",
|
|
@@ -6106,6 +6106,7 @@ function codebaseGitOperationPayload(value) {
|
|
|
6106
6106
|
// ../agent-contract/src/worktrees.ts
|
|
6107
6107
|
var WORKTREE_INSPECT_JOB_KIND = "worktree.inspect";
|
|
6108
6108
|
var WORKTREE_OPERATION_JOB_KIND = "worktree.operation";
|
|
6109
|
+
var WORKTREE_COMMIT_JOB_KIND = "worktree.commit";
|
|
6109
6110
|
var WORKTREE_AUTO_SYNC_JOB_KIND = "worktree.auto-sync";
|
|
6110
6111
|
var WORKTREE_GIT_INSPECT_JOB_KIND = "worktree.git.inspect";
|
|
6111
6112
|
var WORKTREE_GIT_OPERATION_JOB_KIND = "worktree.git.operation";
|
|
@@ -6119,6 +6120,7 @@ var WORKTREE_DIFF_ASSET_JOB_KIND = "worktree.diff.asset";
|
|
|
6119
6120
|
var WORKTREE_JOB_KINDS = [
|
|
6120
6121
|
WORKTREE_INSPECT_JOB_KIND,
|
|
6121
6122
|
WORKTREE_OPERATION_JOB_KIND,
|
|
6123
|
+
WORKTREE_COMMIT_JOB_KIND,
|
|
6122
6124
|
WORKTREE_AUTO_SYNC_JOB_KIND,
|
|
6123
6125
|
WORKTREE_GIT_INSPECT_JOB_KIND,
|
|
6124
6126
|
WORKTREE_GIT_OPERATION_JOB_KIND,
|
|
@@ -6145,6 +6147,7 @@ var WORKTREE_OPERATIONS = [
|
|
|
6145
6147
|
"SYNC",
|
|
6146
6148
|
"REBASE",
|
|
6147
6149
|
"CANCEL_REBASE",
|
|
6150
|
+
"PULL",
|
|
6148
6151
|
"PUSH",
|
|
6149
6152
|
"RESET",
|
|
6150
6153
|
"STASH_ALL",
|
|
@@ -6463,6 +6466,71 @@ function worktreeDeleteJobPayload(value) {
|
|
|
6463
6466
|
expectedHeadSha
|
|
6464
6467
|
};
|
|
6465
6468
|
}
|
|
6469
|
+
function worktreeCommitJobPayload(value) {
|
|
6470
|
+
const payload = objectValue4(value, "worktree commit payload");
|
|
6471
|
+
const allowed = /* @__PURE__ */ new Set([
|
|
6472
|
+
"codebaseId",
|
|
6473
|
+
"folder",
|
|
6474
|
+
"gitDirectory",
|
|
6475
|
+
"expectedOrigin",
|
|
6476
|
+
"baseBranch",
|
|
6477
|
+
"message",
|
|
6478
|
+
"signed",
|
|
6479
|
+
"stageAll",
|
|
6480
|
+
"paths"
|
|
6481
|
+
]);
|
|
6482
|
+
const unexpected = Object.keys(payload).find((key) => !allowed.has(key));
|
|
6483
|
+
if (unexpected) {
|
|
6484
|
+
throw new Error(`Unexpected worktree commit payload field: ${unexpected}`);
|
|
6485
|
+
}
|
|
6486
|
+
const message = stringValue4(
|
|
6487
|
+
payload.message,
|
|
6488
|
+
"worktree commit payload.message"
|
|
6489
|
+
);
|
|
6490
|
+
if (!message.trim()) throw new Error("Commit message must not be blank");
|
|
6491
|
+
if (message.length > 5e4) throw new Error("Commit message is too long");
|
|
6492
|
+
if (payload.signed !== null && typeof payload.signed !== "boolean") {
|
|
6493
|
+
throw new Error("worktree commit payload.signed must be a boolean or null");
|
|
6494
|
+
}
|
|
6495
|
+
if (typeof payload.stageAll !== "boolean") {
|
|
6496
|
+
throw new Error("worktree commit payload.stageAll must be a boolean");
|
|
6497
|
+
}
|
|
6498
|
+
if (!Array.isArray(payload.paths) || payload.paths.length > 500) {
|
|
6499
|
+
throw new Error("worktree commit payload.paths is invalid");
|
|
6500
|
+
}
|
|
6501
|
+
const paths = payload.paths.map(
|
|
6502
|
+
(path, index) => safeRelativePath(path, `worktree commit payload.paths[${index}]`)
|
|
6503
|
+
);
|
|
6504
|
+
if (new Set(paths).size !== paths.length) {
|
|
6505
|
+
throw new Error("worktree commit payload.paths must be unique");
|
|
6506
|
+
}
|
|
6507
|
+
if (!payload.stageAll && paths.length === 0) {
|
|
6508
|
+
throw new Error("A partial commit requires at least one path");
|
|
6509
|
+
}
|
|
6510
|
+
return {
|
|
6511
|
+
codebaseId: stringValue4(
|
|
6512
|
+
payload.codebaseId,
|
|
6513
|
+
"worktree commit payload.codebaseId"
|
|
6514
|
+
),
|
|
6515
|
+
folder: stringValue4(payload.folder, "worktree commit payload.folder"),
|
|
6516
|
+
gitDirectory: stringValue4(
|
|
6517
|
+
payload.gitDirectory,
|
|
6518
|
+
"worktree commit payload.gitDirectory"
|
|
6519
|
+
),
|
|
6520
|
+
expectedOrigin: stringValue4(
|
|
6521
|
+
payload.expectedOrigin,
|
|
6522
|
+
"worktree commit payload.expectedOrigin"
|
|
6523
|
+
),
|
|
6524
|
+
baseBranch: nullableString3(
|
|
6525
|
+
payload.baseBranch,
|
|
6526
|
+
"worktree commit payload.baseBranch"
|
|
6527
|
+
),
|
|
6528
|
+
message,
|
|
6529
|
+
signed: payload.signed,
|
|
6530
|
+
stageAll: payload.stageAll,
|
|
6531
|
+
paths
|
|
6532
|
+
};
|
|
6533
|
+
}
|
|
6466
6534
|
function worktreeJobPayload(value) {
|
|
6467
6535
|
const payload = objectValue4(value, "worktree payload");
|
|
6468
6536
|
const allowed = /* @__PURE__ */ new Set([
|
|
@@ -8283,10 +8351,183 @@ function collectInventory() {
|
|
|
8283
8351
|
}
|
|
8284
8352
|
|
|
8285
8353
|
// src/handlers/commands.ts
|
|
8286
|
-
import { spawn } from "node:child_process";
|
|
8354
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
8287
8355
|
import { chmod as chmod2, mkdtemp, realpath, rm as rm2, writeFile as writeFile2 } from "node:fs/promises";
|
|
8288
8356
|
import { homedir as homedir2, tmpdir } from "node:os";
|
|
8289
8357
|
import { join as join2 } from "node:path";
|
|
8358
|
+
|
|
8359
|
+
// src/command-processes.ts
|
|
8360
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
8361
|
+
|
|
8362
|
+
// src/capture-command.ts
|
|
8363
|
+
import { spawn } from "node:child_process";
|
|
8364
|
+
var MAX_OUTPUT_BYTES = 2 * 1024 * 1024;
|
|
8365
|
+
function captureCommand(options) {
|
|
8366
|
+
if (options.signal.aborted || options.timeoutMs <= 0) {
|
|
8367
|
+
return Promise.resolve({
|
|
8368
|
+
exitCode: null,
|
|
8369
|
+
signal: null,
|
|
8370
|
+
timedOut: !options.signal.aborted,
|
|
8371
|
+
cancelled: options.signal.aborted,
|
|
8372
|
+
stdout: "",
|
|
8373
|
+
stderr: ""
|
|
8374
|
+
});
|
|
8375
|
+
}
|
|
8376
|
+
return new Promise((resolve8, reject) => {
|
|
8377
|
+
let stdout = "";
|
|
8378
|
+
let stderr = "";
|
|
8379
|
+
let timedOut = false;
|
|
8380
|
+
let cancelled = false;
|
|
8381
|
+
let settled = false;
|
|
8382
|
+
let killTimer = null;
|
|
8383
|
+
const child = spawn(options.command, options.args, {
|
|
8384
|
+
shell: false,
|
|
8385
|
+
stdio: [
|
|
8386
|
+
"ignore",
|
|
8387
|
+
options.stdoutFileDescriptor === void 0 ? "pipe" : options.stdoutFileDescriptor,
|
|
8388
|
+
"pipe"
|
|
8389
|
+
],
|
|
8390
|
+
env: options.env ?? process.env,
|
|
8391
|
+
cwd: options.cwd
|
|
8392
|
+
});
|
|
8393
|
+
const append = (current, chunk) => `${current}${String(chunk)}`.slice(0, MAX_OUTPUT_BYTES);
|
|
8394
|
+
child.stdout?.on("data", (chunk) => {
|
|
8395
|
+
stdout = append(stdout, chunk);
|
|
8396
|
+
});
|
|
8397
|
+
child.stderr?.on("data", (chunk) => {
|
|
8398
|
+
stderr = append(stderr, chunk);
|
|
8399
|
+
});
|
|
8400
|
+
const terminate = () => {
|
|
8401
|
+
if (child.exitCode !== null || child.killed) return;
|
|
8402
|
+
child.kill("SIGTERM");
|
|
8403
|
+
killTimer = setTimeout(() => {
|
|
8404
|
+
if (child.exitCode === null) child.kill("SIGKILL");
|
|
8405
|
+
}, 5e3);
|
|
8406
|
+
killTimer.unref();
|
|
8407
|
+
};
|
|
8408
|
+
const timeout = setTimeout(() => {
|
|
8409
|
+
timedOut = true;
|
|
8410
|
+
terminate();
|
|
8411
|
+
}, options.timeoutMs);
|
|
8412
|
+
timeout.unref();
|
|
8413
|
+
const abort = () => {
|
|
8414
|
+
cancelled = true;
|
|
8415
|
+
terminate();
|
|
8416
|
+
};
|
|
8417
|
+
options.signal.addEventListener("abort", abort, { once: true });
|
|
8418
|
+
if (options.signal.aborted) abort();
|
|
8419
|
+
child.once("error", (error) => {
|
|
8420
|
+
if (settled) return;
|
|
8421
|
+
settled = true;
|
|
8422
|
+
clearTimeout(timeout);
|
|
8423
|
+
if (killTimer) clearTimeout(killTimer);
|
|
8424
|
+
options.signal.removeEventListener("abort", abort);
|
|
8425
|
+
reject(error);
|
|
8426
|
+
});
|
|
8427
|
+
child.once("close", (exitCode, signal) => {
|
|
8428
|
+
if (settled) return;
|
|
8429
|
+
settled = true;
|
|
8430
|
+
clearTimeout(timeout);
|
|
8431
|
+
if (killTimer) clearTimeout(killTimer);
|
|
8432
|
+
options.signal.removeEventListener("abort", abort);
|
|
8433
|
+
resolve8({
|
|
8434
|
+
exitCode,
|
|
8435
|
+
signal,
|
|
8436
|
+
timedOut,
|
|
8437
|
+
cancelled,
|
|
8438
|
+
stdout,
|
|
8439
|
+
stderr
|
|
8440
|
+
});
|
|
8441
|
+
});
|
|
8442
|
+
});
|
|
8443
|
+
}
|
|
8444
|
+
|
|
8445
|
+
// src/command-processes.ts
|
|
8446
|
+
var COMMAND_SCRIPT_PREFIX = "aide-command-";
|
|
8447
|
+
var TERMINATE_GRACE_MS = 5e3;
|
|
8448
|
+
function commandScriptPrefix(agentId) {
|
|
8449
|
+
const marker = createHash3("sha256").update(agentId).digest("hex").slice(0, 16);
|
|
8450
|
+
return `${COMMAND_SCRIPT_PREFIX}${marker}-`;
|
|
8451
|
+
}
|
|
8452
|
+
var scriptPattern = (agentId) => `${commandScriptPrefix(agentId)}[^/]*/command\\.sh`;
|
|
8453
|
+
function parseCommandProcesses(output, ownPid) {
|
|
8454
|
+
const groups = /* @__PURE__ */ new Map();
|
|
8455
|
+
for (const line of output.split("\n")) {
|
|
8456
|
+
const [rawPid, rawGroup] = line.trim().split(/\s+/);
|
|
8457
|
+
const pid = Number(rawPid);
|
|
8458
|
+
const processGroup = Number(rawGroup);
|
|
8459
|
+
if (!Number.isInteger(pid) || !Number.isInteger(processGroup)) continue;
|
|
8460
|
+
if (processGroup <= 1 || pid <= 1) continue;
|
|
8461
|
+
if (pid === ownPid || processGroup === ownPid) continue;
|
|
8462
|
+
const known = groups.get(processGroup);
|
|
8463
|
+
if (!known || pid === processGroup) {
|
|
8464
|
+
groups.set(processGroup, { pid, processGroup });
|
|
8465
|
+
}
|
|
8466
|
+
}
|
|
8467
|
+
return [...groups.values()];
|
|
8468
|
+
}
|
|
8469
|
+
async function listCommandProcesses(agentId, signal) {
|
|
8470
|
+
const found = await captureCommand({
|
|
8471
|
+
command: "/usr/bin/pgrep",
|
|
8472
|
+
args: ["-f", scriptPattern(agentId)],
|
|
8473
|
+
timeoutMs: 5e3,
|
|
8474
|
+
signal
|
|
8475
|
+
});
|
|
8476
|
+
const pids = found.stdout.split("\n").map((line) => line.trim()).filter((line) => /^\d+$/.test(line));
|
|
8477
|
+
if (!pids.length) return [];
|
|
8478
|
+
const described = await captureCommand({
|
|
8479
|
+
command: "/bin/ps",
|
|
8480
|
+
args: ["-o", "pid=,pgid=", "-p", pids.join(",")],
|
|
8481
|
+
timeoutMs: 5e3,
|
|
8482
|
+
signal
|
|
8483
|
+
});
|
|
8484
|
+
return parseCommandProcesses(described.stdout, process.pid);
|
|
8485
|
+
}
|
|
8486
|
+
function signalGroup(processGroup, name) {
|
|
8487
|
+
try {
|
|
8488
|
+
process.kill(-processGroup, name);
|
|
8489
|
+
return true;
|
|
8490
|
+
} catch (error) {
|
|
8491
|
+
if (error instanceof Error && "code" in error && error.code === "ESRCH") {
|
|
8492
|
+
return false;
|
|
8493
|
+
}
|
|
8494
|
+
console.error(
|
|
8495
|
+
`Could not signal orphaned command process group ${processGroup}:`,
|
|
8496
|
+
error instanceof Error ? error.message : error
|
|
8497
|
+
);
|
|
8498
|
+
return false;
|
|
8499
|
+
}
|
|
8500
|
+
}
|
|
8501
|
+
async function reapOrphanedCommandProcesses(agentId, signal = new AbortController().signal) {
|
|
8502
|
+
if (process.platform === "win32") return [];
|
|
8503
|
+
let orphans;
|
|
8504
|
+
try {
|
|
8505
|
+
orphans = await listCommandProcesses(agentId, signal);
|
|
8506
|
+
} catch (error) {
|
|
8507
|
+
console.error(
|
|
8508
|
+
"Could not look for orphaned command processes:",
|
|
8509
|
+
error instanceof Error ? error.message : error
|
|
8510
|
+
);
|
|
8511
|
+
return [];
|
|
8512
|
+
}
|
|
8513
|
+
const signalled = orphans.filter(
|
|
8514
|
+
(orphan) => signalGroup(orphan.processGroup, "SIGTERM")
|
|
8515
|
+
);
|
|
8516
|
+
if (!signalled.length) return [];
|
|
8517
|
+
console.error(
|
|
8518
|
+
`Terminating ${signalled.length} command process(es) left by a previous agent run.`
|
|
8519
|
+
);
|
|
8520
|
+
await new Promise((resolve8) => {
|
|
8521
|
+
const timer = setTimeout(resolve8, TERMINATE_GRACE_MS);
|
|
8522
|
+
timer.unref();
|
|
8523
|
+
if (signal.aborted) resolve8();
|
|
8524
|
+
else signal.addEventListener("abort", () => resolve8(), { once: true });
|
|
8525
|
+
});
|
|
8526
|
+
for (const orphan of signalled) signalGroup(orphan.processGroup, "SIGKILL");
|
|
8527
|
+
return signalled;
|
|
8528
|
+
}
|
|
8529
|
+
|
|
8530
|
+
// src/handlers/commands.ts
|
|
8290
8531
|
var MAX_BATCH_BYTES = 128 * 1024;
|
|
8291
8532
|
var FLUSH_DELAY_MS = 25;
|
|
8292
8533
|
var MAX_UPLOAD_ATTEMPTS = 8;
|
|
@@ -8295,7 +8536,9 @@ var runCommand = async (rawPayload, _timeoutMs, signal, _onLog, context) => {
|
|
|
8295
8536
|
if (!context?.appendCommandOutput) {
|
|
8296
8537
|
throw new Error("Command output uploader is unavailable");
|
|
8297
8538
|
}
|
|
8298
|
-
const directory = await mkdtemp(
|
|
8539
|
+
const directory = await mkdtemp(
|
|
8540
|
+
join2(tmpdir(), commandScriptPrefix(context.agentId))
|
|
8541
|
+
);
|
|
8299
8542
|
const scriptPath = join2(directory, "command.sh");
|
|
8300
8543
|
await writeFile2(scriptPath, payload.script, {
|
|
8301
8544
|
encoding: "utf8",
|
|
@@ -8382,7 +8625,7 @@ var runCommand = async (rawPayload, _timeoutMs, signal, _onLog, context) => {
|
|
|
8382
8625
|
return await new Promise((resolve8, reject) => {
|
|
8383
8626
|
let cancelled = false;
|
|
8384
8627
|
let settled = false;
|
|
8385
|
-
const child =
|
|
8628
|
+
const child = spawn2(process.env.SHELL || "/bin/sh", ["-l", scriptPath], {
|
|
8386
8629
|
cwd,
|
|
8387
8630
|
detached: process.platform !== "win32",
|
|
8388
8631
|
env: {
|
|
@@ -8466,7 +8709,7 @@ import { readFile as readFile3, realpath as realpath3, stat as stat3 } from "nod
|
|
|
8466
8709
|
import { join as join4, relative as relative4, sep as sep4 } from "node:path";
|
|
8467
8710
|
|
|
8468
8711
|
// src/handlers/builds.ts
|
|
8469
|
-
import { createHash as
|
|
8712
|
+
import { createHash as createHash5, randomUUID } from "node:crypto";
|
|
8470
8713
|
import {
|
|
8471
8714
|
cp,
|
|
8472
8715
|
mkdir as mkdir2,
|
|
@@ -8643,91 +8886,8 @@ function parsePlist(xml) {
|
|
|
8643
8886
|
return parseValue(xml, from, 0)[0];
|
|
8644
8887
|
}
|
|
8645
8888
|
|
|
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
8889
|
// src/git-code-state.ts
|
|
8730
|
-
import { createHash as
|
|
8890
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
8731
8891
|
import { createReadStream as createReadStream2 } from "node:fs";
|
|
8732
8892
|
import { lstat, readlink } from "node:fs/promises";
|
|
8733
8893
|
import { isAbsolute as isAbsolute2, relative as relative2, resolve as resolve2, sep as sep2 } from "node:path";
|
|
@@ -8854,7 +9014,7 @@ async function worktreeCodeStateHash(folder, timeoutMs, signal) {
|
|
|
8854
9014
|
if (head.exitCode !== 0 || untracked.exitCode !== 0 || submodules.exitCode !== 0 || operation.signal.aborted) {
|
|
8855
9015
|
return null;
|
|
8856
9016
|
}
|
|
8857
|
-
const hash =
|
|
9017
|
+
const hash = createHash4("sha256");
|
|
8858
9018
|
hash.update("head\0");
|
|
8859
9019
|
hash.update(head.stdout.trim());
|
|
8860
9020
|
hash.update("\0diff\0");
|
|
@@ -10209,7 +10369,7 @@ async function fileChecksum(path) {
|
|
|
10209
10369
|
try {
|
|
10210
10370
|
const information = await stat2(path);
|
|
10211
10371
|
if (!information.isFile()) return null;
|
|
10212
|
-
const hash =
|
|
10372
|
+
const hash = createHash5("sha256");
|
|
10213
10373
|
await pipeline(createReadStream3(path), hash);
|
|
10214
10374
|
return hash.digest("hex");
|
|
10215
10375
|
} catch {
|
|
@@ -11563,7 +11723,7 @@ var importCoverageReport = async (payloadValue, timeoutMs, signal, onLog) => {
|
|
|
11563
11723
|
};
|
|
11564
11724
|
|
|
11565
11725
|
// src/handlers/signing.ts
|
|
11566
|
-
import { createHash as
|
|
11726
|
+
import { createHash as createHash6, randomUUID as randomUUID2, X509Certificate } from "node:crypto";
|
|
11567
11727
|
import {
|
|
11568
11728
|
mkdir as mkdir3,
|
|
11569
11729
|
mkdtemp as mkdtemp3,
|
|
@@ -11656,7 +11816,7 @@ function certificateFingerprints(value) {
|
|
|
11656
11816
|
try {
|
|
11657
11817
|
const der = Buffer.from(encoded.replace(/\s+/g, ""), "base64");
|
|
11658
11818
|
if (!der.length) return [];
|
|
11659
|
-
return [
|
|
11819
|
+
return [createHash6("sha1").update(der).digest("hex").toUpperCase()];
|
|
11660
11820
|
} catch {
|
|
11661
11821
|
return [];
|
|
11662
11822
|
}
|
|
@@ -11737,7 +11897,7 @@ function profileAsset(decoded, now) {
|
|
|
11737
11897
|
const createdAt = text(decoded.raw.CreationDate);
|
|
11738
11898
|
return {
|
|
11739
11899
|
uuid: profile.uuid,
|
|
11740
|
-
contentHash:
|
|
11900
|
+
contentHash: createHash6("sha256").update(decoded.content).digest("hex").toUpperCase(),
|
|
11741
11901
|
name: profile.name,
|
|
11742
11902
|
profileType: profile.type,
|
|
11743
11903
|
bundleId: profile.bundleId,
|
|
@@ -12031,7 +12191,7 @@ var readSigningProfile = async (payload, timeoutMs, signal) => {
|
|
|
12031
12191
|
...successfulProcess3,
|
|
12032
12192
|
uuid,
|
|
12033
12193
|
contentBase64: profile.content.toString("base64"),
|
|
12034
|
-
sha256:
|
|
12194
|
+
sha256: createHash6("sha256").update(profile.content).digest("hex").toUpperCase()
|
|
12035
12195
|
};
|
|
12036
12196
|
};
|
|
12037
12197
|
var installSigningProfile = async (payload, timeoutMs, signal) => {
|
|
@@ -12073,7 +12233,7 @@ var importSigningIdentity = async (payload, timeoutMs, signal, _onLog, context)
|
|
|
12073
12233
|
}
|
|
12074
12234
|
const secret = await context.claimSigningSecretTransfer(input.transferId);
|
|
12075
12235
|
const p12 = Buffer.from(secret.p12Base64, "base64");
|
|
12076
|
-
const hash =
|
|
12236
|
+
const hash = createHash6("sha256").update(p12).digest("hex").toUpperCase();
|
|
12077
12237
|
if (hash !== input.sha256)
|
|
12078
12238
|
throw new Error("Signing identity transfer changed");
|
|
12079
12239
|
const temporaryRoot = await mkdtemp3(join5(tmpdir3(), "ade-identity-"));
|
|
@@ -15072,10 +15232,16 @@ async function inspectCommits(folder, baseBranch, timeoutMs, signal) {
|
|
|
15072
15232
|
return { commits: commits.slice(0, 100), truncated: commits.length > 100 };
|
|
15073
15233
|
}
|
|
15074
15234
|
async function inspectWorktreeDetail(folder, baseBranch, timeoutMs, signal) {
|
|
15075
|
-
const [commitResult, changeResult, branchResult] = await Promise.all([
|
|
15235
|
+
const [commitResult, changeResult, branchResult, signingResult] = await Promise.all([
|
|
15076
15236
|
inspectCommits(folder, baseBranch, timeoutMs, signal),
|
|
15077
15237
|
inspectChanges(folder, timeoutMs, signal),
|
|
15078
|
-
inspectBranchChanges(folder, baseBranch, timeoutMs, signal)
|
|
15238
|
+
inspectBranchChanges(folder, baseBranch, timeoutMs, signal),
|
|
15239
|
+
git3(
|
|
15240
|
+
folder,
|
|
15241
|
+
["config", "--bool", "--get", "commit.gpgsign"],
|
|
15242
|
+
timeoutMs,
|
|
15243
|
+
signal
|
|
15244
|
+
)
|
|
15079
15245
|
]);
|
|
15080
15246
|
return {
|
|
15081
15247
|
commits: commitResult.commits,
|
|
@@ -15083,7 +15249,8 @@ async function inspectWorktreeDetail(folder, baseBranch, timeoutMs, signal) {
|
|
|
15083
15249
|
branchChanges: branchResult.changes,
|
|
15084
15250
|
commitsTruncated: commitResult.truncated,
|
|
15085
15251
|
changesTruncated: changeResult.truncated,
|
|
15086
|
-
branchChangesTruncated: branchResult.truncated
|
|
15252
|
+
branchChangesTruncated: branchResult.truncated,
|
|
15253
|
+
commitSigningEnabled: signingResult.exitCode === 0 && signingResult.stdout.trim() === "true"
|
|
15087
15254
|
};
|
|
15088
15255
|
}
|
|
15089
15256
|
async function baseMergeCommit(folder, baseBranch, timeoutMs, signal) {
|
|
@@ -15609,6 +15776,97 @@ var inspectWorktreeGit = async (payload, timeoutMs, signal) => {
|
|
|
15609
15776
|
)
|
|
15610
15777
|
};
|
|
15611
15778
|
};
|
|
15779
|
+
var commitWorktree = async (payload, timeoutMs, signal) => {
|
|
15780
|
+
const input = worktreeCommitJobPayload(payload);
|
|
15781
|
+
const folder = await validateWorktree2(input, timeoutMs, signal);
|
|
15782
|
+
const runGit = async (args, fallback) => requireSuccess3(await git3(folder, args, timeoutMs, signal), fallback);
|
|
15783
|
+
if (input.stageAll) {
|
|
15784
|
+
await runGit(["add", "--all"], "Could not stage worktree changes");
|
|
15785
|
+
} else {
|
|
15786
|
+
const current = await inspectChanges(folder, timeoutMs, signal);
|
|
15787
|
+
const byPath = new Map(
|
|
15788
|
+
current.changes.map((change) => [change.path, change])
|
|
15789
|
+
);
|
|
15790
|
+
const selected = input.paths.map((path) => {
|
|
15791
|
+
const change = byPath.get(path);
|
|
15792
|
+
if (!change) throw new Error(`Worktree change no longer exists: ${path}`);
|
|
15793
|
+
return change;
|
|
15794
|
+
});
|
|
15795
|
+
const head = await git3(
|
|
15796
|
+
folder,
|
|
15797
|
+
["rev-parse", "--verify", "HEAD"],
|
|
15798
|
+
timeoutMs,
|
|
15799
|
+
signal
|
|
15800
|
+
);
|
|
15801
|
+
if (head.exitCode === 0) {
|
|
15802
|
+
await runGit(
|
|
15803
|
+
["reset", "--mixed", "HEAD", "--"],
|
|
15804
|
+
"Could not reset the Git index"
|
|
15805
|
+
);
|
|
15806
|
+
} else {
|
|
15807
|
+
await runGit(
|
|
15808
|
+
["rm", "--cached", "-r", "--ignore-unmatch", "."],
|
|
15809
|
+
"Could not reset the Git index"
|
|
15810
|
+
);
|
|
15811
|
+
}
|
|
15812
|
+
const selectedPaths = [
|
|
15813
|
+
...new Set(
|
|
15814
|
+
selected.flatMap(
|
|
15815
|
+
(change) => change.previousPath ? [change.previousPath, change.path] : [change.path]
|
|
15816
|
+
)
|
|
15817
|
+
)
|
|
15818
|
+
];
|
|
15819
|
+
await runGit(
|
|
15820
|
+
["--literal-pathspecs", "add", "--all", "--", ...selectedPaths],
|
|
15821
|
+
"Could not stage selected worktree changes"
|
|
15822
|
+
);
|
|
15823
|
+
}
|
|
15824
|
+
const staged = await git3(
|
|
15825
|
+
folder,
|
|
15826
|
+
["diff", "--cached", "--quiet", "--exit-code"],
|
|
15827
|
+
timeoutMs,
|
|
15828
|
+
signal
|
|
15829
|
+
);
|
|
15830
|
+
if (staged.exitCode === 0) throw new Error("No staged changes to commit");
|
|
15831
|
+
if (staged.exitCode !== 1)
|
|
15832
|
+
requireSuccess3(staged, "Could not inspect staged changes");
|
|
15833
|
+
let signed = input.signed;
|
|
15834
|
+
if (signed === null) {
|
|
15835
|
+
const configured = await git3(
|
|
15836
|
+
folder,
|
|
15837
|
+
["config", "--bool", "--get", "commit.gpgsign"],
|
|
15838
|
+
timeoutMs,
|
|
15839
|
+
signal
|
|
15840
|
+
);
|
|
15841
|
+
signed = configured.exitCode === 0 && configured.stdout.trim() === "true";
|
|
15842
|
+
}
|
|
15843
|
+
await runGit(
|
|
15844
|
+
["commit", signed ? "--gpg-sign" : "--no-gpg-sign", "-m", input.message],
|
|
15845
|
+
"Commit failed"
|
|
15846
|
+
);
|
|
15847
|
+
const sha = runGit(["rev-parse", "HEAD"], "Could not read the new commit");
|
|
15848
|
+
const subject = runGit(
|
|
15849
|
+
["log", "-1", "--format=%s"],
|
|
15850
|
+
"Could not read the new commit subject"
|
|
15851
|
+
);
|
|
15852
|
+
const [shaResult, subjectResult] = await Promise.all([sha, subject]);
|
|
15853
|
+
return {
|
|
15854
|
+
...successfulProcess8,
|
|
15855
|
+
commit: {
|
|
15856
|
+
sha: shaResult.stdout.trim(),
|
|
15857
|
+
subject: subjectResult.stdout.trim(),
|
|
15858
|
+
signed
|
|
15859
|
+
},
|
|
15860
|
+
worktree: await inspectWorktreeItem(
|
|
15861
|
+
folder,
|
|
15862
|
+
folder,
|
|
15863
|
+
input.baseBranch,
|
|
15864
|
+
false,
|
|
15865
|
+
Math.min(timeoutMs, 3e4),
|
|
15866
|
+
signal
|
|
15867
|
+
)
|
|
15868
|
+
};
|
|
15869
|
+
};
|
|
15612
15870
|
var operateWorktreeGit = async (payload, timeoutMs, signal) => {
|
|
15613
15871
|
const input = worktreeGitOperationPayload(payload);
|
|
15614
15872
|
const folder = await validateWorktree2(input, timeoutMs, signal);
|
|
@@ -16364,6 +16622,14 @@ var operateWorktree = async (payload, timeoutMs, signal) => {
|
|
|
16364
16622
|
await runGit(["rebase", "--abort"], "Could not cancel rebase");
|
|
16365
16623
|
break;
|
|
16366
16624
|
}
|
|
16625
|
+
case "PULL": {
|
|
16626
|
+
if (!branch || !upstream)
|
|
16627
|
+
throw new Error("Pull requires a branch with an upstream");
|
|
16628
|
+
const remote = upstream.split("/")[0] || "origin";
|
|
16629
|
+
await runGit(["fetch", remote], `Could not fetch ${remote}`);
|
|
16630
|
+
await runGit(["merge", "--ff-only", "@{upstream}"], "Pull failed");
|
|
16631
|
+
break;
|
|
16632
|
+
}
|
|
16367
16633
|
case "PUSH":
|
|
16368
16634
|
if (!branch) throw new Error("Push requires a branch");
|
|
16369
16635
|
await runGit(
|
|
@@ -16643,6 +16909,7 @@ var handlers = {
|
|
|
16643
16909
|
[WORKTREE_INSPECT_JOB_KIND]: inspectWorktree,
|
|
16644
16910
|
[WORKTREE_AUTO_SYNC_JOB_KIND]: autoSyncWorktree,
|
|
16645
16911
|
[WORKTREE_BRANCH_JOB_KIND]: branchWorktree,
|
|
16912
|
+
[WORKTREE_COMMIT_JOB_KIND]: commitWorktree,
|
|
16646
16913
|
[WORKTREE_MOVE_PUSH_JOB_KIND]: pushMovedWorktree,
|
|
16647
16914
|
[WORKTREE_MOVE_CHECKOUT_JOB_KIND]: checkoutMovedWorktree,
|
|
16648
16915
|
[WORKTREE_DELETE_JOB_KIND]: deleteWorktree,
|
|
@@ -16754,6 +17021,7 @@ var JobExecutor = class {
|
|
|
16754
17021
|
controller.signal,
|
|
16755
17022
|
(log) => this.client.appendLog(claimed.id, log).then(() => void 0),
|
|
16756
17023
|
{
|
|
17024
|
+
agentId: claimed.agentId,
|
|
16757
17025
|
reportWorktreeActivity: (input) => this.client.reportWorktreeActivity(input),
|
|
16758
17026
|
reportBuildProgress: (input) => this.client.reportBuildProgress(input),
|
|
16759
17027
|
appendBuildLogChunks: (buildId, chunks) => this.client.appendBuildLogChunks(buildId, chunks),
|
|
@@ -51097,6 +51365,7 @@ async function prepareDevelopmentAgent(options, signal, dependencies = defaultDe
|
|
|
51097
51365
|
}
|
|
51098
51366
|
async function runDevelopmentAgent(options, signal) {
|
|
51099
51367
|
const config = await prepareDevelopmentAgent(options, signal);
|
|
51368
|
+
await reapOrphanedCommandProcesses(config.agentId, signal);
|
|
51100
51369
|
await runAgent(config, signal);
|
|
51101
51370
|
}
|
|
51102
51371
|
|
|
@@ -51369,7 +51638,9 @@ async function main() {
|
|
|
51369
51638
|
controller.signal
|
|
51370
51639
|
);
|
|
51371
51640
|
}
|
|
51372
|
-
|
|
51641
|
+
const config = await loadConfig();
|
|
51642
|
+
await reapOrphanedCommandProcesses(config.agentId, controller.signal);
|
|
51643
|
+
return await runAgent(config, controller.signal);
|
|
51373
51644
|
} finally {
|
|
51374
51645
|
for (const signal of shutdownSignals) process.off(signal, stop2);
|
|
51375
51646
|
}
|