@ai-development-environment/control-agent 0.0.142 → 0.0.144
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 +437 -135
- package/package.json +1 -1
package/dist/control-agent.js
CHANGED
|
@@ -7445,17 +7445,18 @@ var AgentGraphQLClient = class {
|
|
|
7445
7445
|
throw new Error("Downloaded dSYM checksum did not match");
|
|
7446
7446
|
}
|
|
7447
7447
|
}
|
|
7448
|
-
completeJob(jobId, status2, result, error, executionToken) {
|
|
7448
|
+
completeJob(jobId, status2, result, error, executionToken, cleanupVerified = true) {
|
|
7449
7449
|
return this.request(
|
|
7450
|
-
`mutation CompleteJob($jobId: ID!, $status: AgentJobStatus!, $result: JSON, $error: String, $executionToken: String) {
|
|
7451
|
-
completeAgentJob(jobId: $jobId, status: $status, result: $result, error: $error, executionToken: $executionToken) { id status }
|
|
7450
|
+
`mutation CompleteJob($jobId: ID!, $status: AgentJobStatus!, $result: JSON, $error: String, $executionToken: String, $cleanupVerified: Boolean!) {
|
|
7451
|
+
completeAgentJob(jobId: $jobId, status: $status, result: $result, error: $error, executionToken: $executionToken, cleanupVerified: $cleanupVerified) { id status }
|
|
7452
7452
|
}`,
|
|
7453
7453
|
{
|
|
7454
7454
|
jobId,
|
|
7455
7455
|
status: status2,
|
|
7456
7456
|
result,
|
|
7457
7457
|
error,
|
|
7458
|
-
...executionToken ? { executionToken } : {}
|
|
7458
|
+
...executionToken ? { executionToken } : {},
|
|
7459
|
+
cleanupVerified
|
|
7459
7460
|
}
|
|
7460
7461
|
);
|
|
7461
7462
|
}
|
|
@@ -7617,22 +7618,31 @@ var AgentGraphQLClient = class {
|
|
|
7617
7618
|
health() {
|
|
7618
7619
|
return this.request(`query Health { health }`);
|
|
7619
7620
|
}
|
|
7621
|
+
async requestDefaultBranchConfiguration(query) {
|
|
7622
|
+
try {
|
|
7623
|
+
return await this.request(query);
|
|
7624
|
+
} catch (error) {
|
|
7625
|
+
if (!(error instanceof Error) || !/Cannot query field "defaultBranchPatterns"/.test(error.message))
|
|
7626
|
+
throw error;
|
|
7627
|
+
return this.request(query.replace(/\sdefaultBranchPatterns\b/g, ""));
|
|
7628
|
+
}
|
|
7629
|
+
}
|
|
7620
7630
|
async agentCodebases() {
|
|
7621
|
-
const data = await this.
|
|
7631
|
+
const data = await this.requestDefaultBranchConfiguration(`query AgentCodebases {
|
|
7622
7632
|
agentCodebases {
|
|
7623
|
-
id folder canonicalOrigin defaultBranch keepBaseBranchUpToDate lastFetchedAt lastFetchAttemptAt
|
|
7633
|
+
id folder canonicalOrigin defaultBranch keepBaseBranchUpToDate defaultBranchPatterns lastFetchedAt lastFetchAttemptAt
|
|
7624
7634
|
worktrees { id folder branch gitDirectory baseBranchOverride }
|
|
7625
7635
|
}
|
|
7626
7636
|
}`);
|
|
7627
7637
|
return data.agentCodebases;
|
|
7628
7638
|
}
|
|
7629
7639
|
async agentCodebaseConfiguration() {
|
|
7630
|
-
const data = await this.
|
|
7640
|
+
const data = await this.requestDefaultBranchConfiguration(`query AgentCodebaseConfiguration {
|
|
7631
7641
|
agentCodebaseConfiguration {
|
|
7632
7642
|
refreshIntervalSeconds
|
|
7633
7643
|
fetchIntervalSeconds
|
|
7634
7644
|
codebases {
|
|
7635
|
-
id folder canonicalOrigin defaultBranch keepBaseBranchUpToDate lastFetchedAt lastFetchAttemptAt
|
|
7645
|
+
id folder canonicalOrigin defaultBranch keepBaseBranchUpToDate defaultBranchPatterns lastFetchedAt lastFetchAttemptAt
|
|
7636
7646
|
worktrees { id folder branch gitDirectory baseBranchOverride }
|
|
7637
7647
|
}
|
|
7638
7648
|
}
|
|
@@ -8134,6 +8144,50 @@ var CODEBASE_GIT_INSPECT_JOB_KIND = "codebase.git.inspect";
|
|
|
8134
8144
|
var CODEBASE_GIT_OPERATION_JOB_KIND = "codebase.git.operation";
|
|
8135
8145
|
var CODEBASE_BRANCHES_DELETE_JOB_KIND = "codebase.branches.delete";
|
|
8136
8146
|
var CODEBASE_RECONCILE_EVENT_CAPABILITY = "codebase.reconcile.requested";
|
|
8147
|
+
var CODEBASE_DEFAULT_BRANCHES_CAPABILITY = "codebase.default-branches.v1";
|
|
8148
|
+
function normalizeDefaultBranchPatterns(value) {
|
|
8149
|
+
if (!Array.isArray(value) || value.length > 100)
|
|
8150
|
+
throw new Error(
|
|
8151
|
+
"Default branch patterns must be a list of at most 100 entries"
|
|
8152
|
+
);
|
|
8153
|
+
const patterns = value.map((entry) => {
|
|
8154
|
+
if (typeof entry !== "string" || entry.trim().length > 255)
|
|
8155
|
+
throw new Error(
|
|
8156
|
+
"Default branch patterns must be strings of at most 255 characters"
|
|
8157
|
+
);
|
|
8158
|
+
const pattern = entry.trim();
|
|
8159
|
+
if (pattern && /[\s\x00-\x1f\x7f]/.test(pattern))
|
|
8160
|
+
throw new Error(
|
|
8161
|
+
"Default branch patterns cannot contain whitespace or control characters"
|
|
8162
|
+
);
|
|
8163
|
+
return pattern;
|
|
8164
|
+
});
|
|
8165
|
+
return [...new Set(patterns.filter(Boolean))];
|
|
8166
|
+
}
|
|
8167
|
+
function isDefaultBranch(branch, remoteDefaultBranch, patterns = []) {
|
|
8168
|
+
if (!branch) return false;
|
|
8169
|
+
if (branch === remoteDefaultBranch) return true;
|
|
8170
|
+
return patterns.some((pattern) => {
|
|
8171
|
+
let position = 0;
|
|
8172
|
+
let patternPosition = 0;
|
|
8173
|
+
let star = -1;
|
|
8174
|
+
let resume = 0;
|
|
8175
|
+
while (position < branch.length) {
|
|
8176
|
+
if (pattern[patternPosition] === "*") {
|
|
8177
|
+
star = patternPosition++;
|
|
8178
|
+
resume = position;
|
|
8179
|
+
} else if (pattern[patternPosition] === branch[position]) {
|
|
8180
|
+
patternPosition++;
|
|
8181
|
+
position++;
|
|
8182
|
+
} else if (star >= 0) {
|
|
8183
|
+
patternPosition = star + 1;
|
|
8184
|
+
position = ++resume;
|
|
8185
|
+
} else return false;
|
|
8186
|
+
}
|
|
8187
|
+
while (pattern[patternPosition] === "*") patternPosition++;
|
|
8188
|
+
return patternPosition === pattern.length;
|
|
8189
|
+
});
|
|
8190
|
+
}
|
|
8137
8191
|
var MAX_CODEBASE_GIT_BRANCHES = 1e3;
|
|
8138
8192
|
var MAX_CODEBASE_STASHES = 200;
|
|
8139
8193
|
var MAX_CODEBASE_STASH_PATCH_BYTES = 256 * 1024;
|
|
@@ -8439,7 +8493,8 @@ function codebaseJobPayload(value) {
|
|
|
8439
8493
|
"codebaseId",
|
|
8440
8494
|
"expectedOrigin",
|
|
8441
8495
|
"baseBranch",
|
|
8442
|
-
"keepBaseBranchUpToDate"
|
|
8496
|
+
"keepBaseBranchUpToDate",
|
|
8497
|
+
"defaultBranchPatterns"
|
|
8443
8498
|
]);
|
|
8444
8499
|
const unexpected = Object.keys(payload).find((key) => !allowed.has(key));
|
|
8445
8500
|
if (unexpected)
|
|
@@ -8461,6 +8516,11 @@ function codebaseJobPayload(value) {
|
|
|
8461
8516
|
payload.keepBaseBranchUpToDate,
|
|
8462
8517
|
"payload.keepBaseBranchUpToDate"
|
|
8463
8518
|
)
|
|
8519
|
+
},
|
|
8520
|
+
...payload.defaultBranchPatterns === void 0 ? {} : {
|
|
8521
|
+
defaultBranchPatterns: normalizeDefaultBranchPatterns(
|
|
8522
|
+
payload.defaultBranchPatterns
|
|
8523
|
+
)
|
|
8464
8524
|
}
|
|
8465
8525
|
};
|
|
8466
8526
|
}
|
|
@@ -11683,7 +11743,7 @@ function tailscaleServeRemovePayload(value) {
|
|
|
11683
11743
|
// package.json
|
|
11684
11744
|
var package_default = {
|
|
11685
11745
|
name: "@ai-development-environment/control-agent",
|
|
11686
|
-
version: "0.0.
|
|
11746
|
+
version: "0.0.144",
|
|
11687
11747
|
description: "Control agent for the AI Development Environment",
|
|
11688
11748
|
license: "MIT",
|
|
11689
11749
|
repository: {
|
|
@@ -11753,6 +11813,7 @@ var PORTABLE_AGENT_CAPABILITIES = [
|
|
|
11753
11813
|
CCUSAGE_REPORT_JOB_KIND,
|
|
11754
11814
|
...CODEBASE_JOB_KINDS,
|
|
11755
11815
|
CODEBASE_RECONCILE_EVENT_CAPABILITY,
|
|
11816
|
+
CODEBASE_DEFAULT_BRANCHES_CAPABILITY,
|
|
11756
11817
|
...WORKTREE_JOB_KINDS,
|
|
11757
11818
|
WORKTREE_REVIEWED_SYNC_CAPABILITY,
|
|
11758
11819
|
...SKILL_JOB_KINDS,
|
|
@@ -11840,6 +11901,12 @@ import {
|
|
|
11840
11901
|
writeFileSync
|
|
11841
11902
|
} from "node:fs";
|
|
11842
11903
|
import { dirname as dirname2, join as join2 } from "node:path";
|
|
11904
|
+
var ProcessInspectionTimeoutError = class extends Error {
|
|
11905
|
+
constructor() {
|
|
11906
|
+
super("Process inspection timed out after 30 seconds");
|
|
11907
|
+
this.name = "ProcessInspectionTimeoutError";
|
|
11908
|
+
}
|
|
11909
|
+
};
|
|
11843
11910
|
var executions = new AsyncLocalStorage();
|
|
11844
11911
|
function executionEnvironment() {
|
|
11845
11912
|
const journal = executions.getStore();
|
|
@@ -11983,11 +12050,18 @@ var spawnExecutionProcess = ((command3, args = [], options = {}) => {
|
|
|
11983
12050
|
});
|
|
11984
12051
|
function processTable(environment = false) {
|
|
11985
12052
|
const args = environment ? ["axeww", "-o", "pid=,pgid=,stat=,command="] : ["axww", "-o", "pid=,pgid=,stat=,command="];
|
|
11986
|
-
|
|
11987
|
-
|
|
11988
|
-
|
|
11989
|
-
|
|
11990
|
-
|
|
12053
|
+
let output;
|
|
12054
|
+
try {
|
|
12055
|
+
output = execFileSync("/bin/ps", args, {
|
|
12056
|
+
encoding: "utf8",
|
|
12057
|
+
timeout: 3e4,
|
|
12058
|
+
maxBuffer: 32 * 1024 * 1024
|
|
12059
|
+
});
|
|
12060
|
+
} catch (error) {
|
|
12061
|
+
if (error.code === "ETIMEDOUT")
|
|
12062
|
+
throw new ProcessInspectionTimeoutError();
|
|
12063
|
+
throw error;
|
|
12064
|
+
}
|
|
11991
12065
|
return output.split("\n").flatMap((line) => {
|
|
11992
12066
|
const match = /^\s*(\d+)\s+(\d+)\s+(\S+)\s+(.*)$/.exec(line);
|
|
11993
12067
|
return match ? [
|
|
@@ -12026,6 +12100,7 @@ var delay = (ms, signal2) => new Promise((resolve13) => {
|
|
|
12026
12100
|
if (signal2?.aborted) finish();
|
|
12027
12101
|
});
|
|
12028
12102
|
async function cleanupGroups(journal, signal2) {
|
|
12103
|
+
if (!journal.groups.length) return;
|
|
12029
12104
|
const groups = new Set(journal.groups);
|
|
12030
12105
|
const ownGroup = process.platform !== "win32" ? processTable().find((entry) => entry.pid === process.pid)?.group : process.pid;
|
|
12031
12106
|
if (process.platform !== "win32") {
|
|
@@ -12106,12 +12181,40 @@ async function withExecutionJournal(identity, task) {
|
|
|
12106
12181
|
try {
|
|
12107
12182
|
return await executions.run(
|
|
12108
12183
|
journal,
|
|
12109
|
-
() => task({
|
|
12184
|
+
() => task({
|
|
12185
|
+
cleanupProcesses: () => cleanupGroups(journal),
|
|
12186
|
+
settle,
|
|
12187
|
+
checkpointCompletion: (completion) => {
|
|
12188
|
+
journal.completion = completion;
|
|
12189
|
+
persist(journal);
|
|
12190
|
+
}
|
|
12191
|
+
})
|
|
12110
12192
|
);
|
|
12111
12193
|
} finally {
|
|
12112
12194
|
await settle();
|
|
12113
12195
|
}
|
|
12114
12196
|
}
|
|
12197
|
+
function executionCompletion(identity) {
|
|
12198
|
+
let journal;
|
|
12199
|
+
try {
|
|
12200
|
+
journal = JSON.parse(readFileSync(journalPath(identity), "utf8"));
|
|
12201
|
+
} catch (error) {
|
|
12202
|
+
if (error.code === "ENOENT") return void 0;
|
|
12203
|
+
throw error;
|
|
12204
|
+
}
|
|
12205
|
+
if (journal.version !== 1 || journal.agentId !== identity.agentId || journal.jobId !== identity.jobId || journal.executionToken !== identity.executionToken || journal.marker !== digest(identity.executionToken) || !Array.isArray(journal.groups))
|
|
12206
|
+
throw new Error(
|
|
12207
|
+
"Execution journal identity is invalid; its claim remains protected"
|
|
12208
|
+
);
|
|
12209
|
+
const completion = journal.completion;
|
|
12210
|
+
if (completion && (!["SUCCEEDED", "FAILED", "CANCELLED", "TIMED_OUT"].includes(
|
|
12211
|
+
completion.status
|
|
12212
|
+
) || !completion.result || typeof completion.result !== "object" || typeof completion.result.cancelled !== "boolean" || typeof completion.result.timedOut !== "boolean" || !(completion.result.exitCode === null || Number.isInteger(completion.result.exitCode))))
|
|
12213
|
+
throw new Error(
|
|
12214
|
+
"Execution completion checkpoint is invalid; its claim remains protected"
|
|
12215
|
+
);
|
|
12216
|
+
return completion;
|
|
12217
|
+
}
|
|
12115
12218
|
async function recoverExecution(identity, signal2) {
|
|
12116
12219
|
let journal;
|
|
12117
12220
|
try {
|
|
@@ -45725,76 +45828,165 @@ var refreshCodebase = async (payload, timeoutMs, signal2) => {
|
|
|
45725
45828
|
)
|
|
45726
45829
|
};
|
|
45727
45830
|
};
|
|
45728
|
-
async function updateBaseBranchAfterFetch(folder, baseBranch, timeoutMs, signal2) {
|
|
45729
|
-
const localRef = `refs/heads/${baseBranch}`;
|
|
45730
|
-
const remoteRef = `refs/remotes/origin/${baseBranch}`;
|
|
45831
|
+
async function updateBaseBranchAfterFetch(folder, baseBranch, timeoutMs, signal2, expectedOrigin, expectedGitDirectory) {
|
|
45731
45832
|
try {
|
|
45732
|
-
|
|
45833
|
+
return await withRepositoryMetadata(
|
|
45733
45834
|
folder,
|
|
45734
|
-
|
|
45735
|
-
|
|
45736
|
-
|
|
45737
|
-
|
|
45738
|
-
|
|
45739
|
-
|
|
45740
|
-
|
|
45741
|
-
|
|
45742
|
-
|
|
45743
|
-
|
|
45744
|
-
|
|
45745
|
-
|
|
45746
|
-
|
|
45747
|
-
|
|
45748
|
-
|
|
45749
|
-
|
|
45750
|
-
|
|
45751
|
-
|
|
45752
|
-
|
|
45753
|
-
|
|
45754
|
-
|
|
45755
|
-
|
|
45756
|
-
|
|
45757
|
-
|
|
45758
|
-
|
|
45759
|
-
|
|
45760
|
-
|
|
45835
|
+
async () => {
|
|
45836
|
+
if (expectedGitDirectory) {
|
|
45837
|
+
const directory = await git2(
|
|
45838
|
+
folder,
|
|
45839
|
+
["rev-parse", "--path-format=absolute", "--git-dir"],
|
|
45840
|
+
timeoutMs,
|
|
45841
|
+
signal2
|
|
45842
|
+
);
|
|
45843
|
+
if (directory.exitCode !== 0 || await realpath3(directory.stdout.trim()) !== expectedGitDirectory)
|
|
45844
|
+
return false;
|
|
45845
|
+
}
|
|
45846
|
+
const current = await git2(
|
|
45847
|
+
folder,
|
|
45848
|
+
["symbolic-ref", "--short", "-q", "HEAD"],
|
|
45849
|
+
timeoutMs,
|
|
45850
|
+
signal2
|
|
45851
|
+
);
|
|
45852
|
+
if (current.exitCode !== 0 || current.stdout.trim() !== baseBranch)
|
|
45853
|
+
return false;
|
|
45854
|
+
if (expectedOrigin) {
|
|
45855
|
+
const remote = await git2(
|
|
45856
|
+
folder,
|
|
45857
|
+
["remote", "get-url", "origin"],
|
|
45858
|
+
timeoutMs,
|
|
45859
|
+
signal2
|
|
45860
|
+
);
|
|
45861
|
+
if (remote.exitCode !== 0 || normalizeGitOrigin(remote.stdout.trim()).canonicalOrigin !== expectedOrigin)
|
|
45862
|
+
return false;
|
|
45863
|
+
}
|
|
45864
|
+
const status2 = await git2(
|
|
45865
|
+
folder,
|
|
45866
|
+
[
|
|
45867
|
+
"status",
|
|
45868
|
+
"--porcelain=v1",
|
|
45869
|
+
"-z",
|
|
45870
|
+
"--untracked-files=all",
|
|
45871
|
+
"--ignore-submodules=none"
|
|
45872
|
+
],
|
|
45873
|
+
timeoutMs,
|
|
45874
|
+
signal2
|
|
45875
|
+
);
|
|
45876
|
+
if (status2.exitCode !== 0 || status2.outputTruncated || status2.stdout)
|
|
45877
|
+
return false;
|
|
45878
|
+
for (const name of [
|
|
45879
|
+
"MERGE_HEAD",
|
|
45880
|
+
"rebase-merge",
|
|
45881
|
+
"rebase-apply",
|
|
45882
|
+
"CHERRY_PICK_HEAD",
|
|
45883
|
+
"REVERT_HEAD"
|
|
45884
|
+
]) {
|
|
45885
|
+
const path = await git2(
|
|
45886
|
+
folder,
|
|
45887
|
+
["rev-parse", "--path-format=absolute", "--git-path", name],
|
|
45888
|
+
timeoutMs,
|
|
45889
|
+
signal2
|
|
45890
|
+
);
|
|
45891
|
+
if (path.exitCode !== 0) return false;
|
|
45892
|
+
try {
|
|
45893
|
+
await stat4(path.stdout.trim());
|
|
45894
|
+
return false;
|
|
45895
|
+
} catch (error) {
|
|
45896
|
+
if (error.code !== "ENOENT") throw error;
|
|
45897
|
+
}
|
|
45898
|
+
}
|
|
45899
|
+
const remoteRef = `refs/remotes/origin/${baseBranch}`;
|
|
45900
|
+
const valid = await git2(
|
|
45901
|
+
folder,
|
|
45902
|
+
["check-ref-format", remoteRef],
|
|
45903
|
+
timeoutMs,
|
|
45904
|
+
signal2
|
|
45905
|
+
);
|
|
45906
|
+
if (valid.exitCode !== 0) return false;
|
|
45907
|
+
const tip = await git2(
|
|
45908
|
+
folder,
|
|
45909
|
+
["rev-parse", "--verify", remoteRef],
|
|
45910
|
+
timeoutMs,
|
|
45911
|
+
signal2
|
|
45912
|
+
);
|
|
45913
|
+
if (tip.exitCode !== 0) return false;
|
|
45914
|
+
const ancestor = await git2(
|
|
45915
|
+
folder,
|
|
45916
|
+
["merge-base", "--is-ancestor", "HEAD", tip.stdout.trim()],
|
|
45917
|
+
timeoutMs,
|
|
45918
|
+
signal2
|
|
45919
|
+
);
|
|
45920
|
+
if (ancestor.exitCode !== 0) return false;
|
|
45921
|
+
const branch = await git2(
|
|
45922
|
+
folder,
|
|
45923
|
+
["symbolic-ref", "--short", "-q", "HEAD"],
|
|
45924
|
+
timeoutMs,
|
|
45925
|
+
signal2
|
|
45926
|
+
);
|
|
45927
|
+
if (branch.exitCode !== 0 || branch.stdout.trim() !== baseBranch)
|
|
45928
|
+
return false;
|
|
45929
|
+
return (await git2(
|
|
45930
|
+
folder,
|
|
45931
|
+
["merge", "--ff-only", tip.stdout.trim()],
|
|
45932
|
+
timeoutMs,
|
|
45933
|
+
signal2
|
|
45934
|
+
)).exitCode === 0;
|
|
45935
|
+
},
|
|
45761
45936
|
signal2
|
|
45762
45937
|
);
|
|
45763
|
-
|
|
45764
|
-
|
|
45765
|
-
|
|
45766
|
-
|
|
45767
|
-
|
|
45768
|
-
|
|
45769
|
-
|
|
45770
|
-
|
|
45771
|
-
}
|
|
45772
|
-
const localExists = await git2(
|
|
45938
|
+
} catch (error) {
|
|
45939
|
+
if (signal2.aborted) throw error;
|
|
45940
|
+
return false;
|
|
45941
|
+
}
|
|
45942
|
+
}
|
|
45943
|
+
async function updateDefaultBranchesAfterFetch(folder, defaultBranch, patterns, timeoutMs, signal2, withCheckout, expectedOrigin) {
|
|
45944
|
+
const listed = requireSuccess(
|
|
45945
|
+
await git2(
|
|
45773
45946
|
folder,
|
|
45774
|
-
["
|
|
45947
|
+
["worktree", "list", "--porcelain", "-z"],
|
|
45775
45948
|
timeoutMs,
|
|
45776
45949
|
signal2
|
|
45777
|
-
)
|
|
45778
|
-
|
|
45779
|
-
|
|
45780
|
-
|
|
45781
|
-
|
|
45782
|
-
|
|
45783
|
-
|
|
45950
|
+
),
|
|
45951
|
+
"Could not list worktrees"
|
|
45952
|
+
);
|
|
45953
|
+
if (listed.outputTruncated) return;
|
|
45954
|
+
for (const block of listed.stdout.split("\0\0")) {
|
|
45955
|
+
signal2.throwIfAborted();
|
|
45956
|
+
const fields = block.split("\0");
|
|
45957
|
+
if (fields.some((field) => field.startsWith("prunable")) || fields.includes("bare"))
|
|
45958
|
+
continue;
|
|
45959
|
+
const checkout = fields.find((field) => field.startsWith("worktree "))?.slice(9);
|
|
45960
|
+
const branch = fields.find((field) => field.startsWith("branch refs/heads/"))?.slice(18);
|
|
45961
|
+
if (!checkout || !isDefaultBranch(branch, defaultBranch, patterns))
|
|
45962
|
+
continue;
|
|
45963
|
+
try {
|
|
45964
|
+
const path = await realpath3(checkout);
|
|
45965
|
+
const directory = requireSuccess(
|
|
45966
|
+
await git2(
|
|
45967
|
+
path,
|
|
45968
|
+
["rev-parse", "--path-format=absolute", "--git-dir"],
|
|
45969
|
+
timeoutMs,
|
|
45970
|
+
signal2
|
|
45971
|
+
),
|
|
45972
|
+
"Could not resolve worktree"
|
|
45973
|
+
).stdout.trim();
|
|
45974
|
+
const expectedGitDirectory = await realpath3(directory);
|
|
45975
|
+
await withCheckout(
|
|
45976
|
+
path,
|
|
45977
|
+
expectedGitDirectory,
|
|
45978
|
+
() => updateBaseBranchAfterFetch(
|
|
45979
|
+
path,
|
|
45980
|
+
branch,
|
|
45981
|
+
timeoutMs,
|
|
45982
|
+
signal2,
|
|
45983
|
+
expectedOrigin,
|
|
45984
|
+
expectedGitDirectory
|
|
45985
|
+
)
|
|
45784
45986
|
);
|
|
45785
|
-
|
|
45786
|
-
|
|
45787
|
-
return false;
|
|
45987
|
+
} catch (error) {
|
|
45988
|
+
if (signal2.aborted) throw error;
|
|
45788
45989
|
}
|
|
45789
|
-
const updated = await git2(
|
|
45790
|
-
folder,
|
|
45791
|
-
["branch", "--force", baseBranch, remoteTip],
|
|
45792
|
-
timeoutMs,
|
|
45793
|
-
signal2
|
|
45794
|
-
);
|
|
45795
|
-
return updated.exitCode === 0;
|
|
45796
|
-
} catch {
|
|
45797
|
-
return false;
|
|
45798
45990
|
}
|
|
45799
45991
|
}
|
|
45800
45992
|
var fetchCodebase = async (payload, timeoutMs, signal2, onLog, context) => {
|
|
@@ -45833,46 +46025,37 @@ var fetchCodebase = async (payload, timeoutMs, signal2, onLog, context) => {
|
|
|
45833
46025
|
if (error instanceof InterruptedGitInspection) return error.result;
|
|
45834
46026
|
throw error;
|
|
45835
46027
|
}
|
|
45836
|
-
if (result.exitCode === 0 && input.keepBaseBranchUpToDate
|
|
45837
|
-
await log2(
|
|
45838
|
-
|
|
46028
|
+
if (result.exitCode === 0 && input.keepBaseBranchUpToDate) {
|
|
46029
|
+
await log2(
|
|
46030
|
+
"Updating clean default branch worktrees from their matching origin branches"
|
|
46031
|
+
);
|
|
46032
|
+
await updateDefaultBranchesAfterFetch(
|
|
45839
46033
|
input.folder,
|
|
45840
|
-
input.baseBranch,
|
|
46034
|
+
input.baseBranch ?? null,
|
|
46035
|
+
input.defaultBranchPatterns ?? [],
|
|
45841
46036
|
timeoutMs,
|
|
45842
|
-
signal2
|
|
45843
|
-
|
|
45844
|
-
|
|
45845
|
-
|
|
45846
|
-
|
|
45847
|
-
|
|
45848
|
-
|
|
45849
|
-
|
|
45850
|
-
|
|
45851
|
-
|
|
45852
|
-
|
|
45853
|
-
|
|
45854
|
-
|
|
45855
|
-
|
|
45856
|
-
|
|
45857
|
-
|
|
45858
|
-
|
|
45859
|
-
|
|
45860
|
-
|
|
45861
|
-
|
|
45862
|
-
|
|
45863
|
-
|
|
45864
|
-
key: input.folder,
|
|
45865
|
-
path: input.folder,
|
|
45866
|
-
agentId: context.agentId,
|
|
45867
|
-
codebaseId: input.codebaseId,
|
|
45868
|
-
mode: "WRITE"
|
|
45869
|
-
}
|
|
45870
|
-
],
|
|
45871
|
-
update
|
|
45872
|
-
) : { ran: true, result: await update() };
|
|
45873
|
-
const updated = grant.ran && grant.result;
|
|
45874
|
-
await log2(
|
|
45875
|
-
updated ? "Base branch updated" : "Base branch left unchanged because it could not be safely fast-forwarded"
|
|
46037
|
+
signal2,
|
|
46038
|
+
(folder, gitDirectory2, update) => context?.tryWithOperationResources ? context.tryWithOperationResources(
|
|
46039
|
+
[
|
|
46040
|
+
{
|
|
46041
|
+
kind: "WORKTREE",
|
|
46042
|
+
key: `${context.agentId}:worktree:${gitDirectory2}`,
|
|
46043
|
+
agentId: context.agentId,
|
|
46044
|
+
codebaseId: input.codebaseId,
|
|
46045
|
+
mode: "WRITE"
|
|
46046
|
+
},
|
|
46047
|
+
{
|
|
46048
|
+
kind: "PATH",
|
|
46049
|
+
key: folder,
|
|
46050
|
+
path: folder,
|
|
46051
|
+
agentId: context.agentId,
|
|
46052
|
+
codebaseId: input.codebaseId,
|
|
46053
|
+
mode: "WRITE"
|
|
46054
|
+
}
|
|
46055
|
+
],
|
|
46056
|
+
update
|
|
46057
|
+
) : update(),
|
|
46058
|
+
input.expectedOrigin
|
|
45876
46059
|
);
|
|
45877
46060
|
}
|
|
45878
46061
|
const afterResult = await inspectCodebaseProcess(
|
|
@@ -49903,7 +50086,7 @@ var runIosBuildWithPaths = async (payload, timeoutMs, signal2, onLog, context) =
|
|
|
49903
50086
|
finalSignal
|
|
49904
50087
|
);
|
|
49905
50088
|
const codeStateObservedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
49906
|
-
|
|
50089
|
+
const completedResult = {
|
|
49907
50090
|
exitCode,
|
|
49908
50091
|
signal: buildResult?.signal ?? preflightResult?.signal ?? null,
|
|
49909
50092
|
timedOut,
|
|
@@ -49918,6 +50101,8 @@ var runIosBuildWithPaths = async (payload, timeoutMs, signal2, onLog, context) =
|
|
|
49918
50101
|
finalStateHash,
|
|
49919
50102
|
codeStateObservedAt
|
|
49920
50103
|
};
|
|
50104
|
+
await context?.finalizeBuildResult?.(completedResult);
|
|
50105
|
+
return completedResult;
|
|
49921
50106
|
};
|
|
49922
50107
|
let enteredProtectedBuild = false;
|
|
49923
50108
|
try {
|
|
@@ -57035,6 +57220,16 @@ async function refreshFetchedCodebase(client, input, timeoutMs, signal2, withIns
|
|
|
57035
57220
|
}
|
|
57036
57221
|
|
|
57037
57222
|
// src/job-executor.ts
|
|
57223
|
+
var PROCESS_INSPECTION_WARNING = "Process inspection timed out after 30 seconds. The completed build result and artifacts were preserved; process cleanup will be retried.";
|
|
57224
|
+
var resultStatus = (result) => result.cancelled ? "CANCELLED" : result.timedOut ? "TIMED_OUT" : result.exitCode === 0 ? "SUCCEEDED" : "FAILED";
|
|
57225
|
+
function warnPreservedBuild(completion) {
|
|
57226
|
+
completion.result.warnings = [
|
|
57227
|
+
.../* @__PURE__ */ new Set([
|
|
57228
|
+
...completion.result.warnings ?? [],
|
|
57229
|
+
PROCESS_INSPECTION_WARNING
|
|
57230
|
+
])
|
|
57231
|
+
];
|
|
57232
|
+
}
|
|
57038
57233
|
function resourceSignature(value) {
|
|
57039
57234
|
return JSON.stringify(
|
|
57040
57235
|
parseOperationDescriptor(value).resources.map(
|
|
@@ -57058,18 +57253,20 @@ var JobExecutor = class {
|
|
|
57058
57253
|
repositoryCoordinator;
|
|
57059
57254
|
running = /* @__PURE__ */ new Map();
|
|
57060
57255
|
stopping = false;
|
|
57256
|
+
cleanupPending = /* @__PURE__ */ new Set();
|
|
57061
57257
|
shutdown = new AbortController();
|
|
57062
57258
|
execute(job) {
|
|
57063
57259
|
if (this.stopping || this.running.has(job.id)) return;
|
|
57064
57260
|
const controller = new AbortController();
|
|
57065
|
-
const task = this.run(job, controller).finally(
|
|
57066
|
-
|
|
57067
|
-
|
|
57261
|
+
const task = this.run(job, controller).finally(() => {
|
|
57262
|
+
this.running.delete(job.id);
|
|
57263
|
+
this.cleanupPending.delete(job.id);
|
|
57264
|
+
});
|
|
57068
57265
|
this.running.set(job.id, { controller, task });
|
|
57069
57266
|
}
|
|
57070
57267
|
cancel(jobId) {
|
|
57071
57268
|
const running = this.running.get(jobId);
|
|
57072
|
-
running?.controller.abort();
|
|
57269
|
+
if (!this.cleanupPending.has(jobId)) running?.controller.abort();
|
|
57073
57270
|
return Boolean(running);
|
|
57074
57271
|
}
|
|
57075
57272
|
/** Hold source resources until interrupted subprocess cleanup is verified. */
|
|
@@ -57093,19 +57290,46 @@ var JobExecutor = class {
|
|
|
57093
57290
|
async () => {
|
|
57094
57291
|
entered.resolve();
|
|
57095
57292
|
let reported = false;
|
|
57293
|
+
let completion;
|
|
57294
|
+
let checkpointRead = false;
|
|
57295
|
+
let completedStatus = status2;
|
|
57096
57296
|
while (!this.shutdown.signal.aborted) {
|
|
57097
57297
|
try {
|
|
57298
|
+
if (!checkpointRead) {
|
|
57299
|
+
completion = job.kind === IOS_BUILD_JOB_KIND ? executionCompletion(identity) : void 0;
|
|
57300
|
+
checkpointRead = true;
|
|
57301
|
+
if (completion && ["RUNNING", "CANCELLING"].includes(job.status))
|
|
57302
|
+
completedStatus = completion.status;
|
|
57303
|
+
}
|
|
57098
57304
|
await recoverExecution(identity, this.shutdown.signal);
|
|
57099
57305
|
await this.client.completeJob(
|
|
57100
57306
|
job.id,
|
|
57101
|
-
|
|
57102
|
-
|
|
57103
|
-
"Agent service restarted; interrupted processes were stopped and verified",
|
|
57307
|
+
completedStatus,
|
|
57308
|
+
completion?.result,
|
|
57309
|
+
completion?.error ?? (completion ? void 0 : "Agent service restarted; interrupted processes were stopped and verified"),
|
|
57104
57310
|
job.executionToken
|
|
57105
57311
|
);
|
|
57106
57312
|
forgetExecution(identity);
|
|
57107
57313
|
return;
|
|
57108
57314
|
} catch (error) {
|
|
57315
|
+
if (completion && error instanceof ProcessInspectionTimeoutError) {
|
|
57316
|
+
warnPreservedBuild(completion);
|
|
57317
|
+
try {
|
|
57318
|
+
await this.client.completeJob(
|
|
57319
|
+
job.id,
|
|
57320
|
+
completedStatus,
|
|
57321
|
+
completion.result,
|
|
57322
|
+
completion.error,
|
|
57323
|
+
job.executionToken,
|
|
57324
|
+
false
|
|
57325
|
+
);
|
|
57326
|
+
} catch (reportError) {
|
|
57327
|
+
console.error(
|
|
57328
|
+
`Could not preserve completed build ${job.id}:`,
|
|
57329
|
+
reportError
|
|
57330
|
+
);
|
|
57331
|
+
}
|
|
57332
|
+
}
|
|
57109
57333
|
if (!reported) {
|
|
57110
57334
|
console.error(
|
|
57111
57335
|
`Cleanup verification required for job ${job.id}; execution resources remain protected:`,
|
|
@@ -57177,6 +57401,7 @@ var JobExecutor = class {
|
|
|
57177
57401
|
const handler = handlers[claimed.kind];
|
|
57178
57402
|
if (!handler)
|
|
57179
57403
|
throw new Error(`No local handler is registered for ${claimed.kind}`);
|
|
57404
|
+
let execution;
|
|
57180
57405
|
const runHandler = () => {
|
|
57181
57406
|
controller.signal.throwIfAborted();
|
|
57182
57407
|
return handler(
|
|
@@ -57232,6 +57457,58 @@ var JobExecutor = class {
|
|
|
57232
57457
|
)
|
|
57233
57458
|
),
|
|
57234
57459
|
reportBuildProgress: (input) => this.client.reportBuildProgress(input),
|
|
57460
|
+
...resourceAware && claimed.kind === IOS_BUILD_JOB_KIND ? {
|
|
57461
|
+
finalizeBuildResult: async (completedResult) => {
|
|
57462
|
+
if (!execution)
|
|
57463
|
+
throw new Error("Build execution journal is unavailable");
|
|
57464
|
+
result = completedResult;
|
|
57465
|
+
const completion = {
|
|
57466
|
+
status: resultStatus(completedResult),
|
|
57467
|
+
result: completedResult,
|
|
57468
|
+
...completedResult.exitCode !== 0 && !completedResult.cancelled && !completedResult.timedOut ? {
|
|
57469
|
+
error: `Process exited with code ${completedResult.exitCode}`
|
|
57470
|
+
} : {}
|
|
57471
|
+
};
|
|
57472
|
+
execution.checkpointCompletion(completion);
|
|
57473
|
+
this.cleanupPending.add(claimed.id);
|
|
57474
|
+
try {
|
|
57475
|
+
await execution.settle();
|
|
57476
|
+
return;
|
|
57477
|
+
} catch (error) {
|
|
57478
|
+
if (!(error instanceof ProcessInspectionTimeoutError))
|
|
57479
|
+
throw error;
|
|
57480
|
+
}
|
|
57481
|
+
warnPreservedBuild(completion);
|
|
57482
|
+
execution.checkpointCompletion(completion);
|
|
57483
|
+
let preserved = false;
|
|
57484
|
+
while (!this.shutdown.signal.aborted) {
|
|
57485
|
+
try {
|
|
57486
|
+
if (!preserved) {
|
|
57487
|
+
await this.client.completeJob(
|
|
57488
|
+
claimed.id,
|
|
57489
|
+
completion.status,
|
|
57490
|
+
completion.result,
|
|
57491
|
+
completion.error,
|
|
57492
|
+
claimed.executionToken,
|
|
57493
|
+
false
|
|
57494
|
+
);
|
|
57495
|
+
preserved = true;
|
|
57496
|
+
}
|
|
57497
|
+
await execution.settle();
|
|
57498
|
+
return;
|
|
57499
|
+
} catch (error) {
|
|
57500
|
+
console.error(
|
|
57501
|
+
`Build ${claimed.id} result is preserved; cleanup verification will retry:`,
|
|
57502
|
+
error instanceof Error ? error.message : error
|
|
57503
|
+
);
|
|
57504
|
+
await waitForAgentRetry(1e3, this.shutdown.signal);
|
|
57505
|
+
}
|
|
57506
|
+
}
|
|
57507
|
+
throw new Error(
|
|
57508
|
+
"Build cleanup interrupted by agent shutdown; its result remains protected"
|
|
57509
|
+
);
|
|
57510
|
+
}
|
|
57511
|
+
} : {},
|
|
57235
57512
|
reportBuildDeploymentLaunched: (deploymentId, launchedAt) => this.client.reportBuildDeploymentLaunched(
|
|
57236
57513
|
claimed.id,
|
|
57237
57514
|
deploymentId,
|
|
@@ -57256,7 +57533,10 @@ var JobExecutor = class {
|
|
|
57256
57533
|
jobId: claimed.id,
|
|
57257
57534
|
executionToken: claimed.executionToken
|
|
57258
57535
|
},
|
|
57259
|
-
|
|
57536
|
+
(journal) => {
|
|
57537
|
+
execution = journal;
|
|
57538
|
+
return runHandler();
|
|
57539
|
+
}
|
|
57260
57540
|
);
|
|
57261
57541
|
else if (!codebaseId) result = await runHandler();
|
|
57262
57542
|
else if (claimed.kind === IOS_BUILD_JOB_KIND) {
|
|
@@ -57285,6 +57565,7 @@ var JobExecutor = class {
|
|
|
57285
57565
|
status2 = controller.signal.aborted ? "CANCELLED" : "FAILED";
|
|
57286
57566
|
console.error(`Job ${claimed.id} failed:`, completionError);
|
|
57287
57567
|
}
|
|
57568
|
+
if (this.stopping && this.cleanupPending.has(claimed.id)) return;
|
|
57288
57569
|
for (let attempt = 1; ; attempt++) {
|
|
57289
57570
|
try {
|
|
57290
57571
|
if (resourceAware && claimed.executionToken)
|
|
@@ -57465,22 +57746,43 @@ var CodebaseMonitor = class {
|
|
|
57465
57746
|
signal2
|
|
57466
57747
|
);
|
|
57467
57748
|
if (fetchResult.exitCode === 0) {
|
|
57468
|
-
if (codebase.keepBaseBranchUpToDate && codebase.defaultBranch) {
|
|
57469
|
-
const baseBranch = codebase.defaultBranch;
|
|
57470
|
-
await runExclusive(
|
|
57471
|
-
() => updateBaseBranchAfterFetch(
|
|
57472
|
-
codebase.folder,
|
|
57473
|
-
baseBranch,
|
|
57474
|
-
INSPECTION_TIMEOUT_MS,
|
|
57475
|
-
signal2
|
|
57476
|
-
)
|
|
57477
|
-
);
|
|
57478
|
-
}
|
|
57479
57749
|
snapshot = await this.inspectRoot(codebase, signal2);
|
|
57480
57750
|
} else {
|
|
57481
57751
|
fetchError = (fetchResult.stderr || "Git fetch failed").replace(/([a-z][a-z0-9+.-]*:\/\/)[^\s/@]+@/gi, "$1").slice(0, 2e3);
|
|
57482
57752
|
}
|
|
57483
57753
|
}
|
|
57754
|
+
if (snapshot.availability === "AVAILABLE" && codebase.keepBaseBranchUpToDate && !signal2.aborted) {
|
|
57755
|
+
await updateDefaultBranchesAfterFetch(
|
|
57756
|
+
codebase.folder,
|
|
57757
|
+
codebase.defaultBranch,
|
|
57758
|
+
codebase.defaultBranchPatterns ?? [],
|
|
57759
|
+
INSPECTION_TIMEOUT_MS,
|
|
57760
|
+
signal2,
|
|
57761
|
+
(folder, gitDirectory2, update) => this.agentId ? this.repositoryCoordinator.tryWithResources(
|
|
57762
|
+
[
|
|
57763
|
+
{
|
|
57764
|
+
kind: "WORKTREE",
|
|
57765
|
+
key: `${this.agentId}:worktree:${gitDirectory2}`,
|
|
57766
|
+
agentId: this.agentId,
|
|
57767
|
+
codebaseId: codebase.id,
|
|
57768
|
+
mode: "WRITE"
|
|
57769
|
+
},
|
|
57770
|
+
{
|
|
57771
|
+
kind: "PATH",
|
|
57772
|
+
key: folder,
|
|
57773
|
+
path: folder,
|
|
57774
|
+
agentId: this.agentId,
|
|
57775
|
+
codebaseId: codebase.id,
|
|
57776
|
+
mode: "WRITE"
|
|
57777
|
+
}
|
|
57778
|
+
],
|
|
57779
|
+
update,
|
|
57780
|
+
signal2
|
|
57781
|
+
) : runExclusive(update),
|
|
57782
|
+
codebase.canonicalOrigin
|
|
57783
|
+
);
|
|
57784
|
+
snapshot = await this.inspectRoot(codebase, signal2);
|
|
57785
|
+
}
|
|
57484
57786
|
let inventory;
|
|
57485
57787
|
try {
|
|
57486
57788
|
inventory = await discoverWorktrees(
|