@ai-development-environment/control-agent 0.0.28 → 0.0.29
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 +290 -82
- package/package.json +1 -1
package/dist/control-agent.js
CHANGED
|
@@ -269,8 +269,8 @@ function createClient(options) {
|
|
|
269
269
|
retryWait = async function randomisedExponentialBackoff(retries2) {
|
|
270
270
|
const retryDelaySeconds = Math.pow(2, retries2);
|
|
271
271
|
await new Promise(
|
|
272
|
-
(
|
|
273
|
-
|
|
272
|
+
(resolve6) => setTimeout(
|
|
273
|
+
resolve6,
|
|
274
274
|
retryDelaySeconds * 1e3 + // add random timeout from 300ms to 3s
|
|
275
275
|
Math.floor(Math.random() * (3e3 - 300) + 300)
|
|
276
276
|
)
|
|
@@ -506,7 +506,7 @@ function createClient(options) {
|
|
|
506
506
|
if (socket.readyState === WebSocketImpl.CLOSING) await throwOnClose;
|
|
507
507
|
let release2 = () => {
|
|
508
508
|
};
|
|
509
|
-
const released = new Promise((
|
|
509
|
+
const released = new Promise((resolve6) => release2 = resolve6);
|
|
510
510
|
return [
|
|
511
511
|
socket,
|
|
512
512
|
release2,
|
|
@@ -669,7 +669,7 @@ function createClient(options) {
|
|
|
669
669
|
const iterator = (async function* iterator2() {
|
|
670
670
|
for (; ; ) {
|
|
671
671
|
if (!pending.length) {
|
|
672
|
-
await new Promise((
|
|
672
|
+
await new Promise((resolve6) => deferred.resolve = resolve6);
|
|
673
673
|
}
|
|
674
674
|
while (pending.length) {
|
|
675
675
|
yield pending.shift();
|
|
@@ -960,7 +960,7 @@ function createAgentSubscriptionClient(config) {
|
|
|
960
960
|
shouldRetry: () => true,
|
|
961
961
|
retryWait: async (retries) => {
|
|
962
962
|
const delay = Math.min(3e4, 1e3 * 2 ** Math.min(retries, 5));
|
|
963
|
-
await new Promise((
|
|
963
|
+
await new Promise((resolve6) => setTimeout(resolve6, delay));
|
|
964
964
|
},
|
|
965
965
|
on: {
|
|
966
966
|
connected: () => console.log("Connected to control-plane WebSocket"),
|
|
@@ -2756,7 +2756,7 @@ function collectInventory() {
|
|
|
2756
2756
|
// src/process-runner.ts
|
|
2757
2757
|
import { spawn } from "node:child_process";
|
|
2758
2758
|
async function runProcess(options) {
|
|
2759
|
-
return new Promise((
|
|
2759
|
+
return new Promise((resolve6, reject) => {
|
|
2760
2760
|
let sequence = 0;
|
|
2761
2761
|
let timedOut = false;
|
|
2762
2762
|
let cancelled = false;
|
|
@@ -2833,7 +2833,7 @@ async function runProcess(options) {
|
|
|
2833
2833
|
clearTimeout(timeout);
|
|
2834
2834
|
options.signal.removeEventListener("abort", abort);
|
|
2835
2835
|
void logChain.then(
|
|
2836
|
-
() =>
|
|
2836
|
+
() => resolve6({ exitCode, signal, timedOut, cancelled })
|
|
2837
2837
|
);
|
|
2838
2838
|
});
|
|
2839
2839
|
});
|
|
@@ -2927,7 +2927,7 @@ import { basename, dirname as dirname2, isAbsolute, join as join2, resolve } fro
|
|
|
2927
2927
|
import { spawn as spawn2 } from "node:child_process";
|
|
2928
2928
|
var MAX_OUTPUT_BYTES = 1024 * 1024;
|
|
2929
2929
|
function captureCommand(options) {
|
|
2930
|
-
return new Promise((
|
|
2930
|
+
return new Promise((resolve6, reject) => {
|
|
2931
2931
|
let stdout = "";
|
|
2932
2932
|
let stderr = "";
|
|
2933
2933
|
let timedOut = false;
|
|
@@ -2975,7 +2975,7 @@ function captureCommand(options) {
|
|
|
2975
2975
|
settled = true;
|
|
2976
2976
|
clearTimeout(timeout);
|
|
2977
2977
|
options.signal.removeEventListener("abort", abort);
|
|
2978
|
-
|
|
2978
|
+
resolve6({
|
|
2979
2979
|
exitCode,
|
|
2980
2980
|
signal,
|
|
2981
2981
|
timedOut,
|
|
@@ -4432,7 +4432,200 @@ var applySkills = async (payload) => {
|
|
|
4432
4432
|
// src/handlers/worktrees.ts
|
|
4433
4433
|
import { watch } from "node:fs";
|
|
4434
4434
|
import { readFile as readFile3, realpath as realpath4, stat as stat4 } from "node:fs/promises";
|
|
4435
|
-
import { basename as basename2, dirname as dirname5, join as join5, relative as
|
|
4435
|
+
import { basename as basename2, dirname as dirname5, join as join5, relative as relative3 } from "node:path";
|
|
4436
|
+
|
|
4437
|
+
// src/git-code-state.ts
|
|
4438
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
4439
|
+
import { createReadStream as createReadStream2 } from "node:fs";
|
|
4440
|
+
import { lstat as lstat3, readlink } from "node:fs/promises";
|
|
4441
|
+
import { isAbsolute as isAbsolute3, relative as relative2, resolve as resolve4, sep as sep2 } from "node:path";
|
|
4442
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
4443
|
+
function gitEnvironment() {
|
|
4444
|
+
return {
|
|
4445
|
+
...process.env,
|
|
4446
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
4447
|
+
GIT_OPTIONAL_LOCKS: "0"
|
|
4448
|
+
};
|
|
4449
|
+
}
|
|
4450
|
+
function remainingTimeoutMs(deadline) {
|
|
4451
|
+
return Math.max(1, deadline - Date.now());
|
|
4452
|
+
}
|
|
4453
|
+
function containedPath(folder, path) {
|
|
4454
|
+
const absolutePath = resolve4(folder, path);
|
|
4455
|
+
const difference = relative2(folder, absolutePath);
|
|
4456
|
+
if (!difference || difference === ".." || difference.startsWith(`..${sep2}`) || isAbsolute3(difference)) {
|
|
4457
|
+
return null;
|
|
4458
|
+
}
|
|
4459
|
+
return absolutePath;
|
|
4460
|
+
}
|
|
4461
|
+
function hashGitDiff(hash, folder, timeoutMs, signal) {
|
|
4462
|
+
return new Promise((resolveResult, reject) => {
|
|
4463
|
+
let settled = false;
|
|
4464
|
+
let timedOut = false;
|
|
4465
|
+
const child = spawn3(
|
|
4466
|
+
"git",
|
|
4467
|
+
[
|
|
4468
|
+
"-C",
|
|
4469
|
+
folder,
|
|
4470
|
+
"--no-optional-locks",
|
|
4471
|
+
"diff",
|
|
4472
|
+
"--binary",
|
|
4473
|
+
"--no-ext-diff",
|
|
4474
|
+
"--no-textconv",
|
|
4475
|
+
"HEAD",
|
|
4476
|
+
"--"
|
|
4477
|
+
],
|
|
4478
|
+
{
|
|
4479
|
+
env: gitEnvironment(),
|
|
4480
|
+
shell: false,
|
|
4481
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
4482
|
+
}
|
|
4483
|
+
);
|
|
4484
|
+
child.stdout.on("data", (chunk) => hash.update(chunk));
|
|
4485
|
+
child.stderr.resume();
|
|
4486
|
+
const terminate = () => {
|
|
4487
|
+
if (child.exitCode === null && !child.killed) child.kill("SIGTERM");
|
|
4488
|
+
};
|
|
4489
|
+
const timeout = setTimeout(() => {
|
|
4490
|
+
timedOut = true;
|
|
4491
|
+
terminate();
|
|
4492
|
+
}, timeoutMs);
|
|
4493
|
+
timeout.unref();
|
|
4494
|
+
const abort = () => terminate();
|
|
4495
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
4496
|
+
if (signal.aborted) abort();
|
|
4497
|
+
child.once("error", (error) => {
|
|
4498
|
+
if (settled) return;
|
|
4499
|
+
settled = true;
|
|
4500
|
+
clearTimeout(timeout);
|
|
4501
|
+
signal.removeEventListener("abort", abort);
|
|
4502
|
+
reject(error);
|
|
4503
|
+
});
|
|
4504
|
+
child.once("close", (exitCode) => {
|
|
4505
|
+
if (settled) return;
|
|
4506
|
+
settled = true;
|
|
4507
|
+
clearTimeout(timeout);
|
|
4508
|
+
signal.removeEventListener("abort", abort);
|
|
4509
|
+
resolveResult(!timedOut && !signal.aborted && exitCode === 0);
|
|
4510
|
+
});
|
|
4511
|
+
});
|
|
4512
|
+
}
|
|
4513
|
+
async function worktreeCodeStateHash(folder, timeoutMs, signal) {
|
|
4514
|
+
const deadline = Date.now() + timeoutMs;
|
|
4515
|
+
const operation = new AbortController();
|
|
4516
|
+
const abort = () => operation.abort(signal.reason);
|
|
4517
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
4518
|
+
if (signal.aborted) abort();
|
|
4519
|
+
const timeout = setTimeout(() => operation.abort(), Math.max(0, timeoutMs));
|
|
4520
|
+
timeout.unref();
|
|
4521
|
+
try {
|
|
4522
|
+
operation.signal.throwIfAborted();
|
|
4523
|
+
const [head, untracked, submodules] = await Promise.all([
|
|
4524
|
+
captureCommand({
|
|
4525
|
+
command: "git",
|
|
4526
|
+
args: ["-C", folder, "rev-parse", "HEAD"],
|
|
4527
|
+
timeoutMs: remainingTimeoutMs(deadline),
|
|
4528
|
+
signal: operation.signal,
|
|
4529
|
+
env: gitEnvironment()
|
|
4530
|
+
}),
|
|
4531
|
+
captureCommand({
|
|
4532
|
+
command: "git",
|
|
4533
|
+
args: [
|
|
4534
|
+
"-C",
|
|
4535
|
+
folder,
|
|
4536
|
+
"--no-optional-locks",
|
|
4537
|
+
"ls-files",
|
|
4538
|
+
"--others",
|
|
4539
|
+
"--exclude-standard",
|
|
4540
|
+
"-z"
|
|
4541
|
+
],
|
|
4542
|
+
timeoutMs: remainingTimeoutMs(deadline),
|
|
4543
|
+
signal: operation.signal,
|
|
4544
|
+
env: gitEnvironment()
|
|
4545
|
+
}),
|
|
4546
|
+
captureCommand({
|
|
4547
|
+
command: "git",
|
|
4548
|
+
args: [
|
|
4549
|
+
"-C",
|
|
4550
|
+
folder,
|
|
4551
|
+
"--no-optional-locks",
|
|
4552
|
+
"submodule",
|
|
4553
|
+
"foreach",
|
|
4554
|
+
"--quiet",
|
|
4555
|
+
'printf "%s\\0" "$sm_path"'
|
|
4556
|
+
],
|
|
4557
|
+
timeoutMs: remainingTimeoutMs(deadline),
|
|
4558
|
+
signal: operation.signal,
|
|
4559
|
+
env: gitEnvironment()
|
|
4560
|
+
})
|
|
4561
|
+
]);
|
|
4562
|
+
if (head.exitCode !== 0 || untracked.exitCode !== 0 || submodules.exitCode !== 0 || operation.signal.aborted) {
|
|
4563
|
+
return null;
|
|
4564
|
+
}
|
|
4565
|
+
const hash = createHash2("sha256");
|
|
4566
|
+
hash.update("head\0");
|
|
4567
|
+
hash.update(head.stdout.trim());
|
|
4568
|
+
hash.update("\0diff\0");
|
|
4569
|
+
if (!await hashGitDiff(
|
|
4570
|
+
hash,
|
|
4571
|
+
folder,
|
|
4572
|
+
remainingTimeoutMs(deadline),
|
|
4573
|
+
operation.signal
|
|
4574
|
+
)) {
|
|
4575
|
+
return null;
|
|
4576
|
+
}
|
|
4577
|
+
hash.update("\0untracked\0");
|
|
4578
|
+
const paths = untracked.stdout.split("\0").filter(Boolean).sort();
|
|
4579
|
+
for (const path of paths) {
|
|
4580
|
+
operation.signal.throwIfAborted();
|
|
4581
|
+
const absolutePath = containedPath(folder, path);
|
|
4582
|
+
if (!absolutePath) return null;
|
|
4583
|
+
hash.update(path);
|
|
4584
|
+
hash.update("\0");
|
|
4585
|
+
const information = await lstat3(absolutePath);
|
|
4586
|
+
operation.signal.throwIfAborted();
|
|
4587
|
+
if (information.isSymbolicLink()) {
|
|
4588
|
+
hash.update("symlink\0");
|
|
4589
|
+
hash.update(await readlink(absolutePath));
|
|
4590
|
+
} else if (information.isFile()) {
|
|
4591
|
+
hash.update("file\0");
|
|
4592
|
+
hash.update((information.mode & 511).toString(8).padStart(3, "0"));
|
|
4593
|
+
hash.update("\0");
|
|
4594
|
+
for await (const chunk of createReadStream2(absolutePath, {
|
|
4595
|
+
signal: operation.signal
|
|
4596
|
+
})) {
|
|
4597
|
+
hash.update(chunk);
|
|
4598
|
+
}
|
|
4599
|
+
}
|
|
4600
|
+
hash.update("\0");
|
|
4601
|
+
}
|
|
4602
|
+
hash.update("submodules\0");
|
|
4603
|
+
const submodulePaths = submodules.stdout.split("\0").filter(Boolean).sort();
|
|
4604
|
+
for (const path of submodulePaths) {
|
|
4605
|
+
operation.signal.throwIfAborted();
|
|
4606
|
+
const absolutePath = containedPath(folder, path);
|
|
4607
|
+
if (!absolutePath) return null;
|
|
4608
|
+
const submoduleHash = await worktreeCodeStateHash(
|
|
4609
|
+
absolutePath,
|
|
4610
|
+
remainingTimeoutMs(deadline),
|
|
4611
|
+
operation.signal
|
|
4612
|
+
);
|
|
4613
|
+
if (!submoduleHash) return null;
|
|
4614
|
+
hash.update(path);
|
|
4615
|
+
hash.update("\0");
|
|
4616
|
+
hash.update(submoduleHash);
|
|
4617
|
+
hash.update("\0");
|
|
4618
|
+
}
|
|
4619
|
+
return hash.digest("hex");
|
|
4620
|
+
} catch {
|
|
4621
|
+
return null;
|
|
4622
|
+
} finally {
|
|
4623
|
+
clearTimeout(timeout);
|
|
4624
|
+
signal.removeEventListener("abort", abort);
|
|
4625
|
+
}
|
|
4626
|
+
}
|
|
4627
|
+
|
|
4628
|
+
// src/handlers/worktrees.ts
|
|
4436
4629
|
var successfulProcess4 = {
|
|
4437
4630
|
exitCode: 0,
|
|
4438
4631
|
signal: null,
|
|
@@ -4482,27 +4675,30 @@ async function flushWorktreeActivity(entry) {
|
|
|
4482
4675
|
entry.pending = false;
|
|
4483
4676
|
try {
|
|
4484
4677
|
const signal = new AbortController().signal;
|
|
4485
|
-
const [status2, branchResult, headResult] = await Promise.all(
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
|
|
4500
|
-
|
|
4501
|
-
|
|
4502
|
-
|
|
4503
|
-
|
|
4504
|
-
|
|
4505
|
-
|
|
4678
|
+
const [status2, branchResult, headResult, codeStateHash] = await Promise.all(
|
|
4679
|
+
[
|
|
4680
|
+
git2(
|
|
4681
|
+
entry.folder,
|
|
4682
|
+
[
|
|
4683
|
+
"--no-optional-locks",
|
|
4684
|
+
"status",
|
|
4685
|
+
"--porcelain=v1",
|
|
4686
|
+
"-z",
|
|
4687
|
+
"--untracked-files=all"
|
|
4688
|
+
],
|
|
4689
|
+
entry.timeoutMs,
|
|
4690
|
+
signal
|
|
4691
|
+
),
|
|
4692
|
+
git2(
|
|
4693
|
+
entry.folder,
|
|
4694
|
+
["symbolic-ref", "--short", "-q", "HEAD"],
|
|
4695
|
+
entry.timeoutMs,
|
|
4696
|
+
signal
|
|
4697
|
+
),
|
|
4698
|
+
git2(entry.folder, ["rev-parse", "HEAD"], entry.timeoutMs, signal),
|
|
4699
|
+
worktreeCodeStateHash(entry.folder, entry.timeoutMs, signal)
|
|
4700
|
+
]
|
|
4701
|
+
);
|
|
4506
4702
|
const branch = branchResult.exitCode === 0 ? branchResult.stdout.trim() : null;
|
|
4507
4703
|
const headSha = headResult.exitCode === 0 ? headResult.stdout.trim() : null;
|
|
4508
4704
|
const headIdentity = headSha ? `${branch ?? ""}\0${headSha}` : null;
|
|
@@ -4510,6 +4706,7 @@ async function flushWorktreeActivity(entry) {
|
|
|
4510
4706
|
const report = {
|
|
4511
4707
|
codebaseId: entry.codebaseId,
|
|
4512
4708
|
gitDirectory: entry.gitDirectory,
|
|
4709
|
+
codeStateHash,
|
|
4513
4710
|
...changes,
|
|
4514
4711
|
pushStatus: await worktreePushStatus(
|
|
4515
4712
|
entry.folder,
|
|
@@ -4718,10 +4915,11 @@ async function inspectWorktreeItem(folderValue2, rootFolder, baseBranch, primary
|
|
|
4718
4915
|
return {
|
|
4719
4916
|
gitDirectory: gitDir,
|
|
4720
4917
|
folder,
|
|
4721
|
-
relativePath:
|
|
4918
|
+
relativePath: relative3(rootFolder, folder) || ".",
|
|
4722
4919
|
primary,
|
|
4723
4920
|
branch,
|
|
4724
4921
|
headSha: headResult.exitCode === 0 ? headResult.stdout.trim() : null,
|
|
4922
|
+
codeStateHash: await worktreeCodeStateHash(folder, timeoutMs, signal),
|
|
4725
4923
|
upstream,
|
|
4726
4924
|
ahead: upstreamCounts.ahead,
|
|
4727
4925
|
behind: upstreamCounts.behind,
|
|
@@ -4744,10 +4942,11 @@ async function inspectWorktreeItem(folderValue2, rootFolder, baseBranch, primary
|
|
|
4744
4942
|
return {
|
|
4745
4943
|
gitDirectory: folderValue2,
|
|
4746
4944
|
folder: folderValue2,
|
|
4747
|
-
relativePath:
|
|
4945
|
+
relativePath: relative3(rootFolder, folderValue2) || ".",
|
|
4748
4946
|
primary,
|
|
4749
4947
|
branch: null,
|
|
4750
4948
|
headSha: null,
|
|
4949
|
+
codeStateHash: null,
|
|
4751
4950
|
upstream: null,
|
|
4752
4951
|
ahead: null,
|
|
4753
4952
|
behind: null,
|
|
@@ -5752,7 +5951,7 @@ var operateWorktree = async (payload, timeoutMs, signal) => {
|
|
|
5752
5951
|
};
|
|
5753
5952
|
|
|
5754
5953
|
// src/handlers/builds.ts
|
|
5755
|
-
import { createHash as
|
|
5954
|
+
import { createHash as createHash3, randomUUID as randomUUID2 } from "node:crypto";
|
|
5756
5955
|
import {
|
|
5757
5956
|
cp,
|
|
5758
5957
|
mkdir as mkdir3,
|
|
@@ -5769,13 +5968,13 @@ import { tmpdir } from "node:os";
|
|
|
5769
5968
|
import {
|
|
5770
5969
|
basename as basename3,
|
|
5771
5970
|
dirname as dirname6,
|
|
5772
|
-
isAbsolute as
|
|
5971
|
+
isAbsolute as isAbsolute4,
|
|
5773
5972
|
join as join6,
|
|
5774
|
-
relative as
|
|
5775
|
-
resolve as
|
|
5776
|
-
sep as
|
|
5973
|
+
relative as relative4,
|
|
5974
|
+
resolve as resolve5,
|
|
5975
|
+
sep as sep3
|
|
5777
5976
|
} from "node:path";
|
|
5778
|
-
import { spawn as
|
|
5977
|
+
import { spawn as spawn4 } from "node:child_process";
|
|
5779
5978
|
var successfulProcess5 = {
|
|
5780
5979
|
exitCode: 0,
|
|
5781
5980
|
signal: null,
|
|
@@ -5848,19 +6047,19 @@ async function validateWorktree2(input, timeoutMs, signal) {
|
|
|
5848
6047
|
}
|
|
5849
6048
|
return folder;
|
|
5850
6049
|
}
|
|
5851
|
-
function
|
|
5852
|
-
const target2 =
|
|
5853
|
-
const difference =
|
|
5854
|
-
if (difference === ".." || difference.startsWith(`..${
|
|
6050
|
+
function containedPath2(root, relativePath) {
|
|
6051
|
+
const target2 = resolve5(root, relativePath);
|
|
6052
|
+
const difference = relative4(root, target2);
|
|
6053
|
+
if (difference === ".." || difference.startsWith(`..${sep3}`) || isAbsolute4(difference)) {
|
|
5855
6054
|
throw new Error("Path must stay within the worktree");
|
|
5856
6055
|
}
|
|
5857
6056
|
return target2;
|
|
5858
6057
|
}
|
|
5859
6058
|
async function validateSource(root, source) {
|
|
5860
|
-
const target2 =
|
|
6059
|
+
const target2 = containedPath2(root, source.relativePath);
|
|
5861
6060
|
const resolved = await realpath5(target2);
|
|
5862
|
-
const difference =
|
|
5863
|
-
if (difference === ".." || difference.startsWith(`..${
|
|
6061
|
+
const difference = relative4(root, resolved);
|
|
6062
|
+
if (difference === ".." || difference.startsWith(`..${sep3}`) || isAbsolute4(difference)) {
|
|
5864
6063
|
throw new Error("Build source resolves outside the worktree");
|
|
5865
6064
|
}
|
|
5866
6065
|
const information = await stat5(resolved);
|
|
@@ -5880,7 +6079,7 @@ async function discoverSourcesInFolder(folder) {
|
|
|
5880
6079
|
for (const entry of await readdir4(current, { withFileTypes: true })) {
|
|
5881
6080
|
if (entry.isSymbolicLink()) continue;
|
|
5882
6081
|
const absolute = join6(current, entry.name);
|
|
5883
|
-
const path =
|
|
6082
|
+
const path = relative4(folder, absolute).split(sep3).join("/");
|
|
5884
6083
|
if (entry.isDirectory()) {
|
|
5885
6084
|
if (entry.name.endsWith(".xcodeproj")) {
|
|
5886
6085
|
sources.push({ kind: "PROJECT", relativePath: path });
|
|
@@ -5971,15 +6170,15 @@ async function workspaceProjectPaths(workspacePath2, folder) {
|
|
|
5971
6170
|
}).filter((path) => path.endsWith(".xcodeproj"));
|
|
5972
6171
|
const projects = [];
|
|
5973
6172
|
for (const location of locations) {
|
|
5974
|
-
const candidate =
|
|
5975
|
-
const difference =
|
|
5976
|
-
if (difference === ".." || difference.startsWith(`..${
|
|
6173
|
+
const candidate = resolve5(dirname6(workspacePath2), location);
|
|
6174
|
+
const difference = relative4(folder, candidate);
|
|
6175
|
+
if (difference === ".." || difference.startsWith(`..${sep3}`) || isAbsolute4(difference)) {
|
|
5977
6176
|
continue;
|
|
5978
6177
|
}
|
|
5979
6178
|
try {
|
|
5980
6179
|
const resolved = await realpath5(candidate);
|
|
5981
|
-
const resolvedDifference =
|
|
5982
|
-
if (resolvedDifference === ".." || resolvedDifference.startsWith(`..${
|
|
6180
|
+
const resolvedDifference = relative4(folder, resolved);
|
|
6181
|
+
if (resolvedDifference === ".." || resolvedDifference.startsWith(`..${sep3}`) || isAbsolute4(resolvedDifference) || !(await stat5(resolved)).isDirectory()) {
|
|
5983
6182
|
continue;
|
|
5984
6183
|
}
|
|
5985
6184
|
projects.push(resolved);
|
|
@@ -6239,12 +6438,6 @@ var inspectBuildDestinations = async (payload, timeoutMs, signal) => {
|
|
|
6239
6438
|
folder
|
|
6240
6439
|
);
|
|
6241
6440
|
requireSuccess3(preflight, "The saved scheme or configuration is unavailable");
|
|
6242
|
-
if (GENERIC_BUILD_DESTINATION_ACTIONS.includes(input.action)) {
|
|
6243
|
-
return {
|
|
6244
|
-
...successfulProcess5,
|
|
6245
|
-
destinations: genericBuildDestinations(input.action)
|
|
6246
|
-
};
|
|
6247
|
-
}
|
|
6248
6441
|
const [simulators, physical] = await Promise.all([
|
|
6249
6442
|
command2(
|
|
6250
6443
|
"xcrun",
|
|
@@ -6258,6 +6451,7 @@ var inspectBuildDestinations = async (payload, timeoutMs, signal) => {
|
|
|
6258
6451
|
return {
|
|
6259
6452
|
...successfulProcess5,
|
|
6260
6453
|
destinations: [
|
|
6454
|
+
...genericBuildDestinations(input.action),
|
|
6261
6455
|
...simulators.exitCode === 0 ? simulatorDestinations(JSON.parse(simulators.stdout)) : [],
|
|
6262
6456
|
...physical
|
|
6263
6457
|
]
|
|
@@ -6406,7 +6600,7 @@ function runLoggedCommand(options) {
|
|
|
6406
6600
|
mode: 384
|
|
6407
6601
|
});
|
|
6408
6602
|
}
|
|
6409
|
-
const child =
|
|
6603
|
+
const child = spawn4(options.command, options.args, {
|
|
6410
6604
|
cwd: options.cwd,
|
|
6411
6605
|
env: options.env,
|
|
6412
6606
|
shell: false,
|
|
@@ -6599,7 +6793,7 @@ function actionArgument(action) {
|
|
|
6599
6793
|
}
|
|
6600
6794
|
function xcodeBuildArguments(input) {
|
|
6601
6795
|
const resultBundle = join6(input.artifactDirectory, "result.xcresult");
|
|
6602
|
-
const sourcePath =
|
|
6796
|
+
const sourcePath = containedPath2(input.folder, input.source.relativePath);
|
|
6603
6797
|
const usesCapturedTestProducts = input.action === "TEST_WITHOUT_BUILDING";
|
|
6604
6798
|
const args = [
|
|
6605
6799
|
"xcodebuild",
|
|
@@ -6646,7 +6840,7 @@ function xcodeBuildSettingsArguments(input, folder = input.folder) {
|
|
|
6646
6840
|
"xcodebuild",
|
|
6647
6841
|
...sourceArguments(
|
|
6648
6842
|
input.source,
|
|
6649
|
-
|
|
6843
|
+
containedPath2(folder, input.source.relativePath)
|
|
6650
6844
|
),
|
|
6651
6845
|
"-scheme",
|
|
6652
6846
|
input.scheme,
|
|
@@ -6749,7 +6943,7 @@ if (typeof hookModule.default === "function") {
|
|
|
6749
6943
|
exitCode: result.exitCode,
|
|
6750
6944
|
durationMs: Date.now() - started,
|
|
6751
6945
|
causedBuildFailure: failed && options.script.failureBehavior === "FAIL_BUILD",
|
|
6752
|
-
outputRelativePath:
|
|
6946
|
+
outputRelativePath: relative4(options.input.artifactDirectory, log),
|
|
6753
6947
|
error: failed ? cleanError3(result.output || "Build hook failed") : null
|
|
6754
6948
|
};
|
|
6755
6949
|
} catch (hookError) {
|
|
@@ -6762,7 +6956,7 @@ if (typeof hookModule.default === "function") {
|
|
|
6762
6956
|
exitCode: null,
|
|
6763
6957
|
durationMs: Date.now() - started,
|
|
6764
6958
|
causedBuildFailure: !options.signal.aborted && options.script.failureBehavior === "FAIL_BUILD",
|
|
6765
|
-
outputRelativePath:
|
|
6959
|
+
outputRelativePath: relative4(options.input.artifactDirectory, log),
|
|
6766
6960
|
error
|
|
6767
6961
|
};
|
|
6768
6962
|
}
|
|
@@ -6791,7 +6985,7 @@ async function fileChecksum(path) {
|
|
|
6791
6985
|
const information = await stat5(path);
|
|
6792
6986
|
if (!information.isFile() || information.size > 256 * 1024 * 1024)
|
|
6793
6987
|
return null;
|
|
6794
|
-
return
|
|
6988
|
+
return createHash3("sha256").update(await readFile4(path)).digest("hex");
|
|
6795
6989
|
} catch {
|
|
6796
6990
|
return null;
|
|
6797
6991
|
}
|
|
@@ -6799,7 +6993,7 @@ async function fileChecksum(path) {
|
|
|
6799
6993
|
async function artifact(root, kind, path, metadata = {}) {
|
|
6800
6994
|
return {
|
|
6801
6995
|
kind,
|
|
6802
|
-
relativePath:
|
|
6996
|
+
relativePath: relative4(root, path).split(sep3).join("/"),
|
|
6803
6997
|
sizeBytes: await pathSize(path),
|
|
6804
6998
|
checksum: await fileChecksum(path),
|
|
6805
6999
|
metadata
|
|
@@ -6941,9 +7135,14 @@ var runIosBuild = async (payload, timeoutMs, signal, onLog, context) => {
|
|
|
6941
7135
|
throw new Error("The captured .xctestrun artifact is unavailable");
|
|
6942
7136
|
}
|
|
6943
7137
|
}
|
|
6944
|
-
if (!
|
|
7138
|
+
if (!isAbsolute4(input.artifactDirectory) || basename3(input.artifactDirectory) !== input.buildId) {
|
|
6945
7139
|
throw new Error("Build artifact directory is invalid");
|
|
6946
7140
|
}
|
|
7141
|
+
const sourceStateHash = await worktreeCodeStateHash(
|
|
7142
|
+
folder,
|
|
7143
|
+
Math.min(timeoutMs, 6e4),
|
|
7144
|
+
signal
|
|
7145
|
+
);
|
|
6947
7146
|
await mkdir3(input.artifactDirectory, { recursive: true, mode: 448 });
|
|
6948
7147
|
const rawLog = join6(input.artifactDirectory, "build.log");
|
|
6949
7148
|
const logger = new BuildLogger(input.buildId, context, rawLog, process.env);
|
|
@@ -7099,6 +7298,12 @@ var runIosBuild = async (payload, timeoutMs, signal, onLog, context) => {
|
|
|
7099
7298
|
const timedOut = buildResult?.timedOut === true;
|
|
7100
7299
|
const exitCode = cancelled ? null : failBuild || !buildResult ? 1 : buildResult.exitCode;
|
|
7101
7300
|
const args = xcodeBuildArguments(input);
|
|
7301
|
+
const finalStateHash = await worktreeCodeStateHash(
|
|
7302
|
+
folder,
|
|
7303
|
+
Math.min(timeoutMs, 6e4),
|
|
7304
|
+
new AbortController().signal
|
|
7305
|
+
);
|
|
7306
|
+
const codeStateObservedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
7102
7307
|
return {
|
|
7103
7308
|
exitCode,
|
|
7104
7309
|
signal: buildResult?.signal ?? null,
|
|
@@ -7108,7 +7313,10 @@ var runIosBuild = async (payload, timeoutMs, signal, onLog, context) => {
|
|
|
7108
7313
|
error,
|
|
7109
7314
|
commandSummary: commandSummary("xcrun", args),
|
|
7110
7315
|
artifacts,
|
|
7111
|
-
scriptExecutions
|
|
7316
|
+
scriptExecutions,
|
|
7317
|
+
sourceStateHash,
|
|
7318
|
+
finalStateHash,
|
|
7319
|
+
codeStateObservedAt
|
|
7112
7320
|
};
|
|
7113
7321
|
} finally {
|
|
7114
7322
|
await logger.close();
|
|
@@ -7133,10 +7341,10 @@ var downloadIosBuildArtifact = async (payload, timeoutMs, signal, onLog, context
|
|
|
7133
7341
|
}
|
|
7134
7342
|
const root = await realpath5(input.artifactDirectory);
|
|
7135
7343
|
const target2 = await realpath5(
|
|
7136
|
-
|
|
7344
|
+
containedPath2(root, input.artifactRelativePath)
|
|
7137
7345
|
);
|
|
7138
|
-
const difference =
|
|
7139
|
-
if (!difference || difference === ".." || difference.startsWith(`..${
|
|
7346
|
+
const difference = relative4(root, target2);
|
|
7347
|
+
if (!difference || difference === ".." || difference.startsWith(`..${sep3}`) || isAbsolute4(difference)) {
|
|
7140
7348
|
throw new Error("Build artifact resolves outside the build folder");
|
|
7141
7349
|
}
|
|
7142
7350
|
const information = await stat5(target2);
|
|
@@ -7209,7 +7417,7 @@ var deployIosBuild = async (payload, timeoutMs, signal, _onLog, context) => {
|
|
|
7209
7417
|
Math.min(timeoutMs, 6e4),
|
|
7210
7418
|
signal
|
|
7211
7419
|
);
|
|
7212
|
-
const appPath =
|
|
7420
|
+
const appPath = containedPath2(
|
|
7213
7421
|
input.artifactDirectory,
|
|
7214
7422
|
input.artifactRelativePath
|
|
7215
7423
|
);
|
|
@@ -7387,7 +7595,7 @@ var deployIosBuild = async (payload, timeoutMs, signal, _onLog, context) => {
|
|
|
7387
7595
|
status: signal.aborted ? "CANCELLED" : failure ? "FAILED" : "SUCCEEDED",
|
|
7388
7596
|
error: failure,
|
|
7389
7597
|
durationMs: Date.now() - started,
|
|
7390
|
-
outputRelativePath:
|
|
7598
|
+
outputRelativePath: relative4(input.artifactDirectory, logPath)
|
|
7391
7599
|
});
|
|
7392
7600
|
if (signal.aborted) break;
|
|
7393
7601
|
}
|
|
@@ -7447,7 +7655,7 @@ var exportIosArchive = async (payload, timeoutMs, signal, _onLog, context) => {
|
|
|
7447
7655
|
Math.min(timeoutMs, 6e4),
|
|
7448
7656
|
signal
|
|
7449
7657
|
);
|
|
7450
|
-
const archivePath =
|
|
7658
|
+
const archivePath = containedPath2(
|
|
7451
7659
|
input.artifactDirectory,
|
|
7452
7660
|
input.archiveRelativePath
|
|
7453
7661
|
);
|
|
@@ -7493,7 +7701,7 @@ var exportIosArchive = async (payload, timeoutMs, signal, _onLog, context) => {
|
|
|
7493
7701
|
signal: result.signal,
|
|
7494
7702
|
timedOut: result.timedOut,
|
|
7495
7703
|
cancelled: result.cancelled,
|
|
7496
|
-
outputRelativePath:
|
|
7704
|
+
outputRelativePath: relative4(input.artifactDirectory, exportDirectory),
|
|
7497
7705
|
sizeBytes: result.exitCode === 0 ? await pathSize(exportDirectory) : null,
|
|
7498
7706
|
error: result.exitCode === 0 ? null : cleanError3(result.output)
|
|
7499
7707
|
};
|
|
@@ -7542,8 +7750,8 @@ var RepositoryCoordinator = class {
|
|
|
7542
7750
|
async run(key, task) {
|
|
7543
7751
|
const previous = this.tails.get(key) ?? Promise.resolve();
|
|
7544
7752
|
let release2;
|
|
7545
|
-
const current = new Promise((
|
|
7546
|
-
release2 =
|
|
7753
|
+
const current = new Promise((resolve6) => {
|
|
7754
|
+
release2 = resolve6;
|
|
7547
7755
|
});
|
|
7548
7756
|
const tail = previous.then(() => current);
|
|
7549
7757
|
this.tails.set(key, tail);
|
|
@@ -7641,7 +7849,7 @@ var JobExecutor = class {
|
|
|
7641
7849
|
);
|
|
7642
7850
|
if (this.stopping) return;
|
|
7643
7851
|
const delay = Math.min(3e4, 1e3 * 2 ** Math.min(retry++, 5));
|
|
7644
|
-
await new Promise((
|
|
7852
|
+
await new Promise((resolve6) => setTimeout(resolve6, delay));
|
|
7645
7853
|
}
|
|
7646
7854
|
}
|
|
7647
7855
|
}
|
|
@@ -7912,9 +8120,9 @@ async function runAgent(config, signal) {
|
|
|
7912
8120
|
() => void heartbeat(),
|
|
7913
8121
|
HEARTBEAT_INTERVAL_MS
|
|
7914
8122
|
);
|
|
7915
|
-
await new Promise((
|
|
7916
|
-
if (signal.aborted)
|
|
7917
|
-
else signal.addEventListener("abort", () =>
|
|
8123
|
+
await new Promise((resolve6) => {
|
|
8124
|
+
if (signal.aborted) resolve6();
|
|
8125
|
+
else signal.addEventListener("abort", () => resolve6(), { once: true });
|
|
7918
8126
|
});
|
|
7919
8127
|
clearInterval(heartbeatTimer);
|
|
7920
8128
|
if (codebaseTimer) clearTimeout(codebaseTimer);
|
|
@@ -7931,7 +8139,7 @@ var defaultDependencies = {
|
|
|
7931
8139
|
inventory: collectInventory,
|
|
7932
8140
|
load: loadConfig,
|
|
7933
8141
|
save: saveConfig,
|
|
7934
|
-
wait: (milliseconds, signal) => new Promise((
|
|
8142
|
+
wait: (milliseconds, signal) => new Promise((resolve6, reject) => {
|
|
7935
8143
|
if (signal.aborted) {
|
|
7936
8144
|
reject(new Error("Development agent startup was cancelled"));
|
|
7937
8145
|
return;
|
|
@@ -7942,7 +8150,7 @@ var defaultDependencies = {
|
|
|
7942
8150
|
};
|
|
7943
8151
|
const timer = setTimeout(() => {
|
|
7944
8152
|
signal.removeEventListener("abort", onAbort);
|
|
7945
|
-
|
|
8153
|
+
resolve6();
|
|
7946
8154
|
}, milliseconds);
|
|
7947
8155
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
7948
8156
|
})
|