@bastani/atomic 0.9.60 → 0.9.61-alpha.2
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/CHANGELOG.md +80 -0
- package/dist/builtin/intercom/index.bundle.mjs +3 -3
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/CHANGELOG.md +12 -0
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +14 -0
- package/dist/builtin/workflows/README.md +9 -7
- package/dist/builtin/workflows/builtin/{chunk-h6bt0r0g.js → chunk-a2j0x1a6.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-g3b2zf8r.js → chunk-vzbec50q.js} +1 -1
- package/dist/builtin/workflows/builtin/goal.js +1 -1
- package/dist/builtin/workflows/builtin/index.js +2 -2
- package/dist/builtin/workflows/builtin/ralph.js +1 -1
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/src/extension/index.bundle.mjs +922 -473
- package/dist/builtin/workflows/src/extension/local-worker-entry.mjs +1006 -484
- package/dist/builtin/workflows/src/index.js +892 -448
- package/dist/builtin/workflows/src/local-worker-entry.mjs +1006 -484
- package/dist/builtin/workflows/src/shared/workflow-infrastructure-error.d.ts +6 -0
- package/dist/core/agent-session-retry.d.ts.map +1 -1
- package/dist/core/agent-session-retry.js +12 -3
- package/dist/core/agent-session-retry.js.map +1 -1
- package/dist/core/model-fallback-failures.d.ts +14 -4
- package/dist/core/model-fallback-failures.d.ts.map +1 -1
- package/dist/core/model-fallback-failures.js +43 -15
- package/dist/core/model-fallback-failures.js.map +1 -1
- package/dist/core/settings-defaults.d.ts +1 -0
- package/dist/core/settings-defaults.d.ts.map +1 -1
- package/dist/core/settings-defaults.js +1 -0
- package/dist/core/settings-defaults.js.map +1 -1
- package/dist/core/settings-manager-basic-accessors.d.ts +1 -0
- package/dist/core/settings-manager-basic-accessors.d.ts.map +1 -1
- package/dist/core/settings-manager-basic-accessors.js +2 -0
- package/dist/core/settings-manager-basic-accessors.js.map +1 -1
- package/dist/core/settings-schema.d.ts +2 -0
- package/dist/core/settings-schema.d.ts.map +1 -1
- package/dist/core/settings-schema.js +4 -0
- package/dist/core/settings-schema.js.map +1 -1
- package/dist/core/slash-commands.js +2 -2
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/modes/interactive/interactive-startup-chat-container.d.ts +2 -0
- package/dist/modes/interactive/interactive-startup-chat-container.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-startup-chat-container.js +6 -0
- package/dist/modes/interactive/interactive-startup-chat-container.js.map +1 -1
- package/dist/modes/interactive-engine/remote-renderer.d.ts +27 -5
- package/dist/modes/interactive-engine/remote-renderer.d.ts.map +1 -1
- package/dist/modes/interactive-engine/remote-renderer.js +76 -16
- package/dist/modes/interactive-engine/remote-renderer.js.map +1 -1
- package/docs/intercom/reference.md +1 -1
- package/docs/models/evals.md +30 -2
- package/docs/settings.md +3 -0
- package/docs/subagents/reference.md +3 -1
- package/docs/workflows/api-reference.md +9 -6
- package/docs/workflows/builtins.md +1 -1
- package/docs/workflows/operations.md +6 -4
- package/npm-shrinkwrap.json +32 -32
- package/package.json +3 -3
- package/schemas/settings.schema.json +5 -0
|
@@ -5135,6 +5135,18 @@ function failedOnInfrastructureFault(result) {
|
|
|
5135
5135
|
return true;
|
|
5136
5136
|
return Array.isArray(result.stages) && result.stages.some((stage) => stage?.status === "failed" && (names(stage.failureCode) || names(stage.error)));
|
|
5137
5137
|
}
|
|
5138
|
+
function infrastructureFaultMessage(result) {
|
|
5139
|
+
const names = (value2) => typeof value2 === "string" && (WORKFLOW_INFRASTRUCTURE_FAULTS.has(value2) || workflowInfrastructureFault(value2) !== undefined);
|
|
5140
|
+
if (names(result.error))
|
|
5141
|
+
return result.error;
|
|
5142
|
+
const stage = Array.isArray(result.stages) ? result.stages.find((candidate) => candidate?.status === "failed" && (names(candidate.failureCode) || names(candidate.error))) : undefined;
|
|
5143
|
+
if (typeof stage?.error === "string")
|
|
5144
|
+
return stage.error;
|
|
5145
|
+
if (typeof stage?.failureCode === "string")
|
|
5146
|
+
return stage.failureCode;
|
|
5147
|
+
const code = result.failure?.code;
|
|
5148
|
+
return typeof code === "string" ? code : typeof result.error === "string" ? result.error : "pool fault";
|
|
5149
|
+
}
|
|
5138
5150
|
var WORKFLOW_INFRASTRUCTURE_ERROR_NAME = "WorkflowInfrastructureError", WORKFLOW_INFRASTRUCTURE_FAULTS, MAX_CAUSE_DEPTH = 8, FAULT_PATTERNS, WorkflowInfrastructureError;
|
|
5139
5151
|
var init_workflow_infrastructure_error = __esm(() => {
|
|
5140
5152
|
WORKFLOW_INFRASTRUCTURE_FAULTS = new Set([
|
|
@@ -13450,9 +13462,104 @@ var init_workflow_artifacts = __esm(() => {
|
|
|
13450
13462
|
pendingArtifactPrunes = new Map;
|
|
13451
13463
|
});
|
|
13452
13464
|
|
|
13453
|
-
// dist/builtin/workflows/src/runs/shared/worktree-
|
|
13465
|
+
// dist/builtin/workflows/src/runs/shared/worktree-allocation.ts
|
|
13466
|
+
import { randomBytes } from "node:crypto";
|
|
13454
13467
|
import * as fs from "node:fs";
|
|
13455
13468
|
import * as path from "node:path";
|
|
13469
|
+
function nextBucketId() {
|
|
13470
|
+
return nextBucketIdOverride?.() ?? randomBytes(2).toString("hex");
|
|
13471
|
+
}
|
|
13472
|
+
function errorCode(error) {
|
|
13473
|
+
return error && typeof error === "object" && "code" in error ? String(error.code) : undefined;
|
|
13474
|
+
}
|
|
13475
|
+
function allocateWorktreeCheckoutPath(root, repositoryName) {
|
|
13476
|
+
for (let attempt = 0;attempt < MAX_BUCKET_ATTEMPTS; attempt++) {
|
|
13477
|
+
const bucket = path.join(root, nextBucketId());
|
|
13478
|
+
try {
|
|
13479
|
+
fs.mkdirSync(bucket);
|
|
13480
|
+
return path.join(bucket, repositoryName);
|
|
13481
|
+
} catch (error) {
|
|
13482
|
+
if (errorCode(error) === "EEXIST")
|
|
13483
|
+
continue;
|
|
13484
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
13485
|
+
throw new Error(`Failed to create worktree bucket ${bucket} under gitWorktreeDir ${root}: ${message}`);
|
|
13486
|
+
}
|
|
13487
|
+
}
|
|
13488
|
+
throw new Error(`Every worktree bucket identifier under gitWorktreeDir ${root} is already in use.`);
|
|
13489
|
+
}
|
|
13490
|
+
function removeEmptyWorktreeBucket(checkout) {
|
|
13491
|
+
try {
|
|
13492
|
+
fs.rmdirSync(path.dirname(checkout));
|
|
13493
|
+
} catch {}
|
|
13494
|
+
}
|
|
13495
|
+
function linkedGitDirectory(checkout) {
|
|
13496
|
+
let pointer;
|
|
13497
|
+
try {
|
|
13498
|
+
if (!fs.lstatSync(path.join(checkout, ".git")).isFile())
|
|
13499
|
+
return;
|
|
13500
|
+
pointer = fs.readFileSync(path.join(checkout, ".git"), "utf8");
|
|
13501
|
+
} catch {
|
|
13502
|
+
return;
|
|
13503
|
+
}
|
|
13504
|
+
if (!pointer.startsWith("gitdir:"))
|
|
13505
|
+
return;
|
|
13506
|
+
const gitDir = pointer.slice("gitdir:".length).trim();
|
|
13507
|
+
return gitDir ? path.resolve(checkout, gitDir) : undefined;
|
|
13508
|
+
}
|
|
13509
|
+
function readWorktreeOwner(gitDir) {
|
|
13510
|
+
let contents;
|
|
13511
|
+
try {
|
|
13512
|
+
contents = fs.readFileSync(path.join(gitDir, WORKTREE_OWNER_FILENAME), "utf8");
|
|
13513
|
+
} catch {
|
|
13514
|
+
return;
|
|
13515
|
+
}
|
|
13516
|
+
try {
|
|
13517
|
+
const record = JSON.parse(contents);
|
|
13518
|
+
return record.version === WORKTREE_OWNER_VERSION && typeof record.ownerRunId === "string" && record.ownerRunId ? record.ownerRunId : undefined;
|
|
13519
|
+
} catch {
|
|
13520
|
+
return;
|
|
13521
|
+
}
|
|
13522
|
+
}
|
|
13523
|
+
function findOwnedWorktreeCheckouts(root, repositoryName, ownerRunId) {
|
|
13524
|
+
let entries;
|
|
13525
|
+
try {
|
|
13526
|
+
entries = fs.readdirSync(root, { withFileTypes: true });
|
|
13527
|
+
} catch {
|
|
13528
|
+
return [];
|
|
13529
|
+
}
|
|
13530
|
+
const owned = [];
|
|
13531
|
+
for (const entry of entries) {
|
|
13532
|
+
if (!entry.isDirectory() || !BUCKET_PATTERN.test(entry.name))
|
|
13533
|
+
continue;
|
|
13534
|
+
const checkout = path.join(root, entry.name, repositoryName);
|
|
13535
|
+
try {
|
|
13536
|
+
if (!fs.lstatSync(checkout).isDirectory())
|
|
13537
|
+
continue;
|
|
13538
|
+
} catch {
|
|
13539
|
+
continue;
|
|
13540
|
+
}
|
|
13541
|
+
const gitDir = linkedGitDirectory(checkout);
|
|
13542
|
+
if (gitDir !== undefined && readWorktreeOwner(gitDir) === ownerRunId)
|
|
13543
|
+
owned.push(checkout);
|
|
13544
|
+
}
|
|
13545
|
+
return owned.sort();
|
|
13546
|
+
}
|
|
13547
|
+
function bindWorktreeOwner(gitDir, ownerRunId) {
|
|
13548
|
+
const record = { version: WORKTREE_OWNER_VERSION, ownerRunId };
|
|
13549
|
+
fs.writeFileSync(path.join(gitDir, WORKTREE_OWNER_FILENAME), `${JSON.stringify(record)}
|
|
13550
|
+
`, {
|
|
13551
|
+
encoding: "utf8",
|
|
13552
|
+
flag: "wx"
|
|
13553
|
+
});
|
|
13554
|
+
}
|
|
13555
|
+
var WORKTREE_OWNER_FILENAME = "atomic-workflow-run.json", WORKTREE_OWNER_VERSION = 1, BUCKET_PATTERN, MAX_BUCKET_ATTEMPTS = 65536, nextBucketIdOverride;
|
|
13556
|
+
var init_worktree_allocation = __esm(() => {
|
|
13557
|
+
BUCKET_PATTERN = /^[0-9a-f]{4}$/;
|
|
13558
|
+
});
|
|
13559
|
+
|
|
13560
|
+
// dist/builtin/workflows/src/runs/shared/worktree-generation.ts
|
|
13561
|
+
import * as fs2 from "node:fs";
|
|
13562
|
+
import * as path2 from "node:path";
|
|
13456
13563
|
function sameFileIdentity(left, right) {
|
|
13457
13564
|
return left.dev === right.dev && left.ino === right.ino;
|
|
13458
13565
|
}
|
|
@@ -13462,7 +13569,7 @@ function openMainCheckoutAnchor(gitDir) {
|
|
|
13462
13569
|
assertCurrent() {
|
|
13463
13570
|
if (disposed)
|
|
13464
13571
|
throw new Error(`cached .git identity anchor is already disposed: ${gitDir}`);
|
|
13465
|
-
if (!
|
|
13572
|
+
if (!fs2.lstatSync(gitDir).isDirectory()) {
|
|
13466
13573
|
throw new Error(`cached main-checkout .git directory is no longer current: ${gitDir}`);
|
|
13467
13574
|
}
|
|
13468
13575
|
},
|
|
@@ -13472,17 +13579,17 @@ function openMainCheckoutAnchor(gitDir) {
|
|
|
13472
13579
|
};
|
|
13473
13580
|
}
|
|
13474
13581
|
function openGitWorktreeGenerationAnchor(worktreeRoot) {
|
|
13475
|
-
const gitFile =
|
|
13476
|
-
if (
|
|
13582
|
+
const gitFile = path2.join(worktreeRoot, ".git");
|
|
13583
|
+
if (fs2.lstatSync(gitFile).isDirectory())
|
|
13477
13584
|
return openMainCheckoutAnchor(gitFile);
|
|
13478
|
-
const descriptor =
|
|
13585
|
+
const descriptor = fs2.openSync(gitFile, "r");
|
|
13479
13586
|
let disposed = false;
|
|
13480
13587
|
const anchor = {
|
|
13481
13588
|
assertCurrent() {
|
|
13482
13589
|
if (disposed)
|
|
13483
13590
|
throw new Error(`cached .git identity anchor is already disposed: ${gitFile}`);
|
|
13484
|
-
const held =
|
|
13485
|
-
const current =
|
|
13591
|
+
const held = fs2.fstatSync(descriptor, { bigint: true });
|
|
13592
|
+
const current = fs2.lstatSync(gitFile, { bigint: true });
|
|
13486
13593
|
if (!current.isFile() || !sameFileIdentity(held, current)) {
|
|
13487
13594
|
throw new Error(`cached .git identity anchor no longer matches the selected checkout: ${gitFile}`);
|
|
13488
13595
|
}
|
|
@@ -13491,7 +13598,7 @@ function openGitWorktreeGenerationAnchor(worktreeRoot) {
|
|
|
13491
13598
|
if (disposed)
|
|
13492
13599
|
return;
|
|
13493
13600
|
disposed = true;
|
|
13494
|
-
|
|
13601
|
+
fs2.closeSync(descriptor);
|
|
13495
13602
|
}
|
|
13496
13603
|
};
|
|
13497
13604
|
try {
|
|
@@ -13605,84 +13712,84 @@ var init_worktree_git_runner = __esm(() => {
|
|
|
13605
13712
|
});
|
|
13606
13713
|
|
|
13607
13714
|
// dist/builtin/workflows/src/runs/shared/worktree-root.ts
|
|
13608
|
-
import * as
|
|
13609
|
-
import * as
|
|
13715
|
+
import * as fs3 from "node:fs";
|
|
13716
|
+
import * as path3 from "node:path";
|
|
13610
13717
|
function pathFromPointer(contents, prefix, base) {
|
|
13611
13718
|
if (!contents.startsWith(prefix))
|
|
13612
13719
|
return;
|
|
13613
13720
|
const value2 = contents.slice(prefix.length).trim();
|
|
13614
13721
|
if (!value2)
|
|
13615
13722
|
return;
|
|
13616
|
-
return
|
|
13723
|
+
return path3.resolve(base, value2);
|
|
13617
13724
|
}
|
|
13618
13725
|
function readText(file) {
|
|
13619
13726
|
try {
|
|
13620
|
-
return
|
|
13727
|
+
return fs3.readFileSync(file, "utf8");
|
|
13621
13728
|
} catch {
|
|
13622
13729
|
return;
|
|
13623
13730
|
}
|
|
13624
13731
|
}
|
|
13625
13732
|
function samePath(left, right) {
|
|
13626
13733
|
try {
|
|
13627
|
-
const a =
|
|
13628
|
-
const b =
|
|
13734
|
+
const a = fs3.realpathSync.native(left);
|
|
13735
|
+
const b = fs3.realpathSync.native(right);
|
|
13629
13736
|
return process.platform === "win32" ? a.toLowerCase() === b.toLowerCase() : a === b;
|
|
13630
13737
|
} catch {
|
|
13631
13738
|
return false;
|
|
13632
13739
|
}
|
|
13633
13740
|
}
|
|
13634
13741
|
function findGitRoot(cwd) {
|
|
13635
|
-
let current =
|
|
13742
|
+
let current = path3.resolve(cwd);
|
|
13636
13743
|
try {
|
|
13637
|
-
if (!
|
|
13638
|
-
current =
|
|
13744
|
+
if (!fs3.statSync(current).isDirectory())
|
|
13745
|
+
current = path3.dirname(current);
|
|
13639
13746
|
} catch {
|
|
13640
13747
|
return;
|
|
13641
13748
|
}
|
|
13642
13749
|
while (true) {
|
|
13643
13750
|
try {
|
|
13644
|
-
const entry =
|
|
13751
|
+
const entry = fs3.statSync(path3.join(current, ".git"));
|
|
13645
13752
|
if (entry.isDirectory() || entry.isFile())
|
|
13646
13753
|
return current;
|
|
13647
13754
|
} catch {}
|
|
13648
|
-
const parent =
|
|
13755
|
+
const parent = path3.dirname(current);
|
|
13649
13756
|
if (parent === current)
|
|
13650
13757
|
return;
|
|
13651
13758
|
current = parent;
|
|
13652
13759
|
}
|
|
13653
13760
|
}
|
|
13654
13761
|
function resolveMainRepoRoot(repoRoot) {
|
|
13655
|
-
const gitEntry =
|
|
13762
|
+
const gitEntry = path3.join(repoRoot, ".git");
|
|
13656
13763
|
let stats;
|
|
13657
13764
|
try {
|
|
13658
|
-
stats =
|
|
13765
|
+
stats = fs3.statSync(gitEntry);
|
|
13659
13766
|
} catch {
|
|
13660
13767
|
return;
|
|
13661
13768
|
}
|
|
13662
13769
|
if (stats.isDirectory())
|
|
13663
|
-
return
|
|
13770
|
+
return path3.resolve(repoRoot);
|
|
13664
13771
|
if (!stats.isFile())
|
|
13665
13772
|
return;
|
|
13666
13773
|
const pointer = readText(gitEntry);
|
|
13667
13774
|
if (pointer === undefined)
|
|
13668
13775
|
return;
|
|
13669
13776
|
const gitDir = pathFromPointer(pointer, "gitdir:", repoRoot);
|
|
13670
|
-
if (gitDir === undefined ||
|
|
13777
|
+
if (gitDir === undefined || path3.basename(path3.dirname(gitDir)) !== "worktrees")
|
|
13671
13778
|
return;
|
|
13672
|
-
const commonGitDir =
|
|
13673
|
-
if (
|
|
13779
|
+
const commonGitDir = path3.dirname(path3.dirname(gitDir));
|
|
13780
|
+
if (path3.basename(commonGitDir) !== ".git")
|
|
13674
13781
|
return;
|
|
13675
|
-
const backPointer = readText(
|
|
13676
|
-
const commonPointer = readText(
|
|
13782
|
+
const backPointer = readText(path3.join(gitDir, "gitdir"));
|
|
13783
|
+
const commonPointer = readText(path3.join(gitDir, "commondir"));
|
|
13677
13784
|
if (backPointer === undefined || commonPointer === undefined)
|
|
13678
13785
|
return;
|
|
13679
|
-
const linkedGitEntry =
|
|
13680
|
-
const declaredCommonDir =
|
|
13786
|
+
const linkedGitEntry = path3.resolve(gitDir, backPointer.trim());
|
|
13787
|
+
const declaredCommonDir = path3.resolve(gitDir, commonPointer.trim());
|
|
13681
13788
|
if (!backPointer.trim() || !commonPointer.trim())
|
|
13682
13789
|
return;
|
|
13683
13790
|
if (!samePath(linkedGitEntry, gitEntry) || !samePath(declaredCommonDir, commonGitDir))
|
|
13684
13791
|
return;
|
|
13685
|
-
return
|
|
13792
|
+
return path3.dirname(commonGitDir);
|
|
13686
13793
|
}
|
|
13687
13794
|
function findCanonicalGitRoot(cwd) {
|
|
13688
13795
|
const repoRoot = findGitRoot(cwd);
|
|
@@ -13691,33 +13798,31 @@ function findCanonicalGitRoot(cwd) {
|
|
|
13691
13798
|
var init_worktree_root = () => {};
|
|
13692
13799
|
|
|
13693
13800
|
// dist/builtin/workflows/src/runs/shared/worktree-git.ts
|
|
13694
|
-
import * as
|
|
13695
|
-
import * as
|
|
13696
|
-
function gitWorktreeSetupCacheKey(
|
|
13697
|
-
const repoRoot = repositoryRootForGitWorktree(options.cwd);
|
|
13698
|
-
const { logicalRepoRoot } = cwdWithinGitRepository(options.cwd, repoRoot);
|
|
13699
|
-
const worktreeRoot = resolveGitWorktreePath(options.gitWorktreeDir, logicalRepoRoot);
|
|
13801
|
+
import * as fs4 from "node:fs";
|
|
13802
|
+
import * as path4 from "node:path";
|
|
13803
|
+
function gitWorktreeSetupCacheKey(request) {
|
|
13700
13804
|
return JSON.stringify([
|
|
13701
|
-
|
|
13702
|
-
comparablePathThroughExistingAncestor(
|
|
13805
|
+
comparablePathThroughExistingAncestor(request.options.cwd),
|
|
13806
|
+
comparablePathThroughExistingAncestor(request.target)
|
|
13703
13807
|
]);
|
|
13704
13808
|
}
|
|
13705
|
-
function createGitWorktreeSetupCache() {
|
|
13809
|
+
function createGitWorktreeSetupCache(ownerRunId) {
|
|
13706
13810
|
const results = new Map;
|
|
13707
13811
|
let disposed = false;
|
|
13708
13812
|
return {
|
|
13709
13813
|
get(options) {
|
|
13710
13814
|
if (disposed)
|
|
13711
13815
|
throw new Error("Git worktree setup cache is already disposed");
|
|
13712
|
-
const
|
|
13816
|
+
const request = resolveGitWorktreeSetupRequest(options);
|
|
13817
|
+
const key = gitWorktreeSetupCacheKey(request);
|
|
13713
13818
|
const cached = results.get(key);
|
|
13714
13819
|
if (cached !== undefined) {
|
|
13715
13820
|
assertCachedGitWorktreeIdentity(cached, options);
|
|
13716
13821
|
return cached.result;
|
|
13717
13822
|
}
|
|
13718
|
-
const
|
|
13719
|
-
results.set(key, {
|
|
13720
|
-
return result;
|
|
13823
|
+
const setup = setupGitWorktreeFromRequest(request, ownerRunId);
|
|
13824
|
+
results.set(key, { ...setup, identity: captureGitWorktreeIdentity(setup) });
|
|
13825
|
+
return setup.result;
|
|
13721
13826
|
},
|
|
13722
13827
|
dispose() {
|
|
13723
13828
|
if (disposed)
|
|
@@ -13749,10 +13854,10 @@ function worktreeRecoveryCommand(repositoryRoot, worktreeDir) {
|
|
|
13749
13854
|
return `git -C ${quoteShellArg(repositoryRoot)} worktree remove --force ${quoteShellArg(worktreeDir)}`;
|
|
13750
13855
|
}
|
|
13751
13856
|
function hasGrandparent(value2) {
|
|
13752
|
-
const parent =
|
|
13857
|
+
const parent = path4.dirname(value2);
|
|
13753
13858
|
if (parent === value2)
|
|
13754
13859
|
return false;
|
|
13755
|
-
const grandparent =
|
|
13860
|
+
const grandparent = path4.dirname(parent);
|
|
13756
13861
|
return grandparent !== parent;
|
|
13757
13862
|
}
|
|
13758
13863
|
function pathAncestors(value2) {
|
|
@@ -13760,7 +13865,7 @@ function pathAncestors(value2) {
|
|
|
13760
13865
|
let current = value2;
|
|
13761
13866
|
while (true) {
|
|
13762
13867
|
ancestors.push(current);
|
|
13763
|
-
const parent =
|
|
13868
|
+
const parent = path4.dirname(current);
|
|
13764
13869
|
if (parent === current)
|
|
13765
13870
|
return ancestors;
|
|
13766
13871
|
current = parent;
|
|
@@ -13769,89 +13874,167 @@ function pathAncestors(value2) {
|
|
|
13769
13874
|
function shouldPreserveLogicalPath(logicalPath) {
|
|
13770
13875
|
return pathAncestors(logicalPath).some((ancestor) => {
|
|
13771
13876
|
try {
|
|
13772
|
-
return
|
|
13877
|
+
return fs4.lstatSync(ancestor).isSymbolicLink() && hasGrandparent(ancestor);
|
|
13773
13878
|
} catch {
|
|
13774
13879
|
return false;
|
|
13775
13880
|
}
|
|
13776
13881
|
});
|
|
13777
13882
|
}
|
|
13778
13883
|
function canonicalizePreservingSymlinks(value2) {
|
|
13779
|
-
const logicalPath =
|
|
13884
|
+
const logicalPath = path4.resolve(value2);
|
|
13780
13885
|
const preserveLogicalPath = shouldPreserveLogicalPath(logicalPath);
|
|
13781
13886
|
try {
|
|
13782
|
-
const canonical =
|
|
13887
|
+
const canonical = fs4.realpathSync.native(logicalPath);
|
|
13783
13888
|
return preserveLogicalPath && canonical !== logicalPath ? logicalPath : canonical;
|
|
13784
13889
|
} catch {
|
|
13785
13890
|
return logicalPath;
|
|
13786
13891
|
}
|
|
13787
13892
|
}
|
|
13788
|
-
function resolveGitWorktreePath(value2,
|
|
13893
|
+
function resolveGitWorktreePath(value2, baseDir) {
|
|
13789
13894
|
const trimmed = value2.trim();
|
|
13790
13895
|
if (!trimmed)
|
|
13791
13896
|
throw new Error("gitWorktreeDir cannot be empty");
|
|
13792
13897
|
if (trimmed.includes("\x00")) {
|
|
13793
13898
|
throw new Error("gitWorktreeDir contains an unusable null byte; provide a valid path or omit gitWorktreeDir.");
|
|
13794
13899
|
}
|
|
13795
|
-
return canonicalizePreservingSymlinks(
|
|
13900
|
+
return canonicalizePreservingSymlinks(path4.isAbsolute(trimmed) ? trimmed : path4.resolve(baseDir, trimmed));
|
|
13796
13901
|
}
|
|
13797
13902
|
function comparableRealPath(value2) {
|
|
13798
|
-
const realpath =
|
|
13903
|
+
const realpath = fs4.realpathSync.native(value2).replace(/\\/g, "/");
|
|
13799
13904
|
return process.platform === "win32" ? realpath.toLowerCase() : realpath;
|
|
13800
13905
|
}
|
|
13801
|
-
function
|
|
13802
|
-
let existing =
|
|
13906
|
+
function nearestExistingAncestor(value2) {
|
|
13907
|
+
let existing = path4.resolve(value2);
|
|
13803
13908
|
const missing = [];
|
|
13804
13909
|
while (!pathExistsSync(existing)) {
|
|
13805
|
-
const parent =
|
|
13910
|
+
const parent = path4.dirname(existing);
|
|
13806
13911
|
if (parent === existing)
|
|
13807
13912
|
break;
|
|
13808
|
-
missing.unshift(
|
|
13913
|
+
missing.unshift(path4.basename(existing));
|
|
13809
13914
|
existing = parent;
|
|
13810
13915
|
}
|
|
13811
|
-
|
|
13916
|
+
return { existing, missing };
|
|
13917
|
+
}
|
|
13918
|
+
function comparablePathThroughExistingAncestor(value2) {
|
|
13919
|
+
const { existing, missing } = nearestExistingAncestor(value2);
|
|
13920
|
+
const resolved = path4.resolve(comparableRealPath(existing), ...missing).replace(/\\/g, "/");
|
|
13812
13921
|
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
13813
13922
|
}
|
|
13923
|
+
function isFileOrLink(value2) {
|
|
13924
|
+
try {
|
|
13925
|
+
const entry = fs4.lstatSync(value2);
|
|
13926
|
+
return entry.isFile() || entry.isSymbolicLink();
|
|
13927
|
+
} catch {
|
|
13928
|
+
return false;
|
|
13929
|
+
}
|
|
13930
|
+
}
|
|
13931
|
+
function isDirectory(value2) {
|
|
13932
|
+
try {
|
|
13933
|
+
return fs4.statSync(value2).isDirectory();
|
|
13934
|
+
} catch {
|
|
13935
|
+
return false;
|
|
13936
|
+
}
|
|
13937
|
+
}
|
|
13938
|
+
function looksLikeGitDirectory(directory) {
|
|
13939
|
+
if (!isDirectory(directory))
|
|
13940
|
+
return false;
|
|
13941
|
+
if (path4.basename(directory) === ".git")
|
|
13942
|
+
return true;
|
|
13943
|
+
if (!isFileOrLink(path4.join(directory, "HEAD")))
|
|
13944
|
+
return false;
|
|
13945
|
+
if (isFileOrLink(path4.join(directory, "commondir")))
|
|
13946
|
+
return true;
|
|
13947
|
+
return isDirectory(path4.join(directory, "objects")) && isDirectory(path4.join(directory, "refs"));
|
|
13948
|
+
}
|
|
13949
|
+
function enclosingGitMetadataDirectory(target) {
|
|
13950
|
+
const { existing } = nearestExistingAncestor(target);
|
|
13951
|
+
let realExisting = existing;
|
|
13952
|
+
try {
|
|
13953
|
+
realExisting = fs4.realpathSync.native(existing);
|
|
13954
|
+
} catch {}
|
|
13955
|
+
for (const candidate of new Set([...pathAncestors(existing), ...pathAncestors(realExisting)])) {
|
|
13956
|
+
if (looksLikeGitDirectory(candidate))
|
|
13957
|
+
return candidate;
|
|
13958
|
+
}
|
|
13959
|
+
const insideGitDir = runGitReadOnlyProbe(existing, ["rev-parse", "--is-inside-git-dir"]);
|
|
13960
|
+
if (insideGitDir.status !== 0 && isGitTimeoutResult(insideGitDir)) {
|
|
13961
|
+
throw new Error(`Timed out while validating gitWorktreeDir ${target}. Git reported: ${gitFailureMessage(insideGitDir)}`);
|
|
13962
|
+
}
|
|
13963
|
+
return insideGitDir.status === 0 && insideGitDir.stdout.trim() === "true" ? existing : undefined;
|
|
13964
|
+
}
|
|
13965
|
+
function assertSafeGitWorktreeTarget(target, invokingRoot) {
|
|
13966
|
+
const gitDirectory = enclosingGitMetadataDirectory(target);
|
|
13967
|
+
if (gitDirectory !== undefined) {
|
|
13968
|
+
throw new Error(`gitWorktreeDir must not be inside Git metadata: ${target} is inside the Git directory ${gitDirectory}. Choose a worktree root or a plain directory outside Git metadata.`);
|
|
13969
|
+
}
|
|
13970
|
+
if (invokingRoot !== undefined)
|
|
13971
|
+
validateWorktreeOutsideInvokingCheckout(target, invokingRoot);
|
|
13972
|
+
const { existing, missing } = nearestExistingAncestor(target);
|
|
13973
|
+
const workingTree = gitTopLevel(existing);
|
|
13974
|
+
if (workingTree === undefined)
|
|
13975
|
+
return;
|
|
13976
|
+
if (missing.length === 0 && comparableRealPath(target) === comparableRealPath(workingTree))
|
|
13977
|
+
return;
|
|
13978
|
+
throw new Error(`gitWorktreeDir must not be inside another Git working tree: ${target} is inside ${workingTree}. Pass that checkout's root to reuse it, or a path outside every Git checkout.`);
|
|
13979
|
+
}
|
|
13814
13980
|
function validateWorktreeOutsideInvokingCheckout(worktreeRoot, repoRoot) {
|
|
13815
13981
|
const repository = comparableRealPath(repoRoot);
|
|
13816
13982
|
const candidate = comparablePathThroughExistingAncestor(worktreeRoot);
|
|
13817
|
-
const relativePath =
|
|
13983
|
+
const relativePath = path4.relative(repository, candidate);
|
|
13818
13984
|
if (relativePath === "") {
|
|
13819
|
-
throw new Error("gitWorktreeDir must not resolve to the invoking checkout; provide a separate
|
|
13985
|
+
throw new Error("gitWorktreeDir must not resolve to the invoking checkout; provide a separate worktree path outside it.");
|
|
13820
13986
|
}
|
|
13821
|
-
if (!relativePath.startsWith(`..${
|
|
13822
|
-
throw new Error("gitWorktreeDir must be outside the invoking checkout; provide a separate
|
|
13987
|
+
if (!relativePath.startsWith(`..${path4.sep}`) && relativePath !== ".." && !path4.isAbsolute(relativePath)) {
|
|
13988
|
+
throw new Error("gitWorktreeDir must be outside the invoking checkout; provide a separate worktree path outside it.");
|
|
13823
13989
|
}
|
|
13824
13990
|
}
|
|
13825
13991
|
function gitPathFromOutput(value2, cwd) {
|
|
13826
13992
|
const trimmed = value2.trim();
|
|
13827
13993
|
if (!trimmed)
|
|
13828
13994
|
return;
|
|
13829
|
-
return
|
|
13995
|
+
return path4.isAbsolute(trimmed) ? path4.resolve(trimmed) : path4.resolve(cwd, trimmed);
|
|
13830
13996
|
}
|
|
13831
13997
|
function pathExistsSync(value2) {
|
|
13998
|
+
return pathStatSync(value2) !== undefined;
|
|
13999
|
+
}
|
|
14000
|
+
function pathStatSync(value2) {
|
|
13832
14001
|
try {
|
|
13833
|
-
|
|
13834
|
-
return true;
|
|
14002
|
+
return fs4.statSync(value2);
|
|
13835
14003
|
} catch (error) {
|
|
13836
14004
|
const code = error && typeof error === "object" && "code" in error ? error.code : undefined;
|
|
13837
14005
|
if (code === "ENOENT" || code === "ENOTDIR")
|
|
13838
|
-
return
|
|
14006
|
+
return;
|
|
13839
14007
|
throw error;
|
|
13840
14008
|
}
|
|
13841
14009
|
}
|
|
13842
|
-
function
|
|
14010
|
+
function probeInvokingRepository(cwd) {
|
|
13843
14011
|
const result = runGitReadOnlyProbe(cwd, ["rev-parse", "--show-toplevel"]);
|
|
13844
14012
|
if (result.status !== 0) {
|
|
13845
14013
|
if (isGitTimeoutResult(result)) {
|
|
13846
14014
|
throw new Error(`Timed out while checking the Git repository for gitWorktreeDir from ${cwd}. Git reported: ${gitFailureMessage(result)}`);
|
|
13847
14015
|
}
|
|
13848
|
-
|
|
14016
|
+
return { failure: gitFailureMessage(result) };
|
|
13849
14017
|
}
|
|
13850
|
-
const
|
|
13851
|
-
if (
|
|
14018
|
+
const root = gitPathFromOutput(result.stdout, cwd);
|
|
14019
|
+
if (root === undefined) {
|
|
13852
14020
|
throw new Error(`gitWorktreeDir could not resolve the repository top level from ${cwd}: git returned an empty path.`);
|
|
13853
14021
|
}
|
|
13854
|
-
return
|
|
14022
|
+
return { repository: { root, ...cwdWithinGitRepository(cwd, root) } };
|
|
14023
|
+
}
|
|
14024
|
+
function resolveGitWorktreeSetupRequest(options) {
|
|
14025
|
+
const probe = probeInvokingRepository(options.cwd);
|
|
14026
|
+
if ("repository" in probe) {
|
|
14027
|
+
return {
|
|
14028
|
+
options,
|
|
14029
|
+
repository: probe.repository,
|
|
14030
|
+
target: resolveGitWorktreePath(options.gitWorktreeDir, probe.repository.logicalRoot)
|
|
14031
|
+
};
|
|
14032
|
+
}
|
|
14033
|
+
return {
|
|
14034
|
+
options,
|
|
14035
|
+
repositoryProbeFailure: probe.failure,
|
|
14036
|
+
target: resolveGitWorktreePath(options.gitWorktreeDir, canonicalizePreservingSymlinks(options.cwd))
|
|
14037
|
+
};
|
|
13855
14038
|
}
|
|
13856
14039
|
function gitTopLevelFromResult(result, cwd, description) {
|
|
13857
14040
|
if (result.status !== 0) {
|
|
@@ -13887,25 +14070,25 @@ function dirnameForEachRelativeComponent(base, relativePath) {
|
|
|
13887
14070
|
continue;
|
|
13888
14071
|
if (component === "..")
|
|
13889
14072
|
return;
|
|
13890
|
-
current =
|
|
14073
|
+
current = path4.dirname(current);
|
|
13891
14074
|
}
|
|
13892
14075
|
return current;
|
|
13893
14076
|
}
|
|
13894
14077
|
function cwdWithinGitRepository(cwd, repoRoot) {
|
|
13895
|
-
const sourceCwd =
|
|
13896
|
-
const sourceRepoRoot =
|
|
13897
|
-
const relativeCwd =
|
|
13898
|
-
const safeRelativeCwd = relativeCwd === "" || relativeCwd.startsWith("..") ||
|
|
14078
|
+
const sourceCwd = fs4.realpathSync.native(cwd);
|
|
14079
|
+
const sourceRepoRoot = fs4.realpathSync.native(repoRoot);
|
|
14080
|
+
const relativeCwd = path4.relative(sourceRepoRoot, sourceCwd);
|
|
14081
|
+
const safeRelativeCwd = relativeCwd === "" || relativeCwd.startsWith("..") || path4.isAbsolute(relativeCwd) ? "" : relativeCwd;
|
|
13899
14082
|
const logicalCwd = canonicalizePreservingSymlinks(cwd);
|
|
13900
14083
|
return {
|
|
13901
14084
|
relativeCwd: safeRelativeCwd,
|
|
13902
|
-
|
|
14085
|
+
logicalRoot: dirnameForEachRelativeComponent(logicalCwd, safeRelativeCwd) ?? repoRoot
|
|
13903
14086
|
};
|
|
13904
14087
|
}
|
|
13905
14088
|
function workspaceCwdForGitWorktreeRoot(worktreeRoot, relativeCwd) {
|
|
13906
|
-
return relativeCwd === "" ? worktreeRoot :
|
|
14089
|
+
return relativeCwd === "" ? worktreeRoot : path4.join(worktreeRoot, relativeCwd);
|
|
13907
14090
|
}
|
|
13908
|
-
function
|
|
14091
|
+
function validateGitWorktreeRoot(worktreeRoot) {
|
|
13909
14092
|
const topLevel = gitTopLevel(worktreeRoot);
|
|
13910
14093
|
if (topLevel === undefined) {
|
|
13911
14094
|
throw new Error(`gitWorktreeDir already exists but is not a Git worktree: ${worktreeRoot}`);
|
|
@@ -13913,8 +14096,18 @@ function validateExistingGitWorktreeRoot(worktreeRoot, repoRoot) {
|
|
|
13913
14096
|
if (comparableRealPath(worktreeRoot) !== comparableRealPath(topLevel)) {
|
|
13914
14097
|
throw new Error(`gitWorktreeDir already exists but is not a Git worktree root: ${worktreeRoot}. Git top-level checkout is ${topLevel}`);
|
|
13915
14098
|
}
|
|
13916
|
-
|
|
13917
|
-
|
|
14099
|
+
return gitCommonDirForWorktree(topLevel);
|
|
14100
|
+
}
|
|
14101
|
+
function existingGitWorktreeCommonDir(directory) {
|
|
14102
|
+
return gitTopLevel(directory) === undefined ? undefined : validateGitWorktreeRoot(directory);
|
|
14103
|
+
}
|
|
14104
|
+
function sameGitCommonDir(left, right) {
|
|
14105
|
+
return comparableRealPath(left) === comparableRealPath(right);
|
|
14106
|
+
}
|
|
14107
|
+
function validateCreatedGitWorktree(worktreeRoot, repository) {
|
|
14108
|
+
assertSafeGitWorktreeTarget(worktreeRoot, repository.root);
|
|
14109
|
+
if (!sameGitCommonDir(validateGitWorktreeRoot(worktreeRoot), gitCommonDirForWorktree(repository.root))) {
|
|
14110
|
+
throw new Error(`gitWorktreeDir does not belong to the invoking Git repository: ${worktreeRoot}`);
|
|
13918
14111
|
}
|
|
13919
14112
|
}
|
|
13920
14113
|
function gitDirectoryForWorktree(cwd) {
|
|
@@ -13928,26 +14121,27 @@ function gitDirectoryForWorktree(cwd) {
|
|
|
13928
14121
|
return gitDir;
|
|
13929
14122
|
}
|
|
13930
14123
|
function captureGitWorktreeIdentitySnapshot(setup) {
|
|
13931
|
-
|
|
13932
|
-
|
|
13933
|
-
const
|
|
14124
|
+
const { worktreeRoot, repositoryRoot } = setup.result;
|
|
14125
|
+
assertSafeGitWorktreeTarget(worktreeRoot, setup.invokingRoot);
|
|
14126
|
+
const commonDir = validateGitWorktreeRoot(worktreeRoot);
|
|
14127
|
+
const stats = fs4.statSync(worktreeRoot);
|
|
13934
14128
|
return {
|
|
13935
|
-
worktreeRealPath: comparableRealPath(
|
|
13936
|
-
repositoryRealPath: comparableRealPath(
|
|
13937
|
-
gitDirRealPath: comparableRealPath(gitDirectoryForWorktree(
|
|
13938
|
-
commonDirRealPath: comparableRealPath(
|
|
14129
|
+
worktreeRealPath: comparableRealPath(worktreeRoot),
|
|
14130
|
+
repositoryRealPath: comparableRealPath(repositoryRoot),
|
|
14131
|
+
gitDirRealPath: comparableRealPath(gitDirectoryForWorktree(worktreeRoot)),
|
|
14132
|
+
commonDirRealPath: comparableRealPath(commonDir),
|
|
13939
14133
|
device: stats.dev,
|
|
13940
14134
|
inode: stats.ino
|
|
13941
14135
|
};
|
|
13942
14136
|
}
|
|
13943
14137
|
function captureGitWorktreeIdentity(setup) {
|
|
13944
14138
|
const snapshot = captureGitWorktreeIdentitySnapshot(setup);
|
|
13945
|
-
return { ...snapshot, generation: openGitWorktreeGenerationAnchor(setup.worktreeRoot) };
|
|
14139
|
+
return { ...snapshot, generation: openGitWorktreeGenerationAnchor(setup.result.worktreeRoot) };
|
|
13946
14140
|
}
|
|
13947
14141
|
function assertCachedGitWorktreeIdentity(cached, options) {
|
|
13948
14142
|
let current;
|
|
13949
14143
|
try {
|
|
13950
|
-
current = captureGitWorktreeIdentitySnapshot(cached
|
|
14144
|
+
current = captureGitWorktreeIdentitySnapshot(cached);
|
|
13951
14145
|
cached.identity.generation.assertCurrent();
|
|
13952
14146
|
} catch (error) {
|
|
13953
14147
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -13955,34 +14149,58 @@ function assertCachedGitWorktreeIdentity(cached, options) {
|
|
|
13955
14149
|
}
|
|
13956
14150
|
const expected = cached.identity;
|
|
13957
14151
|
if (current.worktreeRealPath !== expected.worktreeRealPath || current.repositoryRealPath !== expected.repositoryRealPath || current.gitDirRealPath !== expected.gitDirRealPath || current.commonDirRealPath !== expected.commonDirRealPath || current.device !== expected.device || current.inode !== expected.inode) {
|
|
13958
|
-
throw new Error(`Cached gitWorktreeDir changed before reuse: ${options.gitWorktreeDir}. Start a new run with the intended
|
|
13959
|
-
}
|
|
14152
|
+
throw new Error(`Cached gitWorktreeDir changed before reuse: ${options.gitWorktreeDir}. Start a new run with the intended worktree.`);
|
|
14153
|
+
}
|
|
14154
|
+
}
|
|
14155
|
+
function requireInvokingRepository(request, action) {
|
|
14156
|
+
if (request.repository !== undefined)
|
|
14157
|
+
return request.repository;
|
|
14158
|
+
throw new Error([
|
|
14159
|
+
`gitWorktreeDir requires the workflow to be invoked from inside a Git repository to ${action}.`,
|
|
14160
|
+
"Start the workflow from a checkout of the repository the new worktree should come from, or set gitWorktreeDir to the root of an existing Git worktree to reuse it from any directory.",
|
|
14161
|
+
`Git reported: ${request.repositoryProbeFailure ?? "no repository"}`
|
|
14162
|
+
].join(" "));
|
|
14163
|
+
}
|
|
14164
|
+
function setupGitWorktreeFromRequest(request, ownerRunId) {
|
|
14165
|
+
const { repository, target } = request;
|
|
14166
|
+
assertSafeGitWorktreeTarget(target, repository?.root);
|
|
14167
|
+
const entry = pathStatSync(target);
|
|
14168
|
+
if (entry === undefined)
|
|
14169
|
+
return createGitWorktreeAtPath(request, false);
|
|
14170
|
+
if (!entry.isDirectory())
|
|
14171
|
+
throw new Error(`gitWorktreeDir already exists but is not a directory: ${target}`);
|
|
14172
|
+
if (fs4.readdirSync(target).length === 0)
|
|
14173
|
+
return createGitWorktreeAtPath(request, true);
|
|
14174
|
+
const commonDir = existingGitWorktreeCommonDir(target);
|
|
14175
|
+
if (commonDir === undefined)
|
|
14176
|
+
return allocateGitWorktree(request, ownerRunId);
|
|
14177
|
+
const sameRepository = repository !== undefined && sameGitCommonDir(commonDir, gitCommonDirForWorktree(repository.root));
|
|
14178
|
+
return {
|
|
14179
|
+
result: {
|
|
14180
|
+
worktreeRoot: target,
|
|
14181
|
+
cwd: sameRepository ? workspaceCwdForGitWorktreeRoot(target, repository.relativeCwd) : target,
|
|
14182
|
+
repositoryRoot: sameRepository ? repository.root : target,
|
|
14183
|
+
created: false
|
|
14184
|
+
},
|
|
14185
|
+
...repository !== undefined ? { invokingRoot: repository.root } : {}
|
|
14186
|
+
};
|
|
13960
14187
|
}
|
|
13961
|
-
function
|
|
13962
|
-
const
|
|
13963
|
-
const
|
|
14188
|
+
function createGitWorktreeAtPath(request, existingEmptyDirectory) {
|
|
14189
|
+
const { target: worktreeRoot } = request;
|
|
14190
|
+
const repository = requireInvokingRepository(request, `create a new worktree at ${worktreeRoot}`);
|
|
14191
|
+
const mainRoot = findCanonicalGitRoot(repository.root);
|
|
13964
14192
|
if (mainRoot === undefined) {
|
|
13965
|
-
throw new Error(`Unable to resolve the canonical main Git repository root from ${
|
|
13966
|
-
}
|
|
13967
|
-
const { relativeCwd, logicalRepoRoot } = cwdWithinGitRepository(options.cwd, repoRoot);
|
|
13968
|
-
const worktreeRoot = resolveGitWorktreePath(options.gitWorktreeDir, logicalRepoRoot);
|
|
13969
|
-
validateWorktreeOutsideInvokingCheckout(worktreeRoot, repoRoot);
|
|
13970
|
-
if (pathExistsSync(worktreeRoot)) {
|
|
13971
|
-
validateExistingGitWorktreeRoot(worktreeRoot, repoRoot);
|
|
13972
|
-
return {
|
|
13973
|
-
worktreeRoot,
|
|
13974
|
-
cwd: workspaceCwdForGitWorktreeRoot(worktreeRoot, relativeCwd),
|
|
13975
|
-
repositoryRoot: repoRoot,
|
|
13976
|
-
created: false
|
|
13977
|
-
};
|
|
14193
|
+
throw new Error(`Unable to resolve the canonical main Git repository root from ${repository.root}.`);
|
|
13978
14194
|
}
|
|
13979
|
-
|
|
13980
|
-
|
|
13981
|
-
|
|
13982
|
-
|
|
13983
|
-
|
|
14195
|
+
if (!existingEmptyDirectory) {
|
|
14196
|
+
try {
|
|
14197
|
+
fs4.mkdirSync(path4.dirname(worktreeRoot), { recursive: true });
|
|
14198
|
+
} catch (error) {
|
|
14199
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
14200
|
+
throw new Error(`Failed to create parent directory for requested gitWorktreeDir ${worktreeRoot}: ${message}`);
|
|
14201
|
+
}
|
|
13984
14202
|
}
|
|
13985
|
-
const baseRef = options.baseBranch?.trim() || "HEAD";
|
|
14203
|
+
const baseRef = request.options.baseBranch?.trim() || "HEAD";
|
|
13986
14204
|
const result = runGit(mainRoot, ["worktree", "add", "--detach", worktreeRoot, baseRef]);
|
|
13987
14205
|
if (result.status !== 0) {
|
|
13988
14206
|
throw new Error([
|
|
@@ -13992,30 +14210,127 @@ function setupGitWorktree(options) {
|
|
|
13992
14210
|
`));
|
|
13993
14211
|
}
|
|
13994
14212
|
try {
|
|
13995
|
-
|
|
13996
|
-
validateExistingGitWorktreeRoot(worktreeRoot, repoRoot);
|
|
14213
|
+
validateCreatedGitWorktree(worktreeRoot, repository);
|
|
13997
14214
|
} catch (error) {
|
|
13998
14215
|
runGit(mainRoot, ["worktree", "remove", "--force", worktreeRoot]);
|
|
13999
14216
|
const message = error instanceof Error ? error.message : String(error);
|
|
14000
14217
|
throw new Error(`Created gitWorktreeDir failed post-creation validation: ${message}`);
|
|
14001
14218
|
}
|
|
14002
14219
|
return {
|
|
14003
|
-
|
|
14004
|
-
|
|
14005
|
-
|
|
14006
|
-
|
|
14220
|
+
result: {
|
|
14221
|
+
worktreeRoot,
|
|
14222
|
+
cwd: workspaceCwdForGitWorktreeRoot(worktreeRoot, repository.relativeCwd),
|
|
14223
|
+
repositoryRoot: repository.root,
|
|
14224
|
+
created: true
|
|
14225
|
+
},
|
|
14226
|
+
invokingRoot: repository.root
|
|
14007
14227
|
};
|
|
14008
14228
|
}
|
|
14229
|
+
function containedWorktreeCwd(checkout, relativeCwd) {
|
|
14230
|
+
const cwd = workspaceCwdForGitWorktreeRoot(checkout, relativeCwd);
|
|
14231
|
+
let resolved;
|
|
14232
|
+
try {
|
|
14233
|
+
resolved = fs4.realpathSync.native(cwd);
|
|
14234
|
+
} catch {
|
|
14235
|
+
resolved = "";
|
|
14236
|
+
}
|
|
14237
|
+
const relative2 = resolved === "" ? undefined : path4.relative(fs4.realpathSync.native(checkout), resolved);
|
|
14238
|
+
if (relative2 === undefined || relative2 === ".." || relative2.startsWith(`..${path4.sep}`) || path4.isAbsolute(relative2) || !fs4.statSync(resolved).isDirectory()) {
|
|
14239
|
+
throw new Error(`The selected base does not contain a working directory ${relativeCwd || "."} inside the worktree ${checkout}.`);
|
|
14240
|
+
}
|
|
14241
|
+
return cwd;
|
|
14242
|
+
}
|
|
14243
|
+
function allocatedGitWorktreeSetup(checkout, repository, created) {
|
|
14244
|
+
return {
|
|
14245
|
+
result: {
|
|
14246
|
+
worktreeRoot: checkout,
|
|
14247
|
+
cwd: containedWorktreeCwd(checkout, repository.relativeCwd),
|
|
14248
|
+
repositoryRoot: repository.root,
|
|
14249
|
+
created
|
|
14250
|
+
},
|
|
14251
|
+
invokingRoot: repository.root
|
|
14252
|
+
};
|
|
14253
|
+
}
|
|
14254
|
+
function validateOwnedGitWorktree(checkout, repository) {
|
|
14255
|
+
validateCreatedGitWorktree(checkout, repository);
|
|
14256
|
+
const gitDir = gitDirectoryForWorktree(checkout);
|
|
14257
|
+
if (sameGitCommonDir(gitDir, gitCommonDirForWorktree(checkout))) {
|
|
14258
|
+
throw new Error(`${checkout} is not a linked worktree`);
|
|
14259
|
+
}
|
|
14260
|
+
const backlink = fs4.readFileSync(path4.join(gitDir, "gitdir"), "utf8").trim();
|
|
14261
|
+
if (!backlink || comparableRealPath(path4.resolve(gitDir, backlink)) !== comparableRealPath(path4.join(checkout, ".git"))) {
|
|
14262
|
+
throw new Error(`the Git directory of ${checkout} belongs to another checkout`);
|
|
14263
|
+
}
|
|
14264
|
+
}
|
|
14265
|
+
function findOwnedGitWorktree(root, repository, ownerRunId) {
|
|
14266
|
+
const [checkout] = findOwnedWorktreeCheckouts(root, path4.basename(repository.root), ownerRunId);
|
|
14267
|
+
if (checkout === undefined)
|
|
14268
|
+
return;
|
|
14269
|
+
try {
|
|
14270
|
+
validateOwnedGitWorktree(checkout, repository);
|
|
14271
|
+
return allocatedGitWorktreeSetup(checkout, repository, false);
|
|
14272
|
+
} catch (error) {
|
|
14273
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
14274
|
+
throw new Error(`The worktree this workflow run allocated under gitWorktreeDir ${root} can no longer be reused: ${checkout}. ${message}`);
|
|
14275
|
+
}
|
|
14276
|
+
}
|
|
14277
|
+
function allocateGitWorktree(request, ownerRunId) {
|
|
14278
|
+
const { target: root } = request;
|
|
14279
|
+
const repository = requireInvokingRepository(request, `create a new worktree under ${root}`);
|
|
14280
|
+
if (ownerRunId !== undefined) {
|
|
14281
|
+
const owned = findOwnedGitWorktree(root, repository, ownerRunId);
|
|
14282
|
+
if (owned !== undefined)
|
|
14283
|
+
return owned;
|
|
14284
|
+
}
|
|
14285
|
+
const baseRef = request.options.baseBranch?.trim() || "HEAD";
|
|
14286
|
+
const revision = runGit(repository.root, ["rev-parse", "--verify", "--end-of-options", `${baseRef}^{commit}`]);
|
|
14287
|
+
const commit = revision.status === 0 ? revision.stdout.trim() : "";
|
|
14288
|
+
if (commit === "") {
|
|
14289
|
+
throw new Error(`Failed to resolve ${baseRef} to a commit for gitWorktreeDir ${root}. Git reported: ${gitFailureMessage(revision)}`);
|
|
14290
|
+
}
|
|
14291
|
+
const checkout = allocateWorktreeCheckoutPath(root, path4.basename(repository.root));
|
|
14292
|
+
const add = runGit(repository.root, ["worktree", "add", "--detach", "--no-checkout", checkout, commit]);
|
|
14293
|
+
if (add.status !== 0) {
|
|
14294
|
+
removeEmptyWorktreeBucket(checkout);
|
|
14295
|
+
throw new Error(`Failed to create git worktree at ${checkout} under gitWorktreeDir ${root} from ${baseRef}. Git reported: ${gitFailureMessage(add)}`);
|
|
14296
|
+
}
|
|
14297
|
+
try {
|
|
14298
|
+
const gitDir = gitDirectoryForWorktree(checkout);
|
|
14299
|
+
runGitOrThrow(checkout, ["config", "--file", path4.join(gitDir, "config.worktree"), "core.worktree", checkout]);
|
|
14300
|
+
runGitOrThrow(checkout, ["--work-tree=.", "reset", "--hard", "--no-recurse-submodules", commit]);
|
|
14301
|
+
validateCreatedGitWorktree(checkout, repository);
|
|
14302
|
+
const setup = allocatedGitWorktreeSetup(checkout, repository, true);
|
|
14303
|
+
if (ownerRunId !== undefined)
|
|
14304
|
+
bindWorktreeOwner(gitDir, ownerRunId);
|
|
14305
|
+
return setup;
|
|
14306
|
+
} catch (error) {
|
|
14307
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
14308
|
+
const removal = runGit(repository.root, ["worktree", "remove", "--force", checkout]);
|
|
14309
|
+
removeEmptyWorktreeBucket(checkout);
|
|
14310
|
+
const rollback = removal.status === 0 ? "" : ` Rolling back the incomplete worktree also failed; remove it with: ${worktreeRecoveryCommand(repository.root, checkout)}`;
|
|
14311
|
+
throw new Error(`Failed to set up git worktree at ${checkout} under gitWorktreeDir ${root}: ${message}${rollback}`);
|
|
14312
|
+
}
|
|
14313
|
+
}
|
|
14314
|
+
function runGitOrThrow(cwd, args) {
|
|
14315
|
+
const result = runGit(cwd, args);
|
|
14316
|
+
if (result.status !== 0)
|
|
14317
|
+
throw new Error(gitFailureMessage(result));
|
|
14318
|
+
}
|
|
14319
|
+
function setupGitWorktree(options) {
|
|
14320
|
+
return setupGitWorktreeFromRequest(resolveGitWorktreeSetupRequest(options)).result;
|
|
14321
|
+
}
|
|
14009
14322
|
var init_worktree_git = __esm(() => {
|
|
14323
|
+
init_worktree_allocation();
|
|
14010
14324
|
init_worktree_generation();
|
|
14011
14325
|
init_worktree_git_runner();
|
|
14012
14326
|
init_worktree_root();
|
|
14327
|
+
init_worktree_allocation();
|
|
14013
14328
|
init_worktree_git_runner();
|
|
14014
14329
|
});
|
|
14015
14330
|
|
|
14016
14331
|
// dist/builtin/workflows/src/runs/shared/worktree-cache-lifecycle.ts
|
|
14017
|
-
function createGitWorktreeSetupCacheOwner(suppliedCache) {
|
|
14018
|
-
const cache = suppliedCache ?? createGitWorktreeSetupCache();
|
|
14332
|
+
function createGitWorktreeSetupCacheOwner(suppliedCache, ownerRunId) {
|
|
14333
|
+
const cache = suppliedCache ?? createGitWorktreeSetupCache(ownerRunId);
|
|
14019
14334
|
const ownsCache = suppliedCache === undefined;
|
|
14020
14335
|
let released = false;
|
|
14021
14336
|
return {
|
|
@@ -14036,20 +14351,20 @@ var init_worktree_cache_lifecycle = __esm(() => {
|
|
|
14036
14351
|
});
|
|
14037
14352
|
|
|
14038
14353
|
// dist/builtin/workflows/src/runs/shared/worktree-diff.ts
|
|
14039
|
-
import * as
|
|
14040
|
-
import * as
|
|
14354
|
+
import * as fs5 from "node:fs";
|
|
14355
|
+
import * as path5 from "node:path";
|
|
14041
14356
|
function safePatchAgentName(agent) {
|
|
14042
14357
|
return agent.replace(/[^\w.-]/g, "_");
|
|
14043
14358
|
}
|
|
14044
14359
|
function removeSyntheticPath(worktree, syntheticPath) {
|
|
14045
|
-
const resolved =
|
|
14046
|
-
const relative =
|
|
14047
|
-
if (!relative || relative === "." || relative === ".." || relative.startsWith(`..${
|
|
14360
|
+
const resolved = path5.resolve(worktree.path, syntheticPath);
|
|
14361
|
+
const relative = path5.relative(worktree.path, resolved);
|
|
14362
|
+
if (!relative || relative === "." || relative === ".." || relative.startsWith(`..${path5.sep}`) || path5.isAbsolute(relative)) {
|
|
14048
14363
|
return;
|
|
14049
14364
|
}
|
|
14050
14365
|
let stat;
|
|
14051
14366
|
try {
|
|
14052
|
-
stat =
|
|
14367
|
+
stat = fs5.lstatSync(resolved);
|
|
14053
14368
|
} catch (error) {
|
|
14054
14369
|
const code = error && typeof error === "object" && "code" in error ? error.code : undefined;
|
|
14055
14370
|
if (code === "ENOENT")
|
|
@@ -14057,14 +14372,14 @@ function removeSyntheticPath(worktree, syntheticPath) {
|
|
|
14057
14372
|
throw error;
|
|
14058
14373
|
}
|
|
14059
14374
|
if (stat.isSymbolicLink()) {
|
|
14060
|
-
|
|
14375
|
+
fs5.unlinkSync(resolved);
|
|
14061
14376
|
return;
|
|
14062
14377
|
}
|
|
14063
14378
|
if (stat.isDirectory()) {
|
|
14064
|
-
|
|
14379
|
+
fs5.rmSync(resolved, { recursive: true, force: true });
|
|
14065
14380
|
return;
|
|
14066
14381
|
}
|
|
14067
|
-
|
|
14382
|
+
fs5.rmSync(resolved, { force: true });
|
|
14068
14383
|
}
|
|
14069
14384
|
function removeSyntheticPathsBeforeDiff(worktree) {
|
|
14070
14385
|
if (worktree.syntheticPaths.length === 0)
|
|
@@ -14113,7 +14428,7 @@ function captureWorktreeDiff(setup, worktree, agent, patchPath) {
|
|
|
14113
14428
|
const diffStat = runGitChecked(worktree.path, ["diff", "--cached", "--stat", setup.baseCommit]).trim();
|
|
14114
14429
|
const patch = runGitChecked(worktree.path, ["diff", "--cached", setup.baseCommit]);
|
|
14115
14430
|
const numstat = runGitChecked(worktree.path, ["diff", "--cached", "--numstat", setup.baseCommit]);
|
|
14116
|
-
|
|
14431
|
+
fs5.writeFileSync(patchPath, patch, "utf-8");
|
|
14117
14432
|
if (!patch.trim()) {
|
|
14118
14433
|
return emptyDiff(worktree.index, agent, worktree.branch, patchPath);
|
|
14119
14434
|
}
|
|
@@ -14131,7 +14446,7 @@ function captureWorktreeDiff(setup, worktree, agent, patchPath) {
|
|
|
14131
14446
|
}
|
|
14132
14447
|
function writeEmptyPatch(patchPath) {
|
|
14133
14448
|
try {
|
|
14134
|
-
|
|
14449
|
+
fs5.writeFileSync(patchPath, "", "utf-8");
|
|
14135
14450
|
} catch {}
|
|
14136
14451
|
}
|
|
14137
14452
|
function hasWorktreeChanges(diff) {
|
|
@@ -14139,7 +14454,7 @@ function hasWorktreeChanges(diff) {
|
|
|
14139
14454
|
}
|
|
14140
14455
|
function diffWorktrees(setup, agents, diffsDir) {
|
|
14141
14456
|
try {
|
|
14142
|
-
|
|
14457
|
+
fs5.mkdirSync(diffsDir, { recursive: true });
|
|
14143
14458
|
} catch {
|
|
14144
14459
|
return [];
|
|
14145
14460
|
}
|
|
@@ -14147,7 +14462,7 @@ function diffWorktrees(setup, agents, diffsDir) {
|
|
|
14147
14462
|
for (let index = 0;index < setup.worktrees.length; index++) {
|
|
14148
14463
|
const worktree = setup.worktrees[index];
|
|
14149
14464
|
const agent = agents[index] ?? `task-${index + 1}`;
|
|
14150
|
-
const patchPath =
|
|
14465
|
+
const patchPath = path5.join(diffsDir, `task-${index}-${safePatchAgentName(agent)}.patch`);
|
|
14151
14466
|
try {
|
|
14152
14467
|
diffs.push(captureWorktreeDiff(setup, worktree, agent, patchPath));
|
|
14153
14468
|
} catch {
|
|
@@ -14169,7 +14484,7 @@ function formatWorktreeDiffSummary(diffs) {
|
|
|
14169
14484
|
}
|
|
14170
14485
|
lines.push("");
|
|
14171
14486
|
}
|
|
14172
|
-
const patchesDir =
|
|
14487
|
+
const patchesDir = path5.dirname(changed[0].patchPath);
|
|
14173
14488
|
lines.push(`Full patches: ${patchesDir}`);
|
|
14174
14489
|
return lines.join(`
|
|
14175
14490
|
`).trimEnd();
|
|
@@ -14179,8 +14494,8 @@ var init_worktree_diff = __esm(() => {
|
|
|
14179
14494
|
});
|
|
14180
14495
|
|
|
14181
14496
|
// dist/builtin/workflows/src/runs/shared/worktree-post-create.ts
|
|
14182
|
-
import * as
|
|
14183
|
-
import * as
|
|
14497
|
+
import * as fs6 from "node:fs";
|
|
14498
|
+
import * as path6 from "node:path";
|
|
14184
14499
|
function tracked(repo, relativePath) {
|
|
14185
14500
|
const result = runGit(repo, ["ls-files", "--error-unmatch", "--", relativePath]);
|
|
14186
14501
|
return result.status === 0;
|
|
@@ -14200,12 +14515,12 @@ function hasTrackedPathComponent(repo, relativePath) {
|
|
|
14200
14515
|
return false;
|
|
14201
14516
|
}
|
|
14202
14517
|
function copyUntrackedFile(mainRoot, worktreeRoot, relativePath) {
|
|
14203
|
-
const source =
|
|
14204
|
-
const destination =
|
|
14205
|
-
if (!
|
|
14518
|
+
const source = path6.join(mainRoot, relativePath);
|
|
14519
|
+
const destination = path6.join(worktreeRoot, relativePath);
|
|
14520
|
+
if (!fs6.existsSync(source) || fs6.existsSync(destination) || hasTrackedPathComponent(worktreeRoot, relativePath))
|
|
14206
14521
|
return false;
|
|
14207
|
-
|
|
14208
|
-
|
|
14522
|
+
fs6.mkdirSync(path6.dirname(destination), { recursive: true });
|
|
14523
|
+
fs6.copyFileSync(source, destination);
|
|
14209
14524
|
return true;
|
|
14210
14525
|
}
|
|
14211
14526
|
function propagateLocalSettings(mainRoot, worktreeRoot) {
|
|
@@ -14218,14 +14533,14 @@ function propagateLocalSettings(mainRoot, worktreeRoot) {
|
|
|
14218
14533
|
return copied;
|
|
14219
14534
|
}
|
|
14220
14535
|
function populatedHooksDirectory(mainRoot) {
|
|
14221
|
-
const husky =
|
|
14536
|
+
const husky = path6.join(mainRoot, ".husky");
|
|
14222
14537
|
try {
|
|
14223
|
-
if (
|
|
14538
|
+
if (fs6.statSync(husky).isDirectory())
|
|
14224
14539
|
return husky;
|
|
14225
14540
|
} catch {}
|
|
14226
|
-
const hooks =
|
|
14541
|
+
const hooks = path6.join(mainRoot, ".git", "hooks");
|
|
14227
14542
|
try {
|
|
14228
|
-
const populated =
|
|
14543
|
+
const populated = fs6.readdirSync(hooks, { withFileTypes: true }).some((entry) => entry.isFile() && !entry.name.endsWith(".sample"));
|
|
14229
14544
|
return populated ? hooks : undefined;
|
|
14230
14545
|
} catch {
|
|
14231
14546
|
return;
|
|
@@ -14236,7 +14551,7 @@ function configureSharedHooksPath(mainRoot) {
|
|
|
14236
14551
|
if (desired === undefined)
|
|
14237
14552
|
return;
|
|
14238
14553
|
const current = runGitPlain(mainRoot, ["config", "--get", "core.hooksPath"]);
|
|
14239
|
-
if (current.status === 0 &&
|
|
14554
|
+
if (current.status === 0 && path6.resolve(mainRoot, current.stdout.trim()) === desired)
|
|
14240
14555
|
return;
|
|
14241
14556
|
const configured = runGitPlain(mainRoot, ["config", "core.hooksPath", desired]);
|
|
14242
14557
|
if (configured.status !== 0) {
|
|
@@ -14245,10 +14560,10 @@ function configureSharedHooksPath(mainRoot) {
|
|
|
14245
14560
|
}
|
|
14246
14561
|
function normalizeSymlinkDirectory(value2) {
|
|
14247
14562
|
const trimmed = value2.trim();
|
|
14248
|
-
if (!trimmed ||
|
|
14563
|
+
if (!trimmed || path6.isAbsolute(trimmed))
|
|
14249
14564
|
return;
|
|
14250
|
-
const normalized =
|
|
14251
|
-
if (normalized === "." || normalized === ".." || normalized.startsWith(`..${
|
|
14565
|
+
const normalized = path6.normalize(trimmed);
|
|
14566
|
+
if (normalized === "." || normalized === ".." || normalized.startsWith(`..${path6.sep}`))
|
|
14252
14567
|
return;
|
|
14253
14568
|
return normalized;
|
|
14254
14569
|
}
|
|
@@ -14258,20 +14573,20 @@ function symlinkConfiguredDirectories(mainRoot, worktreeRoot, directories) {
|
|
|
14258
14573
|
const relativePath = normalizeSymlinkDirectory(rawDirectory);
|
|
14259
14574
|
if (relativePath === undefined || synthetic.includes(relativePath))
|
|
14260
14575
|
continue;
|
|
14261
|
-
const source =
|
|
14262
|
-
const destination =
|
|
14263
|
-
if (!
|
|
14576
|
+
const source = path6.join(mainRoot, relativePath);
|
|
14577
|
+
const destination = path6.join(worktreeRoot, relativePath);
|
|
14578
|
+
if (!fs6.existsSync(source) || fs6.existsSync(destination) || hasTrackedPathComponent(worktreeRoot, relativePath))
|
|
14264
14579
|
continue;
|
|
14265
|
-
|
|
14580
|
+
fs6.mkdirSync(path6.dirname(destination), { recursive: true });
|
|
14266
14581
|
try {
|
|
14267
|
-
|
|
14582
|
+
fs6.symlinkSync(source, destination, process.platform === "win32" ? "junction" : undefined);
|
|
14268
14583
|
synthetic.push(relativePath);
|
|
14269
14584
|
} catch {}
|
|
14270
14585
|
}
|
|
14271
14586
|
return synthetic;
|
|
14272
14587
|
}
|
|
14273
14588
|
function copyWorktreeIncludes(mainRoot, worktreeRoot) {
|
|
14274
|
-
if (!
|
|
14589
|
+
if (!fs6.existsSync(path6.join(mainRoot, ".worktreeinclude")))
|
|
14275
14590
|
return;
|
|
14276
14591
|
const ignored = runGit(mainRoot, ["ls-files", "--others", "--ignored", "--exclude-standard"]);
|
|
14277
14592
|
const included = runGit(mainRoot, ["ls-files", "--others", "--ignored", "--exclude-from=.worktreeinclude"]);
|
|
@@ -14281,13 +14596,13 @@ function copyWorktreeIncludes(mainRoot, worktreeRoot) {
|
|
|
14281
14596
|
for (const relativePath of included.stdout.split(/\r?\n/).filter(Boolean)) {
|
|
14282
14597
|
if (!ignoredPaths.has(relativePath) || hasTrackedPathComponent(worktreeRoot, relativePath))
|
|
14283
14598
|
continue;
|
|
14284
|
-
const source =
|
|
14285
|
-
const destination =
|
|
14599
|
+
const source = path6.join(mainRoot, relativePath);
|
|
14600
|
+
const destination = path6.join(worktreeRoot, relativePath);
|
|
14286
14601
|
try {
|
|
14287
|
-
if (!
|
|
14602
|
+
if (!fs6.statSync(source).isFile() || fs6.existsSync(destination))
|
|
14288
14603
|
continue;
|
|
14289
|
-
|
|
14290
|
-
|
|
14604
|
+
fs6.mkdirSync(path6.dirname(destination), { recursive: true });
|
|
14605
|
+
fs6.copyFileSync(source, destination);
|
|
14291
14606
|
} catch {}
|
|
14292
14607
|
}
|
|
14293
14608
|
}
|
|
@@ -14304,9 +14619,9 @@ var init_worktree_post_create = __esm(() => {
|
|
|
14304
14619
|
|
|
14305
14620
|
// dist/builtin/workflows/src/runs/shared/worktree-setup.ts
|
|
14306
14621
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
14307
|
-
import * as
|
|
14622
|
+
import * as fs7 from "node:fs";
|
|
14308
14623
|
import * as os from "node:os";
|
|
14309
|
-
import * as
|
|
14624
|
+
import * as path7 from "node:path";
|
|
14310
14625
|
import { createChildProcessEnvironment as createChildProcessEnvironment2 } from "@bastani/atomic";
|
|
14311
14626
|
function originDefaultBranch(mainRoot) {
|
|
14312
14627
|
const symbolic = runGit(mainRoot, ["symbolic-ref", "--short", "refs/remotes/origin/HEAD"]);
|
|
@@ -14326,7 +14641,7 @@ function originDefaultBranch(mainRoot) {
|
|
|
14326
14641
|
}
|
|
14327
14642
|
function resolveRepoState(cwd, explicitBaseBranch) {
|
|
14328
14643
|
const cwdRelative = resolveRepoCwdRelative(cwd);
|
|
14329
|
-
const toplevel =
|
|
14644
|
+
const toplevel = path7.resolve(runGitChecked(cwd, ["rev-parse", "--show-toplevel"]).trim());
|
|
14330
14645
|
const mainRoot = findCanonicalGitRoot(toplevel);
|
|
14331
14646
|
if (mainRoot === undefined)
|
|
14332
14647
|
throw new Error(`unable to resolve canonical main Git repository root from ${toplevel}`);
|
|
@@ -14339,9 +14654,9 @@ function resolveRepoState(cwd, explicitBaseBranch) {
|
|
|
14339
14654
|
return { toplevel, mainRoot, cwdRelative, baseCommit, baseRef };
|
|
14340
14655
|
}
|
|
14341
14656
|
function normalizeComparableCwd(cwd) {
|
|
14342
|
-
const resolved =
|
|
14657
|
+
const resolved = path7.resolve(cwd);
|
|
14343
14658
|
try {
|
|
14344
|
-
return
|
|
14659
|
+
return fs7.realpathSync(resolved);
|
|
14345
14660
|
} catch {
|
|
14346
14661
|
return resolved;
|
|
14347
14662
|
}
|
|
@@ -14352,7 +14667,7 @@ function findWorktreeTaskCwdConflict(tasks, sharedCwd) {
|
|
|
14352
14667
|
const task = tasks[index];
|
|
14353
14668
|
if (!task.cwd)
|
|
14354
14669
|
continue;
|
|
14355
|
-
const taskCwd =
|
|
14670
|
+
const taskCwd = path7.isAbsolute(task.cwd) ? task.cwd : path7.resolve(sharedCwd, task.cwd);
|
|
14356
14671
|
if (normalizeComparableCwd(taskCwd) === normalizedSharedCwd)
|
|
14357
14672
|
continue;
|
|
14358
14673
|
return { index, agent: task.agent, cwd: task.cwd };
|
|
@@ -14369,12 +14684,12 @@ function buildWorktreeBranch(runId, index) {
|
|
|
14369
14684
|
return `worktree-${flattenedWorktreeName(runId, index)}`;
|
|
14370
14685
|
}
|
|
14371
14686
|
function buildWorktreePath(mainRoot, runId, index) {
|
|
14372
|
-
return
|
|
14687
|
+
return path7.join(mainRoot, ".atomic", "worktrees", flattenedWorktreeName(runId, index));
|
|
14373
14688
|
}
|
|
14374
14689
|
function ensureWorktreeIgnore(mainRoot) {
|
|
14375
|
-
const worktreesRoot =
|
|
14376
|
-
|
|
14377
|
-
|
|
14690
|
+
const worktreesRoot = path7.join(mainRoot, ".atomic", "worktrees");
|
|
14691
|
+
fs7.mkdirSync(worktreesRoot, { recursive: true });
|
|
14692
|
+
fs7.writeFileSync(path7.join(worktreesRoot, ".gitignore"), `*
|
|
14378
14693
|
`, "utf8");
|
|
14379
14694
|
}
|
|
14380
14695
|
function resolveRepoCwdRelative(cwd) {
|
|
@@ -14383,7 +14698,7 @@ function resolveRepoCwdRelative(cwd) {
|
|
|
14383
14698
|
throw new Error("worktree isolation requires a git repository");
|
|
14384
14699
|
}
|
|
14385
14700
|
const rawPrefix = runGitChecked(cwd, ["rev-parse", "--show-prefix"]).trim();
|
|
14386
|
-
const normalizedPrefix = rawPrefix ?
|
|
14701
|
+
const normalizedPrefix = rawPrefix ? path7.normalize(rawPrefix.replace(/[\\/]+$/, "")) : "";
|
|
14387
14702
|
return normalizedPrefix === "." ? "" : normalizedPrefix;
|
|
14388
14703
|
}
|
|
14389
14704
|
function parseHookTimeout(timeoutMs) {
|
|
@@ -14401,19 +14716,19 @@ function resolveWorktreeSetupHook(repoRoot, config) {
|
|
|
14401
14716
|
if (!hookPath) {
|
|
14402
14717
|
throw new Error("worktree setup hook path cannot be empty");
|
|
14403
14718
|
}
|
|
14404
|
-
const expandedHookPath = hookPath.startsWith("~/") ?
|
|
14719
|
+
const expandedHookPath = hookPath.startsWith("~/") ? path7.join(os.homedir(), hookPath.slice(2)) : hookPath;
|
|
14405
14720
|
let resolvedPath;
|
|
14406
|
-
if (
|
|
14721
|
+
if (path7.isAbsolute(expandedHookPath)) {
|
|
14407
14722
|
resolvedPath = expandedHookPath;
|
|
14408
14723
|
} else if (expandedHookPath.includes("/") || expandedHookPath.includes("\\")) {
|
|
14409
|
-
resolvedPath =
|
|
14724
|
+
resolvedPath = path7.resolve(repoRoot, expandedHookPath);
|
|
14410
14725
|
} else {
|
|
14411
14726
|
throw new Error("worktree setup hook must be an absolute path or a repo-relative path");
|
|
14412
14727
|
}
|
|
14413
|
-
if (!
|
|
14728
|
+
if (!fs7.existsSync(resolvedPath)) {
|
|
14414
14729
|
throw new Error(`worktree setup hook not found: ${resolvedPath}`);
|
|
14415
14730
|
}
|
|
14416
|
-
if (
|
|
14731
|
+
if (fs7.statSync(resolvedPath).isDirectory()) {
|
|
14417
14732
|
throw new Error(`worktree setup hook must be a file, got directory: ${resolvedPath}`);
|
|
14418
14733
|
}
|
|
14419
14734
|
return {
|
|
@@ -14425,17 +14740,17 @@ function normalizeSyntheticPath(worktreePath, rawPath) {
|
|
|
14425
14740
|
const trimmed = rawPath.trim();
|
|
14426
14741
|
if (!trimmed)
|
|
14427
14742
|
throw new Error("synthetic path cannot be empty");
|
|
14428
|
-
if (
|
|
14743
|
+
if (path7.isAbsolute(trimmed))
|
|
14429
14744
|
throw new Error(`synthetic path must be relative: ${rawPath}`);
|
|
14430
|
-
const resolved =
|
|
14431
|
-
const relative =
|
|
14745
|
+
const resolved = path7.resolve(worktreePath, trimmed);
|
|
14746
|
+
const relative = path7.relative(worktreePath, resolved);
|
|
14432
14747
|
if (!relative || relative === ".") {
|
|
14433
14748
|
throw new Error(`synthetic path cannot target the worktree root: ${rawPath}`);
|
|
14434
14749
|
}
|
|
14435
|
-
if (relative === ".." || relative.startsWith(`..${
|
|
14750
|
+
if (relative === ".." || relative.startsWith(`..${path7.sep}`) || path7.isAbsolute(relative)) {
|
|
14436
14751
|
throw new Error(`synthetic path escapes the worktree root: ${rawPath}`);
|
|
14437
14752
|
}
|
|
14438
|
-
return
|
|
14753
|
+
return path7.normalize(relative);
|
|
14439
14754
|
}
|
|
14440
14755
|
function hasTrackedEntries(worktreePath, relativePath) {
|
|
14441
14756
|
const result = runGit(worktreePath, ["ls-files", "--", relativePath]);
|
|
@@ -14507,16 +14822,16 @@ function waitForGitLockRelease() {
|
|
|
14507
14822
|
}
|
|
14508
14823
|
function unlinkWorktreeLinks(worktreePath, relativePaths) {
|
|
14509
14824
|
for (const relativePath of new Set(relativePaths)) {
|
|
14510
|
-
const target =
|
|
14511
|
-
const relative =
|
|
14512
|
-
if (!relative || relative === ".." || relative.startsWith(`..${
|
|
14825
|
+
const target = path7.resolve(worktreePath, relativePath);
|
|
14826
|
+
const relative = path7.relative(worktreePath, target);
|
|
14827
|
+
if (!relative || relative === ".." || relative.startsWith(`..${path7.sep}`) || path7.isAbsolute(relative))
|
|
14513
14828
|
continue;
|
|
14514
14829
|
let current = worktreePath;
|
|
14515
|
-
for (const component of relative.split(
|
|
14516
|
-
current =
|
|
14830
|
+
for (const component of relative.split(path7.sep)) {
|
|
14831
|
+
current = path7.join(current, component);
|
|
14517
14832
|
let stat;
|
|
14518
14833
|
try {
|
|
14519
|
-
stat =
|
|
14834
|
+
stat = fs7.lstatSync(current);
|
|
14520
14835
|
} catch (error) {
|
|
14521
14836
|
const code = error.code;
|
|
14522
14837
|
if (code === "ENOENT" || code === "ENOTDIR")
|
|
@@ -14526,7 +14841,7 @@ function unlinkWorktreeLinks(worktreePath, relativePaths) {
|
|
|
14526
14841
|
if (!stat.isSymbolicLink())
|
|
14527
14842
|
continue;
|
|
14528
14843
|
if (current === target)
|
|
14529
|
-
|
|
14844
|
+
fs7.unlinkSync(current);
|
|
14530
14845
|
break;
|
|
14531
14846
|
}
|
|
14532
14847
|
}
|
|
@@ -14554,7 +14869,7 @@ function createSingleWorktree(mainRoot, cwdRelative, runId, index, baseRef, base
|
|
|
14554
14869
|
cleanupSingleWorktree(mainRoot, { path: worktreePath, branch }, []);
|
|
14555
14870
|
throw new Error(message);
|
|
14556
14871
|
}
|
|
14557
|
-
const agentCwd = cwdRelative ?
|
|
14872
|
+
const agentCwd = cwdRelative ? path7.join(worktreePath, cwdRelative) : worktreePath;
|
|
14558
14873
|
const syntheticPaths = [];
|
|
14559
14874
|
try {
|
|
14560
14875
|
syntheticPaths.push(...performPostCreationSetup(mainRoot, worktreePath, symlinkDirectories));
|
|
@@ -14623,8 +14938,8 @@ var init_worktree = __esm(() => {
|
|
|
14623
14938
|
});
|
|
14624
14939
|
|
|
14625
14940
|
// dist/builtin/workflows/src/runs/shared/worktree-cwd.ts
|
|
14626
|
-
import { lstatSync as
|
|
14627
|
-
import { dirname as
|
|
14941
|
+
import { lstatSync as lstatSync6, realpathSync as realpathSync4 } from "node:fs";
|
|
14942
|
+
import { dirname as dirname9, isAbsolute as isAbsolute6, relative as relative4, resolve as resolve7, sep as sep5 } from "node:path";
|
|
14628
14943
|
function relativePathWithin(root, candidate) {
|
|
14629
14944
|
const path = relative4(root, candidate);
|
|
14630
14945
|
return path === ".." || path.startsWith(`..${sep5}`) || isAbsolute6(path) ? undefined : path;
|
|
@@ -14643,14 +14958,14 @@ function resolveWorktreeStageCwd(cwd, setup) {
|
|
|
14643
14958
|
if (cwd === undefined || cwd.length === 0)
|
|
14644
14959
|
return;
|
|
14645
14960
|
const absoluteInput = isAbsolute6(cwd);
|
|
14646
|
-
const candidate = absoluteInput ? cwd :
|
|
14961
|
+
const candidate = absoluteInput ? cwd : resolve7(setup.cwd, cwd);
|
|
14647
14962
|
if (!absoluteInput && relativePathWithin(setup.worktreeRoot, candidate) === undefined) {
|
|
14648
14963
|
throw new Error(`atomic-workflows: relative cwd ${cwd} escapes gitWorktreeDir ${setup.worktreeRoot}; choose a cwd inside the worktree.`);
|
|
14649
14964
|
}
|
|
14650
14965
|
const candidateRealPath = existingRealPath(candidate, "cwd");
|
|
14651
14966
|
const worktreeRelative = relativePathWithin(worktreeRoot, candidateRealPath);
|
|
14652
14967
|
if (worktreeRelative !== undefined)
|
|
14653
|
-
return
|
|
14968
|
+
return resolve7(setup.worktreeRoot, worktreeRelative);
|
|
14654
14969
|
if (!isAbsolute6(cwd)) {
|
|
14655
14970
|
throw new Error(`atomic-workflows: relative cwd ${cwd} escapes gitWorktreeDir ${setup.worktreeRoot}; choose a path that remains inside the selected worktree.`);
|
|
14656
14971
|
}
|
|
@@ -14659,9 +14974,9 @@ function resolveWorktreeStageCwd(cwd, setup) {
|
|
|
14659
14974
|
}
|
|
14660
14975
|
const repositoryRelative = absoluteInput ? relativePathWithin(repositoryRoot, candidateRealPath) : undefined;
|
|
14661
14976
|
if (repositoryRelative === undefined) {
|
|
14662
|
-
throw new Error(`atomic-workflows: cwd ${cwd} is outside gitWorktreeDir ${setup.worktreeRoot}; use a cwd inside the invoking repository so Atomic can remap it, or omit cwd.`);
|
|
14977
|
+
throw new Error(repositoryRoot === worktreeRoot ? `atomic-workflows: cwd ${cwd} is outside gitWorktreeDir ${setup.worktreeRoot}, which belongs to a different repository than the invoking cwd; choose a cwd inside the worktree, or omit cwd.` : `atomic-workflows: cwd ${cwd} is outside gitWorktreeDir ${setup.worktreeRoot}; use a cwd inside the invoking repository so Atomic can remap it, or omit cwd.`);
|
|
14663
14978
|
}
|
|
14664
|
-
const mappedCwd =
|
|
14979
|
+
const mappedCwd = resolve7(setup.worktreeRoot, repositoryRelative);
|
|
14665
14980
|
const mappedRealPath = existingRealPath(mappedCwd, "remapped worktree cwd");
|
|
14666
14981
|
if (relativePathWithin(worktreeRoot, mappedRealPath) === undefined) {
|
|
14667
14982
|
throw new Error(`atomic-workflows: cwd ${cwd} remaps through a path that resolves outside gitWorktreeDir ${setup.worktreeRoot}; remove the escaping symlink or omit cwd.`);
|
|
@@ -14672,7 +14987,7 @@ var init_worktree_cwd = () => {};
|
|
|
14672
14987
|
|
|
14673
14988
|
// dist/builtin/workflows/src/runs/foreground/executor-task-prompts.ts
|
|
14674
14989
|
import { existsSync as existsSync3 } from "node:fs";
|
|
14675
|
-
import { isAbsolute as isAbsolute7, resolve as
|
|
14990
|
+
import { isAbsolute as isAbsolute7, resolve as resolve8 } from "node:path";
|
|
14676
14991
|
function normalizeTaskContexts(previous) {
|
|
14677
14992
|
if (previous === undefined)
|
|
14678
14993
|
return [];
|
|
@@ -14725,7 +15040,7 @@ function taskPrevious(options) {
|
|
|
14725
15040
|
function resolveWorkflowPath(filePath, baseDir) {
|
|
14726
15041
|
if (isAbsolute7(filePath))
|
|
14727
15042
|
return filePath;
|
|
14728
|
-
return
|
|
15043
|
+
return resolve8(baseDir ?? process.cwd(), filePath);
|
|
14729
15044
|
}
|
|
14730
15045
|
function taskBaseDir(options) {
|
|
14731
15046
|
if (typeof options.chainDir === "string" && options.chainDir.trim().length > 0) {
|
|
@@ -14880,7 +15195,7 @@ function taskWithSharedDefaults(taskOptions, options) {
|
|
|
14880
15195
|
var init_executor_task_prompts = () => {};
|
|
14881
15196
|
|
|
14882
15197
|
// dist/builtin/workflows/src/runs/foreground/executor-direct-helpers.ts
|
|
14883
|
-
import { isAbsolute as isAbsolute8, join as
|
|
15198
|
+
import { isAbsolute as isAbsolute8, join as join12, resolve as resolve9 } from "node:path";
|
|
14884
15199
|
import { CONFIG_DIR_NAME } from "@bastani/atomic";
|
|
14885
15200
|
function positiveConcurrency(value2) {
|
|
14886
15201
|
if (typeof value2 !== "number" || !Number.isFinite(value2) || value2 < 1)
|
|
@@ -15033,10 +15348,10 @@ function workflowInvocationMetadata(inputDefaults, workflowInvocationCwd, cache,
|
|
|
15033
15348
|
function resolvedTaskCwd(cwd, workflowInvocationCwd) {
|
|
15034
15349
|
if (cwd === undefined)
|
|
15035
15350
|
return;
|
|
15036
|
-
return isAbsolute8(cwd) ? cwd :
|
|
15351
|
+
return isAbsolute8(cwd) ? cwd : resolve9(workflowInvocationCwd, cwd);
|
|
15037
15352
|
}
|
|
15038
15353
|
function taskWorktreeOutputsRoot(runId) {
|
|
15039
|
-
return
|
|
15354
|
+
return join12(workflowArtifactRunPath(runId), "task-outputs");
|
|
15040
15355
|
}
|
|
15041
15356
|
function prepareTaskWorktrees(tasks, options, runId, scope, workflowInvocationCwd = process.cwd(), symlinkDirectories) {
|
|
15042
15357
|
if (options.worktree !== true && !tasks.some((task) => task.worktree === true)) {
|
|
@@ -15046,7 +15361,7 @@ function prepareTaskWorktrees(tasks, options, runId, scope, workflowInvocationCw
|
|
|
15046
15361
|
throw new Error("atomic-workflows: worktree and gitWorktreeDir are mutually exclusive; use gitWorktreeDir for a reusable worktree or worktree:true for temporary isolated worktrees.");
|
|
15047
15362
|
}
|
|
15048
15363
|
const explicitCwd = tasks.find((task) => typeof task.cwd === "string")?.cwd;
|
|
15049
|
-
const sharedCwd = explicitCwd === undefined ? workflowInvocationCwd : isAbsolute8(explicitCwd) ? explicitCwd :
|
|
15364
|
+
const sharedCwd = explicitCwd === undefined ? workflowInvocationCwd : isAbsolute8(explicitCwd) ? explicitCwd : resolve9(workflowInvocationCwd, explicitCwd);
|
|
15050
15365
|
const conflict = findWorktreeTaskCwdConflict(tasks.map((task) => ({ agent: task.name, cwd: resolvedTaskCwd(task.cwd, workflowInvocationCwd) })), sharedCwd);
|
|
15051
15366
|
if (conflict !== undefined)
|
|
15052
15367
|
throw new Error(formatWorktreeTaskCwdConflict(conflict, sharedCwd));
|
|
@@ -15061,9 +15376,9 @@ function prepareTaskWorktrees(tasks, options, runId, scope, workflowInvocationCw
|
|
|
15061
15376
|
tasks: tasks.map((task, index) => ({ ...task, cwd: setup.worktrees[index].agentCwd })),
|
|
15062
15377
|
setup,
|
|
15063
15378
|
agents,
|
|
15064
|
-
diffsDir:
|
|
15379
|
+
diffsDir: join12(setup.cwd, CONFIG_DIR_NAME, "workflows", "worktree-diffs", runId, scope),
|
|
15065
15380
|
outputIsolations: tasks.map((_, index) => ({
|
|
15066
|
-
baseDir:
|
|
15381
|
+
baseDir: join12(trustedRoot, runId, scope, String(index)),
|
|
15067
15382
|
trustedRoot
|
|
15068
15383
|
}))
|
|
15069
15384
|
};
|
|
@@ -16790,8 +17105,12 @@ function createStageControlRegistry() {
|
|
|
16790
17105
|
}
|
|
16791
17106
|
await disposeEntries(entries);
|
|
16792
17107
|
},
|
|
16793
|
-
async releaseIdle(runId) {
|
|
16794
|
-
|
|
17108
|
+
async releaseIdle(runId, only) {
|
|
17109
|
+
const runMap = _byRun.get(runId);
|
|
17110
|
+
const entries = only === undefined ? [...runMap ?? []] : [[only, runMap?.get(only)]];
|
|
17111
|
+
for (const [stageId, entry] of entries) {
|
|
17112
|
+
if (entry === undefined)
|
|
17113
|
+
continue;
|
|
16795
17114
|
try {
|
|
16796
17115
|
await releaseIdleEntry(runId, stageId, entry);
|
|
16797
17116
|
} catch (error) {
|
|
@@ -16852,7 +17171,7 @@ var init_possible_stages = __esm(() => {
|
|
|
16852
17171
|
|
|
16853
17172
|
// dist/builtin/workflows/src/workers/admission-host.ts
|
|
16854
17173
|
import { execFile, execFileSync } from "node:child_process";
|
|
16855
|
-
import { readdirSync as
|
|
17174
|
+
import { readdirSync as readdirSync4, readFileSync as readFileSync4 } from "node:fs";
|
|
16856
17175
|
import { readFile } from "node:fs/promises";
|
|
16857
17176
|
import { hostname } from "node:os";
|
|
16858
17177
|
function admissionBootIdentity() {
|
|
@@ -16878,13 +17197,32 @@ function admissionProcessStopped(pid) {
|
|
|
16878
17197
|
return error.code === "ESRCH";
|
|
16879
17198
|
}
|
|
16880
17199
|
}
|
|
17200
|
+
function workerProcessExited(pid, final = false) {
|
|
17201
|
+
if (admissionProcessStopped(pid))
|
|
17202
|
+
return true;
|
|
17203
|
+
if (process.platform === "linux") {
|
|
17204
|
+
try {
|
|
17205
|
+
const stat = readFileSync4(`/proc/${pid}/stat`, "utf8");
|
|
17206
|
+
return stat.slice(stat.lastIndexOf(")") + 2).startsWith("Z");
|
|
17207
|
+
} catch {
|
|
17208
|
+
return admissionProcessStopped(pid);
|
|
17209
|
+
}
|
|
17210
|
+
}
|
|
17211
|
+
if (!final)
|
|
17212
|
+
return false;
|
|
17213
|
+
try {
|
|
17214
|
+
return execFileSync("ps", ["-o", "stat=", "-p", String(pid)], { encoding: "utf8", timeout: 5000 }).trim().startsWith("Z");
|
|
17215
|
+
} catch {
|
|
17216
|
+
return admissionProcessStopped(pid);
|
|
17217
|
+
}
|
|
17218
|
+
}
|
|
16881
17219
|
function scanProcessGroup(group) {
|
|
16882
17220
|
const members = new Map;
|
|
16883
17221
|
if (process.platform === "linux") {
|
|
16884
17222
|
let complete = true;
|
|
16885
17223
|
let entries;
|
|
16886
17224
|
try {
|
|
16887
|
-
entries =
|
|
17225
|
+
entries = readdirSync4("/proc");
|
|
16888
17226
|
} catch {
|
|
16889
17227
|
return { members, complete: false };
|
|
16890
17228
|
}
|
|
@@ -16893,7 +17231,7 @@ function scanProcessGroup(group) {
|
|
|
16893
17231
|
continue;
|
|
16894
17232
|
let stat;
|
|
16895
17233
|
try {
|
|
16896
|
-
stat =
|
|
17234
|
+
stat = readFileSync4(`/proc/${entry}/stat`, "utf8");
|
|
16897
17235
|
} catch {
|
|
16898
17236
|
complete = false;
|
|
16899
17237
|
continue;
|
|
@@ -53632,10 +53970,10 @@ var init_verification_prompts = __esm(() => {
|
|
|
53632
53970
|
// dist/builtin/workflows/builtin/pattern-artifact-root.ts
|
|
53633
53971
|
import { randomUUID as randomUUID8 } from "node:crypto";
|
|
53634
53972
|
import { mkdir as mkdir2 } from "node:fs/promises";
|
|
53635
|
-
import { join as
|
|
53973
|
+
import { join as join13 } from "node:path";
|
|
53636
53974
|
async function stableArtifactRoot(ctx, workflowName) {
|
|
53637
53975
|
const cwd = ctx.cwd ?? process.cwd();
|
|
53638
|
-
const artifactDir = await ctx.tool("artifact-root", { workflow: workflowName }, async () =>
|
|
53976
|
+
const artifactDir = await ctx.tool("artifact-root", { workflow: workflowName }, async () => join13(cwd, ".atomic", "workflows", "runs", `${workflowName}-${randomUUID8()}`));
|
|
53639
53977
|
await mkdir2(artifactDir, { recursive: true });
|
|
53640
53978
|
return artifactDir;
|
|
53641
53979
|
}
|
|
@@ -53675,7 +54013,7 @@ function fold_usage(results) {
|
|
|
53675
54013
|
|
|
53676
54014
|
// dist/builtin/workflows/builtin/adversarial-verification-runner.ts
|
|
53677
54015
|
import { readFile as readFile2, writeFile } from "node:fs/promises";
|
|
53678
|
-
import { join as
|
|
54016
|
+
import { join as join14 } from "node:path";
|
|
53679
54017
|
import { Type as Type4 } from "typebox";
|
|
53680
54018
|
function isRecord3(value2) {
|
|
53681
54019
|
return typeof value2 === "object" && value2 !== null && !Array.isArray(value2);
|
|
@@ -53770,8 +54108,8 @@ async function writeInvalid(path, stage) {
|
|
|
53770
54108
|
}
|
|
53771
54109
|
async function runAdversarialVerification(ctx) {
|
|
53772
54110
|
const root = await stableArtifactRoot(ctx, "adversarial-verification");
|
|
53773
|
-
const criteriaPath =
|
|
53774
|
-
const candidatePath =
|
|
54111
|
+
const criteriaPath = join14(root, "criteria.md");
|
|
54112
|
+
const candidatePath = join14(root, "candidate.md");
|
|
53775
54113
|
const resolvedCriteria = resolveCriteria(ctx.inputs.criteria);
|
|
53776
54114
|
await writeFile(criteriaPath, renderCriteriaMarkdown(resolvedCriteria));
|
|
53777
54115
|
await ctx.task("worker", { prompt: renderWorkerPrompt(ctx.inputs.task), context: "fresh", output: candidatePath, outputMode: "file-only" });
|
|
@@ -53803,7 +54141,7 @@ async function runAdversarialVerification(ctx) {
|
|
|
53803
54141
|
criterion,
|
|
53804
54142
|
verifierIndex: index + 1,
|
|
53805
54143
|
name,
|
|
53806
|
-
artifactPath:
|
|
54144
|
+
artifactPath: join14(root, `verification-${round}-${criterion.id}-${index + 1}.json`)
|
|
53807
54145
|
});
|
|
53808
54146
|
}
|
|
53809
54147
|
}
|
|
@@ -53856,8 +54194,8 @@ async function runAdversarialVerification(ctx) {
|
|
|
53856
54194
|
const decision = decide_verification(roundResult, { acceptMean, quorumFraction: QUORUM_FRACTION });
|
|
53857
54195
|
finalDecision = decision;
|
|
53858
54196
|
finalMean = meanScore(scoreReports);
|
|
53859
|
-
scoreTablePath =
|
|
53860
|
-
reviewReportPath =
|
|
54197
|
+
scoreTablePath = join14(root, `verification-summary-${round}.json`);
|
|
54198
|
+
reviewReportPath = join14(root, `review-${round}.json`);
|
|
53861
54199
|
const summary = {
|
|
53862
54200
|
scores: scoreReports,
|
|
53863
54201
|
mean: finalMean,
|
|
@@ -54038,7 +54376,7 @@ var GROUNDED_REPORTING2 = "Before reporting progress, audit each claim against a
|
|
|
54038
54376
|
|
|
54039
54377
|
// dist/builtin/workflows/builtin/classify-and-act-runner.ts
|
|
54040
54378
|
import { writeFile as writeFile2 } from "node:fs/promises";
|
|
54041
|
-
import { join as
|
|
54379
|
+
import { join as join15 } from "node:path";
|
|
54042
54380
|
import { Type as Type6 } from "typebox";
|
|
54043
54381
|
function safeName(value2) {
|
|
54044
54382
|
const normalized = value2.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
@@ -54078,7 +54416,7 @@ async function runClassifyAndAct(ctx) {
|
|
|
54078
54416
|
const needsFallback = exactCategory === undefined || confidence < ctx.inputs.confidence_threshold;
|
|
54079
54417
|
const fallback = needsFallback ? await resolveFallbackCategory(ctx, exactCategory) : undefined;
|
|
54080
54418
|
const category = fallback?.category ?? exactCategory;
|
|
54081
|
-
const classificationPath =
|
|
54419
|
+
const classificationPath = join15(artifactDir, "classification.json");
|
|
54082
54420
|
await writeFile2(classificationPath, JSON.stringify({
|
|
54083
54421
|
proposed_category: proposedCategory,
|
|
54084
54422
|
selected_category: category,
|
|
@@ -54088,7 +54426,7 @@ async function runClassifyAndAct(ctx) {
|
|
|
54088
54426
|
fallback_used: needsFallback,
|
|
54089
54427
|
fallback_mode: fallback?.fallbackMode ?? "none"
|
|
54090
54428
|
}, null, 2));
|
|
54091
|
-
const actionPath =
|
|
54429
|
+
const actionPath = join15(artifactDir, `action-${safeName(category)}.md`);
|
|
54092
54430
|
const action = await ctx.task(`action-${safeName(category)}`, {
|
|
54093
54431
|
tools: actionTools(category),
|
|
54094
54432
|
prompt: actionPrompt({ prompt: ctx.inputs.prompt, category, classificationPath }),
|
|
@@ -54228,7 +54566,7 @@ var GROUNDED_REPORTING3 = "Before reporting progress, audit each claim against a
|
|
|
54228
54566
|
|
|
54229
54567
|
// dist/builtin/workflows/builtin/fan-out-and-synthesize-runner.ts
|
|
54230
54568
|
import { writeFile as writeFile3 } from "node:fs/promises";
|
|
54231
|
-
import { join as
|
|
54569
|
+
import { join as join16 } from "node:path";
|
|
54232
54570
|
import { Type as Type8 } from "typebox";
|
|
54233
54571
|
function safeName2(value2, index) {
|
|
54234
54572
|
const normalized = value2.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
@@ -54259,9 +54597,9 @@ async function runFanOutAndSynthesize(ctx) {
|
|
|
54259
54597
|
context: "fresh"
|
|
54260
54598
|
});
|
|
54261
54599
|
const partitions = parsedPartitions(plan.structured, ctx.inputs.max_branches, ctx.inputs.prompt);
|
|
54262
|
-
const partitionPath =
|
|
54600
|
+
const partitionPath = join16(artifactDir, "partition-plan.json");
|
|
54263
54601
|
await writeFile3(partitionPath, JSON.stringify({ task: ctx.inputs.prompt, partitions }, null, 2));
|
|
54264
|
-
const branchPaths = partitions.map((partition, index) =>
|
|
54602
|
+
const branchPaths = partitions.map((partition, index) => join16(artifactDir, `branch-${safeName2(partition.label, index)}.md`));
|
|
54265
54603
|
const steps = partitions.map((partition, index) => ({
|
|
54266
54604
|
name: `branch-${safeName2(partition.label, index)}`,
|
|
54267
54605
|
prompt: branchPrompt({ prompt: ctx.inputs.prompt, ...partition }),
|
|
@@ -54271,13 +54609,13 @@ async function runFanOutAndSynthesize(ctx) {
|
|
|
54271
54609
|
outputMode: "file-only"
|
|
54272
54610
|
}));
|
|
54273
54611
|
await ctx.parallel(steps, { concurrency: Math.min(ctx.inputs.max_concurrency, partitions.length), failFast: false });
|
|
54274
|
-
const manifestPath =
|
|
54612
|
+
const manifestPath = join16(artifactDir, "manifest.json");
|
|
54275
54613
|
await writeFile3(manifestPath, JSON.stringify({
|
|
54276
54614
|
task: ctx.inputs.prompt,
|
|
54277
54615
|
partition_plan: partitionPath,
|
|
54278
54616
|
branches: partitions.map((partition, index) => ({ ...partition, artifact_path: branchPaths[index] }))
|
|
54279
54617
|
}, null, 2));
|
|
54280
|
-
const synthesisPath =
|
|
54618
|
+
const synthesisPath = join16(artifactDir, "synthesis.md");
|
|
54281
54619
|
const synthesis = await ctx.task("synthesize", {
|
|
54282
54620
|
prompt: synthesisPrompt(ctx.inputs.prompt, manifestPath),
|
|
54283
54621
|
context: "fresh",
|
|
@@ -54442,7 +54780,7 @@ var init_generate_and_filter_prompts = __esm(() => {
|
|
|
54442
54780
|
|
|
54443
54781
|
// dist/builtin/workflows/builtin/generate-and-filter-runner.ts
|
|
54444
54782
|
import { readFile as readFile3, writeFile as writeFile4 } from "node:fs/promises";
|
|
54445
|
-
import { join as
|
|
54783
|
+
import { join as join17 } from "node:path";
|
|
54446
54784
|
import { Type as Type10 } from "typebox";
|
|
54447
54785
|
function isRecord4(value2) {
|
|
54448
54786
|
return typeof value2 === "object" && value2 !== null && !Array.isArray(value2);
|
|
@@ -54461,7 +54799,7 @@ function judgeDecision(value2) {
|
|
|
54461
54799
|
}
|
|
54462
54800
|
async function runGenerateAndFilter(ctx) {
|
|
54463
54801
|
const root = await stableArtifactRoot(ctx, "generate-and-filter");
|
|
54464
|
-
const candidatePaths = Array.from({ length: ctx.inputs.num_candidates }, (_, index) =>
|
|
54802
|
+
const candidatePaths = Array.from({ length: ctx.inputs.num_candidates }, (_, index) => join17(root, `candidate-${index + 1}.md`));
|
|
54465
54803
|
const shortlistLimit = Math.min(ctx.inputs.shortlist_size, ctx.inputs.num_candidates);
|
|
54466
54804
|
await ctx.parallel(candidatePaths.map((path, index) => ({
|
|
54467
54805
|
name: `generate-${index + 1}`,
|
|
@@ -54470,9 +54808,9 @@ async function runGenerateAndFilter(ctx) {
|
|
|
54470
54808
|
output: path,
|
|
54471
54809
|
outputMode: "file-only"
|
|
54472
54810
|
})), { concurrency: ctx.inputs.max_concurrency, failFast: false });
|
|
54473
|
-
const manifestPath =
|
|
54811
|
+
const manifestPath = join17(root, "manifest.json");
|
|
54474
54812
|
await writeFile4(manifestPath, JSON.stringify({ task: ctx.inputs.prompt, candidate_artifact_paths: candidatePaths }, null, 2));
|
|
54475
|
-
const filterPath =
|
|
54813
|
+
const filterPath = join17(root, "filter.json");
|
|
54476
54814
|
const filtered = await ctx.task("dedupe-and-filter", {
|
|
54477
54815
|
prompt: renderFilterPrompt(ctx.inputs.prompt, candidatePaths, shortlistLimit),
|
|
54478
54816
|
context: "fresh",
|
|
@@ -54489,7 +54827,7 @@ async function runGenerateAndFilter(ctx) {
|
|
|
54489
54827
|
let judgePath = null;
|
|
54490
54828
|
let decisionPath = filterPath;
|
|
54491
54829
|
if (ctx.inputs.use_judge) {
|
|
54492
|
-
judgePath =
|
|
54830
|
+
judgePath = join17(root, "judge.json");
|
|
54493
54831
|
const judgeCandidates = await Promise.all(shortlist.map(async (path) => ({ path, body: await readFile3(path, "utf8") })));
|
|
54494
54832
|
const judgePrompt = renderJudgePrompt(ctx.inputs.prompt, filterPath, shortlistLimit, judgeCandidates);
|
|
54495
54833
|
const judged = await ctx.task("judge", {
|
|
@@ -54505,7 +54843,7 @@ async function runGenerateAndFilter(ctx) {
|
|
|
54505
54843
|
shortlist = judgedShortlist.length > 0 ? judgedShortlist : shortlist;
|
|
54506
54844
|
decisionPath = judgePath;
|
|
54507
54845
|
}
|
|
54508
|
-
const finalPath =
|
|
54846
|
+
const finalPath = join17(root, "shortlist.md");
|
|
54509
54847
|
const finalShortlist = await ctx.task("final-shortlist", {
|
|
54510
54848
|
prompt: renderFinalShortlistPrompt(ctx.inputs.prompt, decisionPath),
|
|
54511
54849
|
context: "fresh",
|
|
@@ -55034,7 +55372,7 @@ var init_review_convergence = __esm(() => {
|
|
|
55034
55372
|
|
|
55035
55373
|
// dist/builtin/workflows/builtin/goal-artifacts.ts
|
|
55036
55374
|
import { writeFile as writeFile5 } from "node:fs/promises";
|
|
55037
|
-
import { join as
|
|
55375
|
+
import { join as join18 } from "node:path";
|
|
55038
55376
|
function artifactSafeName(value2) {
|
|
55039
55377
|
const safe = value2.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
55040
55378
|
return safe.length > 0 ? safe : "artifact";
|
|
@@ -55045,7 +55383,7 @@ function withoutTurn(value2) {
|
|
|
55045
55383
|
return copy;
|
|
55046
55384
|
}
|
|
55047
55385
|
async function writeReviewArtifact(artifactDir, reviewer, decision, rawText, convergenceDecision) {
|
|
55048
|
-
const artifactPath =
|
|
55386
|
+
const artifactPath = join18(artifactDir, `review-${artifactSafeName(reviewer)}.json`);
|
|
55049
55387
|
await writeFile5(artifactPath, `${JSON.stringify({ reviewer, decision, convergence_decision: convergenceDecision, raw_text: rawText }, null, 2)}
|
|
55050
55388
|
`, { encoding: "utf8" });
|
|
55051
55389
|
return artifactPath;
|
|
@@ -55054,7 +55392,7 @@ async function writeReviewRoundArtifact(artifactDir, reviews, consolidatedFindin
|
|
|
55054
55392
|
reviewer: review.reviewer,
|
|
55055
55393
|
findings: review.findings
|
|
55056
55394
|
}))), reverification = []) {
|
|
55057
|
-
const artifactPath =
|
|
55395
|
+
const artifactPath = join18(artifactDir, "review-round-latest.json");
|
|
55058
55396
|
const visibleReviews = reviews.map(withoutTurn);
|
|
55059
55397
|
await writeFile5(artifactPath, `${JSON.stringify({ reviews: visibleReviews, consolidated_findings: consolidatedFindings, reverification }, null, 2)}
|
|
55060
55398
|
`, { encoding: "utf8" });
|
|
@@ -55284,7 +55622,7 @@ var init_goal_review = __esm(() => {
|
|
|
55284
55622
|
// dist/builtin/workflows/builtin/goal-ledger.ts
|
|
55285
55623
|
import { randomUUID as randomUUID9 } from "node:crypto";
|
|
55286
55624
|
import { readFile as readFile4, rename, rm as rm2, writeFile as writeFile6 } from "node:fs/promises";
|
|
55287
|
-
import { dirname as
|
|
55625
|
+
import { dirname as dirname10, join as join19 } from "node:path";
|
|
55288
55626
|
import { isDeepStrictEqual } from "node:util";
|
|
55289
55627
|
function withoutTurn2(value2) {
|
|
55290
55628
|
const copy = { ...value2 };
|
|
@@ -55309,7 +55647,7 @@ function modelVisibleLedger(ledger) {
|
|
|
55309
55647
|
};
|
|
55310
55648
|
}
|
|
55311
55649
|
function goalLedgerStatePath(ledgerPath) {
|
|
55312
|
-
return
|
|
55650
|
+
return join19(dirname10(ledgerPath), LEDGER_STATE_FILENAME);
|
|
55313
55651
|
}
|
|
55314
55652
|
function appendLifecycleEvent(ledger, event, summary, turn = ledger.turns) {
|
|
55315
55653
|
ledger.lifecycle.push({
|
|
@@ -55385,7 +55723,7 @@ function recoverLegacyExecutionInterruption(ledger) {
|
|
|
55385
55723
|
return true;
|
|
55386
55724
|
}
|
|
55387
55725
|
async function createGoalLedger(objective, acceptanceCriteria, artifactDir) {
|
|
55388
|
-
const ledgerPath =
|
|
55726
|
+
const ledgerPath = join19(artifactDir, LEDGER_FILENAME);
|
|
55389
55727
|
const existing = await readExistingGoalLedger(ledgerPath);
|
|
55390
55728
|
if (existing !== undefined) {
|
|
55391
55729
|
if (recoverLegacyExecutionInterruption(existing))
|
|
@@ -55892,7 +56230,7 @@ var init_goal_orchestrator_prompts = __esm(() => {
|
|
|
55892
56230
|
});
|
|
55893
56231
|
|
|
55894
56232
|
// dist/builtin/workflows/builtin/goal-runner.ts
|
|
55895
|
-
import { join as
|
|
56233
|
+
import { join as join20 } from "node:path";
|
|
55896
56234
|
function positiveInteger(value2, fallback) {
|
|
55897
56235
|
if (typeof value2 !== "number" || !Number.isFinite(value2) || value2 <= 0) {
|
|
55898
56236
|
return fallback;
|
|
@@ -55943,7 +56281,7 @@ async function runGoalWorkflow(ctx, options) {
|
|
|
55943
56281
|
appendLifecycleEvent(ledger, "work_turn_started", "Orchestrator started.", turn);
|
|
55944
56282
|
await writeGoalLedger(ledgerPath, ledger);
|
|
55945
56283
|
}
|
|
55946
|
-
const orchestratorReceiptPath =
|
|
56284
|
+
const orchestratorReceiptPath = join20(artifactDir, "orchestrator-receipt.md");
|
|
55947
56285
|
const orchestratorForkOptions = forkContinuationOptions(previousOrchestratorSessionId);
|
|
55948
56286
|
const orchestratorPrompt = orchestratorForkOptions.forkFromSessionId === undefined ? renderGoalOrchestratorPrompt({
|
|
55949
56287
|
ledger,
|
|
@@ -56014,7 +56352,7 @@ async function runGoalWorkflow(ctx, options) {
|
|
|
56014
56352
|
const reviewerName = result.name ?? result.stageName;
|
|
56015
56353
|
const normalizedReviewerName = reviewerName.replace(/-\d+$/u, "");
|
|
56016
56354
|
const parsed = reviewerContractError === undefined ? parsedReviewDecisionFromResult(result, reviewerName) : { decision: reviewerErrorDecision(reviewerContractError.message), parsed: false, diagnostics: [reviewerContractError.message] };
|
|
56017
|
-
const reviewArtifactPath =
|
|
56355
|
+
const reviewArtifactPath = join20(artifactDir, `review-${artifactSafeName(normalizedReviewerName)}.json`);
|
|
56018
56356
|
const record = reviewDecisionToRecord({
|
|
56019
56357
|
turn,
|
|
56020
56358
|
reviewer: normalizedReviewerName,
|
|
@@ -56217,7 +56555,7 @@ var init_goal = __esm(() => {
|
|
|
56217
56555
|
}),
|
|
56218
56556
|
git_worktree_dir: Type15.String({
|
|
56219
56557
|
default: "",
|
|
56220
|
-
description: "Optional Git worktree path. Leave at the default unless the user explicitly requested worktree isolation — stages never create git worktrees on their own.
|
|
56558
|
+
description: "Optional Git worktree path. Leave at the default unless the user explicitly requested worktree isolation — stages never create git worktrees on their own. Absolute paths are used as-is; relative paths resolve from the invoking repo root (or the invoking cwd outside a Git repo). An existing Git worktree root of any repository is reused as-is, even from another repository or a non-Git cwd. Creating a worktree requires starting inside the source Git repo: a missing path or an empty directory becomes a new worktree from base_branch, and a non-empty non-Git directory is treated as a worktree root that gets this run's own <dir>/<4-hex>/<repo-name> checkout, reused on resume."
|
|
56221
56559
|
}),
|
|
56222
56560
|
create_pr: Type15.Boolean({
|
|
56223
56561
|
default: false,
|
|
@@ -56335,7 +56673,7 @@ var GROUNDED_REPORTING5 = "Before reporting progress, audit each claim against a
|
|
|
56335
56673
|
|
|
56336
56674
|
// dist/builtin/workflows/builtin/loop-until-done-runner.ts
|
|
56337
56675
|
import { mkdir as mkdir3, writeFile as writeFile7 } from "node:fs/promises";
|
|
56338
|
-
import { join as
|
|
56676
|
+
import { join as join21 } from "node:path";
|
|
56339
56677
|
import { Type as Type16 } from "typebox";
|
|
56340
56678
|
function progressCurve(entries) {
|
|
56341
56679
|
return entries.flatMap((entry) => entry.progress === undefined ? [] : [entry.progress.score]);
|
|
@@ -56418,18 +56756,18 @@ async function writeLedger(path, task, maxIterations, status, entries) {
|
|
|
56418
56756
|
}
|
|
56419
56757
|
async function runLoopUntilDone(ctx) {
|
|
56420
56758
|
const artifactDir = await stableArtifactRoot(ctx, "loop-until-done");
|
|
56421
|
-
const iterationsDir =
|
|
56422
|
-
const evaluationsDir =
|
|
56759
|
+
const iterationsDir = join21(artifactDir, "iterations");
|
|
56760
|
+
const evaluationsDir = join21(artifactDir, "evaluations");
|
|
56423
56761
|
await mkdir3(iterationsDir, { recursive: true });
|
|
56424
56762
|
await mkdir3(evaluationsDir, { recursive: true });
|
|
56425
|
-
const ledgerPath =
|
|
56763
|
+
const ledgerPath = join21(artifactDir, "progress-ledger.json");
|
|
56426
56764
|
const entries = [];
|
|
56427
56765
|
const iterationArtifactPaths = [];
|
|
56428
56766
|
const evaluationArtifactPaths = [];
|
|
56429
56767
|
await writeLedger(ledgerPath, ctx.inputs.prompt, ctx.inputs.max_iterations, "active", entries);
|
|
56430
56768
|
for (let iteration = 1;iteration <= ctx.inputs.max_iterations; iteration += 1) {
|
|
56431
|
-
const iterationPath =
|
|
56432
|
-
const evaluationPath =
|
|
56769
|
+
const iterationPath = join21(iterationsDir, `iteration-${iteration}.md`);
|
|
56770
|
+
const evaluationPath = join21(evaluationsDir, `evaluation-${iteration}.json`);
|
|
56433
56771
|
iterationArtifactPaths.push(iterationPath);
|
|
56434
56772
|
evaluationArtifactPaths.push(evaluationPath);
|
|
56435
56773
|
await ctx.task(`iteration-${iteration}`, {
|
|
@@ -56478,7 +56816,7 @@ async function runLoopUntilDone(ctx) {
|
|
|
56478
56816
|
await writeLedger(ledgerPath, ctx.inputs.prompt, ctx.inputs.max_iterations, decision.done ? "complete" : "active", entries);
|
|
56479
56817
|
if (decision.done) {
|
|
56480
56818
|
const report = progressReport(entries);
|
|
56481
|
-
const resultPath =
|
|
56819
|
+
const resultPath = join21(artifactDir, "result.md");
|
|
56482
56820
|
const final = await ctx.task("completion-summary", {
|
|
56483
56821
|
prompt: renderCompletionPrompt({ task: ctx.inputs.prompt, ledgerPath, iterationPath }),
|
|
56484
56822
|
context: "fresh",
|
|
@@ -56595,9 +56933,9 @@ var init_loop_until_done = __esm(() => {
|
|
|
56595
56933
|
|
|
56596
56934
|
// dist/builtin/workflows/builtin/open-claude-design-utils.ts
|
|
56597
56935
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
56598
|
-
import { mkdirSync as
|
|
56936
|
+
import { mkdirSync as mkdirSync6 } from "node:fs";
|
|
56599
56937
|
import { tmpdir, userInfo } from "node:os";
|
|
56600
|
-
import { join as
|
|
56938
|
+
import { join as join22 } from "node:path";
|
|
56601
56939
|
import { Type as Type18 } from "typebox";
|
|
56602
56940
|
import { createChildProcessEnvironment as createChildProcessEnvironment3 } from "@bastani/atomic";
|
|
56603
56941
|
function taggedPrompt3(sections) {
|
|
@@ -56640,32 +56978,32 @@ function tmpArtifactBase() {
|
|
|
56640
56978
|
try {
|
|
56641
56979
|
user = userInfo().username.replace(/[^A-Za-z0-9._-]/g, "_") || "default";
|
|
56642
56980
|
} catch {}
|
|
56643
|
-
return
|
|
56981
|
+
return join22(tmpdir(), `open-claude-design-${user}`);
|
|
56644
56982
|
}
|
|
56645
56983
|
function prepareArtifactDir(cwd = process.cwd()) {
|
|
56646
56984
|
const runId = `${new Date().toISOString().replace(/[:.]/g, "-")}-${Math.random().toString(36).slice(2, 8)}`;
|
|
56647
56985
|
const tmpCandidates = [
|
|
56648
|
-
|
|
56649
|
-
|
|
56986
|
+
join22(tmpArtifactBase(), runId),
|
|
56987
|
+
join22(tmpdir(), "open-claude-design", runId)
|
|
56650
56988
|
];
|
|
56651
|
-
const candidates = process.env.NODE_ENV === "test" ? tmpCandidates : [
|
|
56989
|
+
const candidates = process.env.NODE_ENV === "test" ? tmpCandidates : [join22(cwd, "specs", "design", runId), ...tmpCandidates];
|
|
56652
56990
|
for (const candidate of candidates) {
|
|
56653
56991
|
try {
|
|
56654
|
-
|
|
56992
|
+
mkdirSync6(candidate, { recursive: true });
|
|
56655
56993
|
return {
|
|
56656
56994
|
runId,
|
|
56657
56995
|
artifactDir: candidate,
|
|
56658
|
-
previewPath:
|
|
56659
|
-
specPath:
|
|
56996
|
+
previewPath: join22(candidate, "preview.html"),
|
|
56997
|
+
specPath: join22(candidate, "spec.html")
|
|
56660
56998
|
};
|
|
56661
56999
|
} catch {}
|
|
56662
57000
|
}
|
|
56663
|
-
const fallback =
|
|
57001
|
+
const fallback = join22(tmpArtifactBase(), runId);
|
|
56664
57002
|
return {
|
|
56665
57003
|
runId,
|
|
56666
57004
|
artifactDir: fallback,
|
|
56667
|
-
previewPath:
|
|
56668
|
-
specPath:
|
|
57005
|
+
previewPath: join22(fallback, "preview.html"),
|
|
57006
|
+
specPath: join22(fallback, "spec.html")
|
|
56669
57007
|
};
|
|
56670
57008
|
}
|
|
56671
57009
|
function ensureAgentBrowser() {
|
|
@@ -56999,8 +57337,8 @@ var init_open_claude_design_live_protocol = __esm(() => {
|
|
|
56999
57337
|
});
|
|
57000
57338
|
|
|
57001
57339
|
// dist/builtin/workflows/builtin/open-claude-design-setup.ts
|
|
57002
|
-
import { mkdirSync as
|
|
57003
|
-
import { join as
|
|
57340
|
+
import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync4 } from "node:fs";
|
|
57341
|
+
import { join as join23 } from "node:path";
|
|
57004
57342
|
function renderDiscoveryContext(discovery) {
|
|
57005
57343
|
return [
|
|
57006
57344
|
`Confirmed design brief: ${discovery.brief}`,
|
|
@@ -57065,10 +57403,10 @@ async function runDiscoveryAndInit(args) {
|
|
|
57065
57403
|
};
|
|
57066
57404
|
}
|
|
57067
57405
|
function designContextPath(artifactDir) {
|
|
57068
|
-
return
|
|
57406
|
+
return join23(artifactDir, DESIGN_CONTEXT_FILENAME);
|
|
57069
57407
|
}
|
|
57070
57408
|
function referencesBriefPath(artifactDir) {
|
|
57071
|
-
return
|
|
57409
|
+
return join23(artifactDir, REFERENCES_BRIEF_FILENAME);
|
|
57072
57410
|
}
|
|
57073
57411
|
function buildReferenceDiscoveryPrompt(args) {
|
|
57074
57412
|
const siteList = REFERENCE_DESIGN_SITES.map((site, index) => `${index + 1}. ${site.name} — ${site.url}`).join(`
|
|
@@ -57094,8 +57432,8 @@ function buildReferenceDiscoveryPrompt(args) {
|
|
|
57094
57432
|
[
|
|
57095
57433
|
"Use the agent-browser skill to open each gallery; if `agent-browser` reports a missing browser executable, follow the bootstrap rules and retry once.",
|
|
57096
57434
|
"From each gallery, choose 1-3 fitting designs and CLICK INTO each actual live or project-detail page; do not capture only the grid or thumbnail.",
|
|
57097
|
-
`Capture motion across the entire page: start \`agent-browser record start ${
|
|
57098
|
-
`Also run \`agent-browser screenshot ${
|
|
57435
|
+
`Capture motion across the entire page: start \`agent-browser record start ${join23(args.artifactDir, "ref-<site>-<n>.webm")}\`, scroll smoothly in small increments with waits (using repeated \`agent-browser scroll down 600\` or \`agent-browser mouse wheel 600\`) so animations fire and lazy content loads, then run \`agent-browser record stop\`.`,
|
|
57436
|
+
`Also run \`agent-browser screenshot ${join23(args.artifactDir, "ref-<site>-<n>.png")} --full\`; this still is the minimum when video is unavailable.`,
|
|
57099
57437
|
"Record the actual destination URL, title, and author. For each reference, cite observed layout, typography, color, spacing, and motion traits rather than inferred traits.",
|
|
57100
57438
|
"Assess fit against DESIGN.md, PRODUCT.md, and the ds-* discovery evidence in the design-context file; prefer on-brand references and flag departures.",
|
|
57101
57439
|
"Use ask_user_question to ask which reference direction they prefer, offering 2-4 strongest options plus `None of these fit`. If none fit, ask them to provide a reference image, screenshot, URL, or local file path and record the answer.",
|
|
@@ -57120,8 +57458,8 @@ function buildReferenceDiscoveryPrompt(args) {
|
|
|
57120
57458
|
}
|
|
57121
57459
|
function writeRequiredContextArtifact(artifactDir, filePath, content, label) {
|
|
57122
57460
|
try {
|
|
57123
|
-
|
|
57124
|
-
|
|
57461
|
+
mkdirSync7(artifactDir, { recursive: true });
|
|
57462
|
+
writeFileSync4(filePath, `${content}
|
|
57125
57463
|
`);
|
|
57126
57464
|
} catch (error) {
|
|
57127
57465
|
const detail = error instanceof Error ? error.message : String(error);
|
|
@@ -57672,7 +58010,7 @@ var init_ralph_review_gate = __esm(() => {
|
|
|
57672
58010
|
|
|
57673
58011
|
// dist/builtin/workflows/builtin/ralph-core.ts
|
|
57674
58012
|
import { mkdir as mkdir4, writeFile as writeFile8 } from "node:fs/promises";
|
|
57675
|
-
import { dirname as
|
|
58013
|
+
import { dirname as dirname11, join as join24 } from "node:path";
|
|
57676
58014
|
import { Type as Type20 } from "typebox";
|
|
57677
58015
|
function taggedPrompt4(sections) {
|
|
57678
58016
|
return sections.map(([tag, content]) => {
|
|
@@ -57712,11 +58050,11 @@ function slugifyResearchTopic(prompt) {
|
|
|
57712
58050
|
}
|
|
57713
58051
|
function defaultResearchPath(prompt, now = new Date) {
|
|
57714
58052
|
const date = now.toISOString().slice(0, 10);
|
|
57715
|
-
return
|
|
58053
|
+
return join24(DEFAULT_RESEARCH_DIR, `${date}-${slugifyResearchTopic(prompt)}.md`);
|
|
57716
58054
|
}
|
|
57717
58055
|
async function createImplementationNotesFile(prompt, runId) {
|
|
57718
58056
|
const notesDir = await createWorkflowArtifactDirectory(runId);
|
|
57719
|
-
const notesPath =
|
|
58057
|
+
const notesPath = join24(notesDir, IMPLEMENTATION_NOTES_FILENAME);
|
|
57720
58058
|
const initialNotes = [
|
|
57721
58059
|
"# Implementation Notes",
|
|
57722
58060
|
"",
|
|
@@ -57737,7 +58075,7 @@ async function createImplementationNotesFile(prompt, runId) {
|
|
|
57737
58075
|
}
|
|
57738
58076
|
async function createQaEvidenceVideoPath(runId) {
|
|
57739
58077
|
const qaDir = await createWorkflowArtifactDirectory(runId);
|
|
57740
|
-
return
|
|
58078
|
+
return join24(qaDir, QA_E2E_VIDEO_FILENAME);
|
|
57741
58079
|
}
|
|
57742
58080
|
function renderQaE2eVideoGuidance(qaVideoPath) {
|
|
57743
58081
|
return [
|
|
@@ -57803,7 +58141,7 @@ function artifactSafeName2(value2) {
|
|
|
57803
58141
|
return safe.length > 0 ? safe : "artifact";
|
|
57804
58142
|
}
|
|
57805
58143
|
async function writeJsonArtifact(path, content) {
|
|
57806
|
-
await mkdir4(
|
|
58144
|
+
await mkdir4(dirname11(path), { recursive: true });
|
|
57807
58145
|
await writeFile8(path, `${JSON.stringify(content, null, 2)}
|
|
57808
58146
|
`, {
|
|
57809
58147
|
encoding: "utf8"
|
|
@@ -58141,7 +58479,7 @@ var init_ralph_models = __esm(() => {
|
|
|
58141
58479
|
|
|
58142
58480
|
// dist/builtin/workflows/builtin/ralph-runner.ts
|
|
58143
58481
|
import { existsSync as existsSync4 } from "node:fs";
|
|
58144
|
-
import { join as
|
|
58482
|
+
import { join as join25, resolve as resolve10 } from "node:path";
|
|
58145
58483
|
async function runRalphWorkflow(ctx, options) {
|
|
58146
58484
|
const { prompt, acceptanceCriteria, maxLoops, comparisonBaseBranch, workflowStartCwd, createPr, runId } = options;
|
|
58147
58485
|
let latestReviewReportPath;
|
|
@@ -58153,7 +58491,7 @@ async function runRalphWorkflow(ctx, options) {
|
|
|
58153
58491
|
let finalPrReport;
|
|
58154
58492
|
const workflowCwdContext = workflowCwdContextSection(workflowStartCwd);
|
|
58155
58493
|
const workflowPrompt = prompt;
|
|
58156
|
-
const workflowResearchPath =
|
|
58494
|
+
const workflowResearchPath = resolve10(workflowStartCwd, defaultResearchPath(workflowPrompt));
|
|
58157
58495
|
const implementationNotesPath = await createImplementationNotesFile(workflowPrompt, runId);
|
|
58158
58496
|
const qaVideoPath = await createQaEvidenceVideoPath(runId);
|
|
58159
58497
|
const artifactDir = await createWorkflowArtifactDirectory(runId);
|
|
@@ -58202,7 +58540,7 @@ async function runRalphWorkflow(ctx, options) {
|
|
|
58202
58540
|
const researchPath = workflowResearchPath;
|
|
58203
58541
|
finalResearchPath = researchPath;
|
|
58204
58542
|
finalPlanPath = researchPath;
|
|
58205
|
-
const orchestratorReportPath =
|
|
58543
|
+
const orchestratorReportPath = join25(artifactDir, "orchestrator-report.md");
|
|
58206
58544
|
const orchestratorForkOptions = forkContinuationOptions3(previousOrchestratorSessionId);
|
|
58207
58545
|
const orchestratorPrompt = orchestratorForkOptions.forkFromSessionId === undefined ? taggedPrompt4([
|
|
58208
58546
|
["acceptance_criteria", keepContext(acceptanceCriteria)],
|
|
@@ -58338,7 +58676,7 @@ Read it before implementation or delegation because it is the primary current im
|
|
|
58338
58676
|
diagnostics: parsed.diagnostics,
|
|
58339
58677
|
allowFinalActionRemaining: createPr
|
|
58340
58678
|
});
|
|
58341
|
-
const artifactPath =
|
|
58679
|
+
const artifactPath = join25(artifactDir, `review-${artifactSafeName2(reviewer)}.json`);
|
|
58342
58680
|
await writeJsonArtifact(artifactPath, {
|
|
58343
58681
|
reviewer,
|
|
58344
58682
|
decision: parsed.decision,
|
|
@@ -58394,7 +58732,7 @@ Read it before implementation or delegation because it is the primary current im
|
|
|
58394
58732
|
usage: fold_usage([orchestrator, ...reviews, ...reverifyResults])
|
|
58395
58733
|
}));
|
|
58396
58734
|
}
|
|
58397
|
-
latestReviewReportPath = await writeJsonArtifact(
|
|
58735
|
+
latestReviewReportPath = await writeJsonArtifact(join25(artifactDir, "review-round-latest.json"), {
|
|
58398
58736
|
convergence_decision: roundConvergenceDecision,
|
|
58399
58737
|
convergence: convergenceEntries,
|
|
58400
58738
|
consolidated_findings: reverified.batch,
|
|
@@ -58543,7 +58881,7 @@ var init_ralph = __esm(() => {
|
|
|
58543
58881
|
}),
|
|
58544
58882
|
git_worktree_dir: Type21.String({
|
|
58545
58883
|
default: "",
|
|
58546
|
-
description: "Optional Git worktree path. Leave at the default unless the user explicitly requested worktree isolation — stages never create git worktrees on their own.
|
|
58884
|
+
description: "Optional Git worktree path. Leave at the default unless the user explicitly requested worktree isolation — stages never create git worktrees on their own. Absolute paths are used as-is; relative paths resolve from the invoking repo root (or the invoking cwd outside a Git repo). An existing Git worktree root of any repository is reused as-is, even from another repository or a non-Git cwd. Creating a worktree requires starting inside the source Git repo: a missing path or an empty directory becomes a new worktree from base_branch, and a non-empty non-Git directory is treated as a worktree root that gets this run's own <dir>/<4-hex>/<repo-name> checkout, reused on resume."
|
|
58547
58885
|
}),
|
|
58548
58886
|
create_pr: Type21.Boolean({
|
|
58549
58887
|
default: false,
|
|
@@ -58763,7 +59101,7 @@ var init_tournament_prompts = __esm(() => {
|
|
|
58763
59101
|
|
|
58764
59102
|
// dist/builtin/workflows/builtin/tournament-runner.ts
|
|
58765
59103
|
import { mkdir as mkdir5, readFile as readFile5, writeFile as writeFile9 } from "node:fs/promises";
|
|
58766
|
-
import { join as
|
|
59104
|
+
import { join as join26, isAbsolute as isAbsolute9, relative as relative5, resolve as resolve11 } from "node:path";
|
|
58767
59105
|
import { Type as Type22 } from "typebox";
|
|
58768
59106
|
function serializableObject2(value2) {
|
|
58769
59107
|
if (value2 === null || Array.isArray(value2) || typeof value2 !== "object")
|
|
@@ -58800,8 +59138,8 @@ function criterionSlug(id) {
|
|
|
58800
59138
|
}
|
|
58801
59139
|
function judgeArtifactPath(judgesDir, job, criterionIndex) {
|
|
58802
59140
|
const filename = `judge-${job.a}-${job.b}-c${criterionIndex}-${criterionSlug(job.criterionId)}-r${job.rep}.json`;
|
|
58803
|
-
const root =
|
|
58804
|
-
const path =
|
|
59141
|
+
const root = resolve11(judgesDir);
|
|
59142
|
+
const path = resolve11(judgesDir, filename);
|
|
58805
59143
|
const fromRoot = relative5(root, path);
|
|
58806
59144
|
if (fromRoot.startsWith("..") || isAbsolute9(fromRoot)) {
|
|
58807
59145
|
throw new Error(`tournament: judge artifact escaped its directory: ${path}`);
|
|
@@ -58852,16 +59190,16 @@ function combineUsage(totals) {
|
|
|
58852
59190
|
}
|
|
58853
59191
|
async function runTournament(ctx) {
|
|
58854
59192
|
const artifactDir = await stableArtifactRoot(ctx, "tournament");
|
|
58855
|
-
const attemptsDir =
|
|
59193
|
+
const attemptsDir = join26(artifactDir, "attempts");
|
|
58856
59194
|
const numAttempts = ctx.inputs.num_attempts ?? 4;
|
|
58857
59195
|
const maxConcurrency = ctx.inputs.max_concurrency ?? 4;
|
|
58858
59196
|
const nEvaluations = ctx.inputs.n_evaluations ?? 2;
|
|
58859
59197
|
const pivotCount = ctx.inputs.pivots ?? 1;
|
|
58860
59198
|
const seed = ctx.inputs.seed ?? 0;
|
|
58861
|
-
const judgesDir =
|
|
59199
|
+
const judgesDir = join26(artifactDir, "judges");
|
|
58862
59200
|
await mkdir5(attemptsDir, { recursive: true });
|
|
58863
59201
|
await mkdir5(judgesDir, { recursive: true });
|
|
58864
|
-
const attemptArtifactPaths = Array.from({ length: numAttempts }, (_, index) =>
|
|
59202
|
+
const attemptArtifactPaths = Array.from({ length: numAttempts }, (_, index) => join26(attemptsDir, `attempt-${index + 1}.md`));
|
|
58865
59203
|
const models = ctx.inputs.models;
|
|
58866
59204
|
const attemptResults = await ctx.parallel(attemptArtifactPaths.map((path, index) => ({
|
|
58867
59205
|
name: `attempt-${index + 1}`,
|
|
@@ -59026,7 +59364,7 @@ The previous report was invalid. Return the required JSON schema exactly once.`,
|
|
|
59026
59364
|
const outputRanking = ranking.map(({ label, meanPreference }) => ({ label, meanPreference }));
|
|
59027
59365
|
const budgetPivots = Math.min(pivotCount, entrants.length);
|
|
59028
59366
|
const comparisonBudget = (entrants.length + budgetPivots * (entrants.length - budgetPivots) + budgetPivots * (budgetPivots - 1) / 2) * criteria.length * nEvaluations;
|
|
59029
|
-
const comparisonPath =
|
|
59367
|
+
const comparisonPath = join26(artifactDir, "comparisons.json");
|
|
59030
59368
|
const assignment = modelAssignment(entrants, models);
|
|
59031
59369
|
const phaseUsage = {
|
|
59032
59370
|
attempts: fold_usage(attemptResults),
|
|
@@ -59062,7 +59400,7 @@ The previous report was invalid. Return the required JSON schema exactly once.`,
|
|
|
59062
59400
|
await writeLedger(fold_usage([]));
|
|
59063
59401
|
const winner = ranking[0];
|
|
59064
59402
|
const winnerEntrant = entrants[winner.index];
|
|
59065
|
-
const resultPath =
|
|
59403
|
+
const resultPath = join26(artifactDir, "winner.md");
|
|
59066
59404
|
const reducer = await ctx.task("comparisons-reducer", {
|
|
59067
59405
|
prompt: renderComparisonsReducerPrompt({
|
|
59068
59406
|
task: ctx.inputs.prompt,
|
|
@@ -59371,17 +59709,26 @@ function isSameCheckpointValue(stored, next) {
|
|
|
59371
59709
|
const hash = (value2) => durableHash(value2);
|
|
59372
59710
|
return hash(stored) === hash(next);
|
|
59373
59711
|
}
|
|
59712
|
+
function withTimesOf(later, recorded) {
|
|
59713
|
+
const both = (key) => typeof recorded[key] === "number" && typeof later[key] === "number";
|
|
59714
|
+
return {
|
|
59715
|
+
...later,
|
|
59716
|
+
...both("startedAt") ? { startedAt: recorded.startedAt } : {},
|
|
59717
|
+
...both("endedAt") ? { endedAt: recorded.endedAt } : {},
|
|
59718
|
+
...both("completedAt") ? { completedAt: recorded.completedAt } : {}
|
|
59719
|
+
};
|
|
59720
|
+
}
|
|
59374
59721
|
function isRecordedCheckpointValue(stored, next) {
|
|
59375
59722
|
const recorded = stored;
|
|
59376
59723
|
if (isSameCheckpointValue(recorded, next))
|
|
59377
59724
|
return true;
|
|
59378
|
-
|
|
59379
|
-
if (typeof completedAt !== "number")
|
|
59725
|
+
if (typeof recorded.completedAt !== "number")
|
|
59380
59726
|
return false;
|
|
59381
59727
|
const later = next;
|
|
59382
|
-
if (startedAt === undefined !== (later.startedAt === undefined))
|
|
59728
|
+
if (recorded.startedAt === undefined !== (later.startedAt === undefined))
|
|
59383
59729
|
return false;
|
|
59384
|
-
|
|
59730
|
+
const retimed = withTimesOf(later, recorded);
|
|
59731
|
+
return isSameCheckpointValue(recorded.topology !== undefined && later.topology !== undefined ? { ...retimed, topology: withTimesOf(later.topology, recorded.topology) } : retimed, recorded);
|
|
59385
59732
|
}
|
|
59386
59733
|
var MIRROR_FRESH_MS = 2000;
|
|
59387
59734
|
var init_pool_remote_journal = __esm(() => {
|
|
@@ -59959,7 +60306,7 @@ class SqlPoolQueue {
|
|
|
59959
60306
|
throw new Error("Stale pool coordinator authority", { cause: error });
|
|
59960
60307
|
throw error;
|
|
59961
60308
|
});
|
|
59962
|
-
const row = await this.current(tx, lease, token);
|
|
60309
|
+
const row = await this.current(tx, lease, token, true);
|
|
59963
60310
|
if (row.epoch !== lease.epoch)
|
|
59964
60311
|
throw new Error(STALE_WORK_LEASE);
|
|
59965
60312
|
assertSession();
|
|
@@ -60417,15 +60764,7 @@ class SqlPoolQueue {
|
|
|
60417
60764
|
}
|
|
60418
60765
|
}
|
|
60419
60766
|
function isRecordedCheckpoint(stored, payload) {
|
|
60420
|
-
|
|
60421
|
-
return true;
|
|
60422
|
-
const { completedAt, startedAt } = JSON.parse(stored);
|
|
60423
|
-
if (typeof completedAt !== "number")
|
|
60424
|
-
return false;
|
|
60425
|
-
const next = JSON.parse(payload);
|
|
60426
|
-
if (startedAt === undefined !== (next.startedAt === undefined))
|
|
60427
|
-
return false;
|
|
60428
|
-
return serializeJson({ ...next, completedAt, ...startedAt === undefined ? {} : { startedAt } }) === stored;
|
|
60767
|
+
return stored === payload || isRecordedCheckpointValue(JSON.parse(stored), JSON.parse(payload));
|
|
60429
60768
|
}
|
|
60430
60769
|
function openSqlPoolQueue(journal, remote) {
|
|
60431
60770
|
const storage = sqlPoolStorage(journal);
|
|
@@ -62201,7 +62540,7 @@ function isBrokerStatusReport(value2) {
|
|
|
62201
62540
|
|
|
62202
62541
|
// dist/builtin/intercom/broker/paths.ts
|
|
62203
62542
|
import { homedir as homedir2 } from "os";
|
|
62204
|
-
import { join as
|
|
62543
|
+
import { join as join27 } from "path";
|
|
62205
62544
|
function getHomeDir() {
|
|
62206
62545
|
if (process.platform === "win32") {
|
|
62207
62546
|
if (process.env.USERPROFILE)
|
|
@@ -62218,7 +62557,7 @@ function expandTildePath(path) {
|
|
|
62218
62557
|
if (path === "~")
|
|
62219
62558
|
return getHomeDir();
|
|
62220
62559
|
if (path.startsWith("~/") || process.platform === "win32" && path.startsWith("~\\")) {
|
|
62221
|
-
return
|
|
62560
|
+
return join27(getHomeDir(), path.slice(2));
|
|
62222
62561
|
}
|
|
62223
62562
|
return path;
|
|
62224
62563
|
}
|
|
@@ -62229,31 +62568,31 @@ function getAgentDir5() {
|
|
|
62229
62568
|
const piAgentDir = process.env.PI_CODING_AGENT_DIR;
|
|
62230
62569
|
if (piAgentDir)
|
|
62231
62570
|
return expandTildePath(piAgentDir);
|
|
62232
|
-
return
|
|
62571
|
+
return join27(getHomeDir(), ".atomic", "agent");
|
|
62233
62572
|
}
|
|
62234
62573
|
function sanitizePipeSegment(value2) {
|
|
62235
62574
|
return value2.replace(/[^a-zA-Z0-9]+/g, "-").replace(/^-+|-+$/g, "").toLowerCase() || "default";
|
|
62236
62575
|
}
|
|
62237
62576
|
function getIntercomDirPath(agentDir = getAgentDir5()) {
|
|
62238
|
-
return
|
|
62577
|
+
return join27(agentDir, "intercom");
|
|
62239
62578
|
}
|
|
62240
62579
|
function getBrokerPidPath(agentDir = getAgentDir5()) {
|
|
62241
|
-
return
|
|
62580
|
+
return join27(getIntercomDirPath(agentDir), "broker.pid");
|
|
62242
62581
|
}
|
|
62243
62582
|
function getBrokerSpawnLockPath(agentDir = getAgentDir5()) {
|
|
62244
|
-
return
|
|
62583
|
+
return join27(getIntercomDirPath(agentDir), "broker.spawn.lock");
|
|
62245
62584
|
}
|
|
62246
62585
|
function getBrokerLogPath(agentDir = getAgentDir5()) {
|
|
62247
|
-
return
|
|
62586
|
+
return join27(getIntercomDirPath(agentDir), "broker.log");
|
|
62248
62587
|
}
|
|
62249
62588
|
function getBrokerStartAnnouncementPath(agentDir = getAgentDir5()) {
|
|
62250
|
-
return
|
|
62589
|
+
return join27(getIntercomDirPath(agentDir), "broker.starting");
|
|
62251
62590
|
}
|
|
62252
62591
|
function getBrokerSocketPath(platform = process.platform, agentDir = getAgentDir5()) {
|
|
62253
62592
|
if (platform === "win32") {
|
|
62254
62593
|
return `\\\\.\\pipe\\pi-intercom-${sanitizePipeSegment(agentDir)}`;
|
|
62255
62594
|
}
|
|
62256
|
-
return
|
|
62595
|
+
return join27(getIntercomDirPath(agentDir), "broker.sock");
|
|
62257
62596
|
}
|
|
62258
62597
|
var BROKER_STORAGE_ENV = "ATOMIC_STORAGE";
|
|
62259
62598
|
var init_paths = () => {};
|
|
@@ -63937,11 +64276,11 @@ var init_client = __esm(() => {
|
|
|
63937
64276
|
var BROKER_READY_TIMEOUT_MS = 30000, MIN_BROKER_STARTUP_TIMEOUT_MS = 1000;
|
|
63938
64277
|
|
|
63939
64278
|
// dist/builtin/intercom/broker/start-announcement.ts
|
|
63940
|
-
import { readFileSync as
|
|
64279
|
+
import { readFileSync as readFileSync5, unlinkSync as unlinkSync3, writeFileSync as writeFileSync5 } from "node:fs";
|
|
63941
64280
|
function readBrokerStartAnnouncement(announcement) {
|
|
63942
64281
|
let text;
|
|
63943
64282
|
try {
|
|
63944
|
-
text =
|
|
64283
|
+
text = readFileSync5(announcement.path, "utf-8");
|
|
63945
64284
|
} catch {
|
|
63946
64285
|
return;
|
|
63947
64286
|
}
|
|
@@ -63960,26 +64299,26 @@ var init_start_announcement = () => {};
|
|
|
63960
64299
|
// dist/builtin/intercom/broker/spawn.ts
|
|
63961
64300
|
import { spawn } from "child_process";
|
|
63962
64301
|
import { randomUUID as randomUUID14 } from "crypto";
|
|
63963
|
-
import { appendFileSync, chmodSync, closeSync as closeSync3, existsSync as existsSync5, mkdirSync as
|
|
63964
|
-
import { join as
|
|
64302
|
+
import { appendFileSync, chmodSync, closeSync as closeSync3, existsSync as existsSync5, mkdirSync as mkdirSync8, openSync as openSync3, readFileSync as readFileSync6, readSync as readSync2, statSync as statSync5, unlinkSync as unlinkSync4, writeFileSync as writeFileSync6 } from "fs";
|
|
64303
|
+
import { join as join28, dirname as dirname12 } from "path";
|
|
63965
64304
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
63966
64305
|
import { createRequire as createRequire3 } from "module";
|
|
63967
64306
|
import { createChildProcessEnvironment as createChildProcessEnvironment4, isBunBinary, processSharedStorageSelection } from "@bastani/atomic";
|
|
63968
64307
|
function isIntercomPackageRoot(dir) {
|
|
63969
|
-
return existsSync5(
|
|
64308
|
+
return existsSync5(join28(dir, "package.json")) && existsSync5(join28(dir, "broker"));
|
|
63970
64309
|
}
|
|
63971
64310
|
function resolveIntercomPackageRoot(moduleHref) {
|
|
63972
|
-
const here =
|
|
64311
|
+
const here = dirname12(fileURLToPath4(moduleHref));
|
|
63973
64312
|
if (isIntercomPackageRoot(here))
|
|
63974
64313
|
return here;
|
|
63975
|
-
return
|
|
64314
|
+
return join28(here, "..");
|
|
63976
64315
|
}
|
|
63977
64316
|
function resolveBrokerEntrypoint(packageRoot) {
|
|
63978
|
-
const bundled =
|
|
63979
|
-
return existsSync5(bundled) ? bundled :
|
|
64317
|
+
const bundled = join28(packageRoot, "broker", "broker.bundle.mjs");
|
|
64318
|
+
return existsSync5(bundled) ? bundled : join28(packageRoot, "broker", "broker.ts");
|
|
63980
64319
|
}
|
|
63981
64320
|
function ensurePrivateDirectory(path) {
|
|
63982
|
-
|
|
64321
|
+
mkdirSync8(path, { recursive: true, mode: 448 });
|
|
63983
64322
|
if (process.platform !== "win32")
|
|
63984
64323
|
chmodSync(path, 448);
|
|
63985
64324
|
}
|
|
@@ -63992,14 +64331,14 @@ function getCurrentBrokerRuntime() {
|
|
|
63992
64331
|
return isBunBinary ? "bun-binary" : "bun-source";
|
|
63993
64332
|
}
|
|
63994
64333
|
function requireFromExtensionDir(extensionDir) {
|
|
63995
|
-
return createRequire3(
|
|
64334
|
+
return createRequire3(join28(extensionDir, "package.json"));
|
|
63996
64335
|
}
|
|
63997
64336
|
function getJitiCliPath(extensionDir = EXTENSION_DIR) {
|
|
63998
64337
|
try {
|
|
63999
64338
|
const jitiPackage = requireFromExtensionDir(extensionDir).resolve("jiti/package.json");
|
|
64000
|
-
return
|
|
64339
|
+
return join28(dirname12(jitiPackage), "lib", "jiti-cli.mjs");
|
|
64001
64340
|
} catch {
|
|
64002
|
-
return
|
|
64341
|
+
return join28(extensionDir, "node_modules", "jiti", "lib", "jiti-cli.mjs");
|
|
64003
64342
|
}
|
|
64004
64343
|
}
|
|
64005
64344
|
function getDefaultBrokerRunnerPath(extensionDir) {
|
|
@@ -64021,7 +64360,7 @@ function quoteWindowsArg(value2) {
|
|
|
64021
64360
|
return `"${value2.replace(/"/g, '""')}"`;
|
|
64022
64361
|
}
|
|
64023
64362
|
function getWindowsHiddenLauncherPath(intercomDir = INTERCOM_DIR) {
|
|
64024
|
-
return
|
|
64363
|
+
return join28(intercomDir, "broker-launch.vbs");
|
|
64025
64364
|
}
|
|
64026
64365
|
function usesDefaultBrokerCommand(brokerCommand, brokerArgs) {
|
|
64027
64366
|
return brokerCommand === "npx" && brokerArgs.length === 2 && brokerArgs[0] === "--no-install" && brokerArgs[1] === "tsx";
|
|
@@ -64048,8 +64387,8 @@ function getWindowsHiddenLauncherScript(commandLine) {
|
|
|
64048
64387
|
`);
|
|
64049
64388
|
}
|
|
64050
64389
|
function writeWindowsHiddenLauncher(commandLine, launcherPath = getWindowsHiddenLauncherPath()) {
|
|
64051
|
-
ensurePrivateDirectory(
|
|
64052
|
-
|
|
64390
|
+
ensurePrivateDirectory(dirname12(launcherPath));
|
|
64391
|
+
writeFileSync6(launcherPath, getWindowsHiddenLauncherScript(commandLine), "utf-8");
|
|
64053
64392
|
return launcherPath;
|
|
64054
64393
|
}
|
|
64055
64394
|
function getBrokerLaunchSpec(brokerPath, brokerCommand, brokerArgs, extensionDir = EXTENSION_DIR, platform = process.platform, intercomDir = INTERCOM_DIR, nodePath = process.execPath, runtime = getCurrentBrokerRuntime(), logPath = BROKER_LOG) {
|
|
@@ -64092,14 +64431,14 @@ function getBrokerSpawnOptions(extensionDir = EXTENSION_DIR, stderr = "ignore",
|
|
|
64092
64431
|
};
|
|
64093
64432
|
}
|
|
64094
64433
|
function resetBrokerLog(logPath = BROKER_LOG) {
|
|
64095
|
-
ensurePrivateDirectory(
|
|
64434
|
+
ensurePrivateDirectory(dirname12(logPath));
|
|
64096
64435
|
try {
|
|
64097
64436
|
const previous = readBrokerLogTail(logPath);
|
|
64098
64437
|
closeSync3(openSync3(logPath, "w"));
|
|
64099
64438
|
if (previous.length > 0) {
|
|
64100
64439
|
try {
|
|
64101
64440
|
const previousPath = `${logPath}.previous`;
|
|
64102
|
-
|
|
64441
|
+
writeFileSync6(previousPath, Buffer.from(previous).subarray(-BROKER_LOG_TAIL_BYTES), { mode: 384 });
|
|
64103
64442
|
if (process.platform !== "win32")
|
|
64104
64443
|
chmodSync(previousPath, 384);
|
|
64105
64444
|
} catch {}
|
|
@@ -64243,7 +64582,7 @@ async function isBrokerRunning() {
|
|
|
64243
64582
|
if (!existsSync5(BROKER_PID))
|
|
64244
64583
|
return false;
|
|
64245
64584
|
try {
|
|
64246
|
-
const pid = parseInt(
|
|
64585
|
+
const pid = parseInt(readFileSync6(BROKER_PID, "utf-8").trim(), 10);
|
|
64247
64586
|
if (!Number.isFinite(pid))
|
|
64248
64587
|
return false;
|
|
64249
64588
|
process.kill(pid, 0);
|
|
@@ -64268,7 +64607,7 @@ function handSpawnLockToBroker(pid, owner) {
|
|
|
64268
64607
|
}
|
|
64269
64608
|
function ownsSpawnLock(owner, lockPath = BROKER_SPAWN_LOCK) {
|
|
64270
64609
|
try {
|
|
64271
|
-
return
|
|
64610
|
+
return readFileSync6(lockPath, "utf-8").startsWith(owner);
|
|
64272
64611
|
} catch {
|
|
64273
64612
|
return false;
|
|
64274
64613
|
}
|
|
@@ -64280,7 +64619,7 @@ function acquireSpawnLock() {
|
|
|
64280
64619
|
${Date.now()}
|
|
64281
64620
|
`;
|
|
64282
64621
|
try {
|
|
64283
|
-
|
|
64622
|
+
writeFileSync6(BROKER_SPAWN_LOCK, owner, { flag: "wx" });
|
|
64284
64623
|
return owner;
|
|
64285
64624
|
} catch (error) {
|
|
64286
64625
|
if (!(error instanceof Error) || error.code !== "EEXIST") {
|
|
@@ -64310,7 +64649,7 @@ function isSpawnLockStale(lockPath = BROKER_SPAWN_LOCK, nowMs = Date.now()) {
|
|
|
64310
64649
|
return false;
|
|
64311
64650
|
}
|
|
64312
64651
|
try {
|
|
64313
|
-
const [spawnerLine = "", createdAtLine = "", brokerLine = ""] =
|
|
64652
|
+
const [spawnerLine = "", createdAtLine = "", brokerLine = ""] = readFileSync6(lockPath, "utf-8").trim().split(`
|
|
64314
64653
|
`);
|
|
64315
64654
|
const spawnerPid = Number.parseInt(spawnerLine, 10);
|
|
64316
64655
|
const createdAt = Number.parseInt(createdAtLine, 10);
|
|
@@ -64388,14 +64727,14 @@ var init_reply_waiter = __esm(() => {
|
|
|
64388
64727
|
});
|
|
64389
64728
|
|
|
64390
64729
|
// dist/builtin/intercom/config.ts
|
|
64391
|
-
import { existsSync as existsSync6, readFileSync as
|
|
64730
|
+
import { existsSync as existsSync6, readFileSync as readFileSync7 } from "fs";
|
|
64392
64731
|
import { getAgentConfigPaths } from "@bastani/atomic";
|
|
64393
64732
|
function loadConfig() {
|
|
64394
64733
|
if (!existsSync6(CONFIG_PATH)) {
|
|
64395
64734
|
return { ...defaults };
|
|
64396
64735
|
}
|
|
64397
64736
|
try {
|
|
64398
|
-
const raw =
|
|
64737
|
+
const raw = readFileSync7(CONFIG_PATH, "utf-8");
|
|
64399
64738
|
const parsed = JSON.parse(raw);
|
|
64400
64739
|
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
64401
64740
|
throw new Error("Config must be a JSON object");
|
|
@@ -64978,12 +65317,12 @@ var init_sql_pool_usage = __esm(() => {
|
|
|
64978
65317
|
|
|
64979
65318
|
// dist/builtin/workflows/src/workers/pooled-watch-trace.ts
|
|
64980
65319
|
import { appendFileSync as appendFileSync2 } from "node:fs";
|
|
64981
|
-
import { dirname as
|
|
65320
|
+
import { dirname as dirname13, join as join29, resolve as resolve12 } from "node:path";
|
|
64982
65321
|
function configurePooledWatchTrace(journalPath, processRole) {
|
|
64983
65322
|
if (setting === undefined || setting === "" || setting === "0")
|
|
64984
65323
|
return;
|
|
64985
65324
|
role = processRole;
|
|
64986
|
-
file = setting === "1" ?
|
|
65325
|
+
file = setting === "1" ? join29(dirname13(resolve12(journalPath)), "pooled-watch-trace.jsonl") : resolve12(setting);
|
|
64987
65326
|
}
|
|
64988
65327
|
function tracePooledWatch(step, detail) {
|
|
64989
65328
|
if (file === undefined)
|
|
@@ -65176,8 +65515,8 @@ var init_startup_timeout = () => {};
|
|
|
65176
65515
|
import { fork } from "node:child_process";
|
|
65177
65516
|
import { randomUUID as randomUUID16 } from "node:crypto";
|
|
65178
65517
|
import { createWriteStream } from "node:fs";
|
|
65179
|
-
import { mkdir as mkdir6, rm as rm3, rmdir } from "node:fs/promises";
|
|
65180
|
-
import { dirname as
|
|
65518
|
+
import { appendFile, mkdir as mkdir6, rm as rm3, rmdir } from "node:fs/promises";
|
|
65519
|
+
import { dirname as dirname14, join as join30, resolve as resolve13 } from "node:path";
|
|
65181
65520
|
import { setTimeout as delay2 } from "node:timers/promises";
|
|
65182
65521
|
function isRootLeaseLost(error) {
|
|
65183
65522
|
return error instanceof StaleLeaseError || error.message.startsWith("Stale lease for run ");
|
|
@@ -65186,7 +65525,7 @@ function unconfirmedCleanup(groups) {
|
|
|
65186
65525
|
return new Error(`Local worker process cleanup unconfirmed; ${groups.length > 0 ? `process groups ${groups.join(", ")} still running after SIGKILL; ` : ""}permits quarantined`);
|
|
65187
65526
|
}
|
|
65188
65527
|
function workerLogDirectory(journalPath, root) {
|
|
65189
|
-
return
|
|
65528
|
+
return join30(dirname14(resolve13(journalPath)), "worker-logs", encodeURIComponent(root).replaceAll(".", "%2E"));
|
|
65190
65529
|
}
|
|
65191
65530
|
function token(work) {
|
|
65192
65531
|
return {
|
|
@@ -65368,9 +65707,13 @@ class PoolCoordinator {
|
|
|
65368
65707
|
incarnations = new Map;
|
|
65369
65708
|
readyWorkers = new Set;
|
|
65370
65709
|
logs = new Map;
|
|
65710
|
+
keptLogs = new Set;
|
|
65711
|
+
unconfirmedExits = new WeakSet;
|
|
65712
|
+
failed = false;
|
|
65371
65713
|
cleanups = new Set;
|
|
65372
65714
|
controlAcks = new Map;
|
|
65373
65715
|
lastHeard = new Map;
|
|
65716
|
+
awaiting = new Map;
|
|
65374
65717
|
pendingActivity = new Map;
|
|
65375
65718
|
batches = new Map;
|
|
65376
65719
|
publicationFailures = new Map;
|
|
@@ -65415,7 +65758,6 @@ class PoolCoordinator {
|
|
|
65415
65758
|
nextRecoveryAt = 0;
|
|
65416
65759
|
cleanupFailure;
|
|
65417
65760
|
held = new Set;
|
|
65418
|
-
quarantined = new Set;
|
|
65419
65761
|
unreaped = [];
|
|
65420
65762
|
retained = new Map;
|
|
65421
65763
|
retainedTransitions = Promise.resolve();
|
|
@@ -65608,18 +65950,15 @@ class PoolCoordinator {
|
|
|
65608
65950
|
try {
|
|
65609
65951
|
const { survivors, unprobeable } = await reapProcessGroups(this.journal, this.root, { attempt: work });
|
|
65610
65952
|
if (survivors.length > 0) {
|
|
65611
|
-
this.quarantined.add(work.id);
|
|
65612
65953
|
this.unreaped.push({ attempt: work });
|
|
65613
|
-
this.
|
|
65614
|
-
return;
|
|
65954
|
+
this.reportHealth(undefined, `${unconfirmedCleanup(survivors).message}; child ${work.childRunId} of an earlier incarnation replays`);
|
|
65615
65955
|
}
|
|
65616
65956
|
if (unprobeable.length === 0)
|
|
65617
65957
|
await this.releaseRetained(work);
|
|
65618
65958
|
} catch (error) {
|
|
65619
65959
|
this.cleanupFailure ??= error instanceof Error ? error : new Error(String(error));
|
|
65620
65960
|
} finally {
|
|
65621
|
-
|
|
65622
|
-
this.held.delete(work.id);
|
|
65961
|
+
this.held.delete(work.id);
|
|
65623
65962
|
}
|
|
65624
65963
|
}
|
|
65625
65964
|
releaseRetained(work) {
|
|
@@ -65750,6 +66089,7 @@ class PoolCoordinator {
|
|
|
65750
66089
|
});
|
|
65751
66090
|
}
|
|
65752
66091
|
fail(error) {
|
|
66092
|
+
this.failed = true;
|
|
65753
66093
|
this.port.send({ kind: "failure", error: error.message });
|
|
65754
66094
|
}
|
|
65755
66095
|
failWork(work, error) {
|
|
@@ -66117,7 +66457,7 @@ class PoolCoordinator {
|
|
|
66117
66457
|
this.slots.add(slot);
|
|
66118
66458
|
let ready = false;
|
|
66119
66459
|
let serial = Promise.resolve();
|
|
66120
|
-
const logPath =
|
|
66460
|
+
const logPath = join30(this.logDirectory, `${worker}.log`);
|
|
66121
66461
|
this.logs.set(child, logPath);
|
|
66122
66462
|
let logFailure;
|
|
66123
66463
|
const log = createWriteStream(logPath, { flags: "a" });
|
|
@@ -66158,7 +66498,11 @@ class PoolCoordinator {
|
|
|
66158
66498
|
terminate();
|
|
66159
66499
|
});
|
|
66160
66500
|
const closed = new Promise((resolve) => child.once("close", () => resolve()));
|
|
66161
|
-
|
|
66501
|
+
let retired = false;
|
|
66502
|
+
const retire = () => {
|
|
66503
|
+
if (retired)
|
|
66504
|
+
return;
|
|
66505
|
+
retired = true;
|
|
66162
66506
|
Promise.race([closed, delay2(STDERR_DRAIN_GRACE_MS)]).then(() => reject(diagnosed(timedOut ? startupTimeoutMessage(startupTimeoutMs) : "Local worker exited before readiness")));
|
|
66163
66507
|
this.children.delete(child);
|
|
66164
66508
|
this.logs.delete(child);
|
|
@@ -66169,6 +66513,7 @@ class PoolCoordinator {
|
|
|
66169
66513
|
this.readyWorkers.delete(worker);
|
|
66170
66514
|
this.controlAcks.delete(worker);
|
|
66171
66515
|
this.lastHeard.delete(worker);
|
|
66516
|
+
this.awaiting.delete(worker);
|
|
66172
66517
|
this.slots.delete(slot);
|
|
66173
66518
|
this.endStageWatches((watch) => watch.slot === slot, "worker_exited");
|
|
66174
66519
|
for (const settle of slot.stageCommands.values())
|
|
@@ -66182,7 +66527,7 @@ class PoolCoordinator {
|
|
|
66182
66527
|
const held = slot.work;
|
|
66183
66528
|
if (held)
|
|
66184
66529
|
this.held.add(held.id);
|
|
66185
|
-
let
|
|
66530
|
+
let unconfirmed;
|
|
66186
66531
|
const cleanup = disconnected.then(() => serial).then(async () => {
|
|
66187
66532
|
try {
|
|
66188
66533
|
const admission = this.admission;
|
|
@@ -66195,18 +66540,13 @@ class PoolCoordinator {
|
|
|
66195
66540
|
throw unconfirmedCleanup(alive);
|
|
66196
66541
|
if (alive.length === 0)
|
|
66197
66542
|
return;
|
|
66198
|
-
|
|
66199
|
-
|
|
66200
|
-
this.quarantined.add(held.id);
|
|
66201
|
-
slot.work = undefined;
|
|
66202
|
-
childFailed = true;
|
|
66203
|
-
this.port.send({ kind: "work-failure", workId: held.id, error: error.message });
|
|
66204
|
-
}
|
|
66543
|
+
unconfirmed = unconfirmedCleanup(alive);
|
|
66544
|
+
this.reportHealth(logPath, `${unconfirmed.message}; the worker was retired and replaced`);
|
|
66205
66545
|
} catch (error) {
|
|
66206
66546
|
this.cleanupFailure = error instanceof Error ? error : new Error(String(error));
|
|
66207
66547
|
throw this.cleanupFailure;
|
|
66208
66548
|
} finally {
|
|
66209
|
-
if (held
|
|
66549
|
+
if (held)
|
|
66210
66550
|
this.held.delete(held.id);
|
|
66211
66551
|
}
|
|
66212
66552
|
}).then(async () => {
|
|
@@ -66227,8 +66567,8 @@ class PoolCoordinator {
|
|
|
66227
66567
|
const settled = attempt !== undefined && await this.whileRootLate(queue, () => exhausted ? queue.failExited(lease, attempt) : queue.releaseExited(lease, attempt));
|
|
66228
66568
|
const superseded = work !== undefined && !settled && await this.superseded(work);
|
|
66229
66569
|
if (work !== undefined && exhausted && !superseded)
|
|
66230
|
-
this.failWork(work, diagnosed(childRetryLimit(work, "lost its worker")));
|
|
66231
|
-
if (work === undefined &&
|
|
66570
|
+
this.failWork(work, diagnosed(`${childRetryLimit(work, "lost its worker")}${unconfirmed === undefined ? "" : `; ${unconfirmed.message}`}`));
|
|
66571
|
+
if (work === undefined && ++this.replacements > 3)
|
|
66232
66572
|
this.fail(diagnosed("Local worker retry limit"));
|
|
66233
66573
|
else
|
|
66234
66574
|
await this.spawn();
|
|
@@ -66237,7 +66577,9 @@ class PoolCoordinator {
|
|
|
66237
66577
|
});
|
|
66238
66578
|
this.cleanups.add(cleanup);
|
|
66239
66579
|
cleanup.finally(() => this.cleanups.delete(cleanup));
|
|
66240
|
-
}
|
|
66580
|
+
};
|
|
66581
|
+
slot.retire = retire;
|
|
66582
|
+
child.once("exit", retire);
|
|
66241
66583
|
child.on("message", (message) => {
|
|
66242
66584
|
this.observeResponsiveness();
|
|
66243
66585
|
this.lastHeard.set(worker, { at: performance.now(), stalled: this.stalledMs });
|
|
@@ -66311,10 +66653,11 @@ class PoolCoordinator {
|
|
|
66311
66653
|
const fenced = slot.work;
|
|
66312
66654
|
slot.work = undefined;
|
|
66313
66655
|
if (fenced && !await this.superseded(fenced)) {
|
|
66656
|
+
this.fence(fenced);
|
|
66314
66657
|
if (this.attempts(fenced) < MAX_CHILD_ATTEMPTS)
|
|
66315
66658
|
await leased(() => queue.releaseExited(lease, fenced));
|
|
66316
66659
|
else if (await leased(() => queue.failExited(lease, fenced)))
|
|
66317
|
-
this.failWork(fenced, new Error(childRetryLimit(fenced, "was refused by the root as stale on every attempt")));
|
|
66660
|
+
this.failWork(fenced, new Error(childRetryLimit(fenced, message.fault === undefined ? "was refused by the root as stale on every attempt" : `failed on a fault of the pool on every attempt (last: ${message.fault})`)));
|
|
66318
66661
|
}
|
|
66319
66662
|
return;
|
|
66320
66663
|
}
|
|
@@ -66428,8 +66771,19 @@ class PoolCoordinator {
|
|
|
66428
66771
|
await this.sendResult(work.id);
|
|
66429
66772
|
});
|
|
66430
66773
|
const response = handled.then((value2) => routeReady ?? value2);
|
|
66431
|
-
if (message.kind !== "delivery" && message.kind !== "delivery-ready")
|
|
66432
|
-
|
|
66774
|
+
if (message.kind !== "delivery" && message.kind !== "delivery-ready") {
|
|
66775
|
+
this.awaiting.set(worker, (this.awaiting.get(worker) ?? 0) + 1);
|
|
66776
|
+
const answered = () => {
|
|
66777
|
+
const left = (this.awaiting.get(worker) ?? 1) - 1;
|
|
66778
|
+
if (left > 0)
|
|
66779
|
+
this.awaiting.set(worker, left);
|
|
66780
|
+
else
|
|
66781
|
+
this.awaiting.delete(worker);
|
|
66782
|
+
if (this.incarnations.get(worker) === child)
|
|
66783
|
+
this.lastHeard.set(worker, { at: performance.now(), stalled: this.stalledMs });
|
|
66784
|
+
};
|
|
66785
|
+
serial = handled.then(answered, answered);
|
|
66786
|
+
}
|
|
66433
66787
|
response.then((value2) => child.send({ id: message.id, value: value2 }, replied), (error) => {
|
|
66434
66788
|
const rootLeaseLost = isRootLeaseLost(error);
|
|
66435
66789
|
if (rootLeaseLost)
|
|
@@ -66475,8 +66829,6 @@ class PoolCoordinator {
|
|
|
66475
66829
|
return;
|
|
66476
66830
|
}
|
|
66477
66831
|
for (const work of expired) {
|
|
66478
|
-
if (this.quarantined.has(work.id))
|
|
66479
|
-
continue;
|
|
66480
66832
|
if (this.held.has(work.id)) {
|
|
66481
66833
|
await queue.extend(lease, work, leaseMs).catch((error) => {
|
|
66482
66834
|
if (error.message !== STALE_WORK_LEASE)
|
|
@@ -66492,16 +66844,19 @@ class PoolCoordinator {
|
|
|
66492
66844
|
});
|
|
66493
66845
|
continue;
|
|
66494
66846
|
}
|
|
66495
|
-
if (child && !await this.stop(child)
|
|
66496
|
-
|
|
66497
|
-
if (this.held.has(work.id)
|
|
66847
|
+
if (child && !await this.stop(child))
|
|
66848
|
+
this.retireUnconfirmed(child);
|
|
66849
|
+
if (this.held.has(work.id))
|
|
66498
66850
|
continue;
|
|
66499
66851
|
if ((await unconfirmedProcessGroups(this.journal, root, { attempt: work })).length > 0)
|
|
66500
66852
|
continue;
|
|
66501
66853
|
if (work.epoch < lease.epoch)
|
|
66502
66854
|
await this.releaseRetained(work);
|
|
66503
66855
|
else
|
|
66504
|
-
await queue.expire(lease, work)
|
|
66856
|
+
await queue.expire(lease, work).catch((error) => {
|
|
66857
|
+
if (error.message !== STALE_WORK_LEASE)
|
|
66858
|
+
throw error;
|
|
66859
|
+
});
|
|
66505
66860
|
}
|
|
66506
66861
|
})().finally(() => {
|
|
66507
66862
|
this.recovering = undefined;
|
|
@@ -66519,19 +66874,51 @@ class PoolCoordinator {
|
|
|
66519
66874
|
isLive(worker, child) {
|
|
66520
66875
|
this.observeResponsiveness();
|
|
66521
66876
|
const heard = this.lastHeard.get(worker);
|
|
66522
|
-
return child.exitCode === null && child.signalCode === null && child.connected && heard !== undefined && performance.now() - heard.at - (this.stalledMs - heard.stalled) < this.options.leaseMs;
|
|
66877
|
+
return child.exitCode === null && child.signalCode === null && child.connected && heard !== undefined && ((this.awaiting.get(worker) ?? 0) > 0 || performance.now() - heard.at - (this.stalledMs - heard.stalled) < this.options.leaseMs);
|
|
66523
66878
|
}
|
|
66524
|
-
stop(child) {
|
|
66525
|
-
|
|
66526
|
-
|
|
66527
|
-
|
|
66528
|
-
|
|
66529
|
-
|
|
66530
|
-
|
|
66531
|
-
|
|
66532
|
-
|
|
66533
|
-
|
|
66534
|
-
|
|
66879
|
+
async stop(child) {
|
|
66880
|
+
const pid = child.pid;
|
|
66881
|
+
const exited = () => child.exitCode !== null || child.signalCode !== null;
|
|
66882
|
+
if (pid === undefined || exited())
|
|
66883
|
+
return true;
|
|
66884
|
+
const exit = new Promise((resolve) => child.once("exit", () => resolve()));
|
|
66885
|
+
const confirmed = async () => {
|
|
66886
|
+
await Promise.race([exit, delay2(WORKER_EXIT_CONFIRMATION_MS)]);
|
|
66887
|
+
return true;
|
|
66888
|
+
};
|
|
66889
|
+
this.observeResponsiveness();
|
|
66890
|
+
child.kill("SIGKILL");
|
|
66891
|
+
const since = performance.now();
|
|
66892
|
+
const stalledAt = this.stalledMs;
|
|
66893
|
+
for (;; ) {
|
|
66894
|
+
await Promise.race([exit, delay2(WORKER_EXIT_POLL_MS)]);
|
|
66895
|
+
if (exited())
|
|
66896
|
+
return true;
|
|
66897
|
+
if (workerProcessExited(pid))
|
|
66898
|
+
return confirmed();
|
|
66899
|
+
this.observeResponsiveness();
|
|
66900
|
+
if (performance.now() - since - (this.stalledMs - stalledAt) >= WORKER_EXIT_CONFIRMATION_MS)
|
|
66901
|
+
return exited() || workerProcessExited(pid, true) && confirmed();
|
|
66902
|
+
}
|
|
66903
|
+
}
|
|
66904
|
+
retireUnconfirmed(child) {
|
|
66905
|
+
if (this.unconfirmedExits.has(child))
|
|
66906
|
+
return;
|
|
66907
|
+
this.unconfirmedExits.add(child);
|
|
66908
|
+
this.reportHealth(this.logs.get(child), `Local worker cleanup was not confirmed: worker process ${child.pid} was still running ${WORKER_EXIT_CONFIRMATION_MS} ms after SIGKILL; it was retired and replaced`);
|
|
66909
|
+
if (child.connected)
|
|
66910
|
+
child.disconnect();
|
|
66911
|
+
for (const slot of this.slots)
|
|
66912
|
+
if (slot.child === child)
|
|
66913
|
+
slot.retire?.();
|
|
66914
|
+
}
|
|
66915
|
+
reportHealth(log, message) {
|
|
66916
|
+
if (log !== undefined) {
|
|
66917
|
+
this.keptLogs.add(log);
|
|
66918
|
+
appendFile(log, `[atomic ${new Date().toISOString()}] ${message}
|
|
66919
|
+
`).catch(() => {});
|
|
66920
|
+
}
|
|
66921
|
+
this.port.send({ kind: "health", message: log === undefined ? message : `${message} (worker log: ${log})` });
|
|
66535
66922
|
}
|
|
66536
66923
|
async close() {
|
|
66537
66924
|
this.closed = true;
|
|
@@ -66541,10 +66928,15 @@ class PoolCoordinator {
|
|
|
66541
66928
|
this.rateLimitOutbox.close();
|
|
66542
66929
|
let failure;
|
|
66543
66930
|
let stopping = [];
|
|
66931
|
+
let confirmed = [];
|
|
66544
66932
|
try {
|
|
66545
66933
|
await this.recovering?.catch(() => {});
|
|
66546
66934
|
stopping = [...this.children].map((child) => ({ child, log: this.logs.get(child) }));
|
|
66547
|
-
await Promise.all(stopping.map(({ child }) => this.stop(child)));
|
|
66935
|
+
confirmed = await Promise.all(stopping.map(({ child }) => this.stop(child)));
|
|
66936
|
+
stopping.forEach(({ child }, index) => {
|
|
66937
|
+
if (!confirmed[index])
|
|
66938
|
+
this.retireUnconfirmed(child);
|
|
66939
|
+
});
|
|
66548
66940
|
await Promise.all([...this.cleanups]);
|
|
66549
66941
|
if (this.cleanupFailure)
|
|
66550
66942
|
throw this.cleanupFailure;
|
|
@@ -66556,15 +66948,15 @@ class PoolCoordinator {
|
|
|
66556
66948
|
if (alive.length > 0)
|
|
66557
66949
|
throw unconfirmedCleanup(alive);
|
|
66558
66950
|
if (this.desiredControl !== "quit" && this.queue !== undefined && this.lease !== undefined)
|
|
66559
|
-
await this.queue.releaseClosed(this.lease
|
|
66951
|
+
await this.queue.releaseClosed(this.lease).catch((error) => {
|
|
66560
66952
|
if (!(error instanceof StaleLeaseError))
|
|
66561
66953
|
throw error;
|
|
66562
66954
|
});
|
|
66563
66955
|
} catch (error) {
|
|
66564
66956
|
failure = error;
|
|
66565
66957
|
}
|
|
66566
|
-
if (failure === undefined) {
|
|
66567
|
-
const stopped = stopping.flatMap(({
|
|
66958
|
+
if (failure === undefined && !this.failed) {
|
|
66959
|
+
const stopped = stopping.flatMap(({ log }, index) => log !== undefined && !this.keptLogs.has(log) && confirmed[index] === true ? [log] : []);
|
|
66568
66960
|
await Promise.all(stopped.map((log) => rm3(log, { force: true }).catch(() => {})));
|
|
66569
66961
|
if (this.logDirectory !== undefined)
|
|
66570
66962
|
await rmdir(this.logDirectory).catch(() => {});
|
|
@@ -66616,7 +67008,7 @@ function runPoolCoordinator() {
|
|
|
66616
67008
|
port.onClose(() => process.exit(0));
|
|
66617
67009
|
return coordinator;
|
|
66618
67010
|
}
|
|
66619
|
-
var CONTROL_ACKNOWLEDGEMENT_STALL_MS = 5000, RECOVERY_INTERVAL_MS = 500, TICK_INTERVAL_MS = 100, STALL_THRESHOLD_MS = 500, COORDINATOR_LEASE_MS, LATE_ROOT_POLL_MS, MIRROR_INTERVAL_MS = 1e4, STDERR_TAIL_CHARACTERS = 8192, STDERR_DRAIN_GRACE_MS = 1000, MAX_CHILD_ATTEMPTS = 3, ROUTE_REGISTRATION_WAIT_MS = 120000, STAGE_WATCH_BACKLOG_EVENTS = 2000, STAGE_WATCH_BACKLOG_BYTES, PENDING_STAGE_MESSAGE_EVENT2 = "atomic:workflow-pending-stage-message", STICKY_LIVE_DELIVERED_EVENT2 = "atomic:workflow-sticky-live-delivered", PENDING_STAGE_UNDELIVERABLE_EVENT2 = "atomic:workflow-pending-stage-undeliverable", ENDED_WATCHES_REMEMBERED = 1024;
|
|
67011
|
+
var CONTROL_ACKNOWLEDGEMENT_STALL_MS = 5000, RECOVERY_INTERVAL_MS = 500, TICK_INTERVAL_MS = 100, STALL_THRESHOLD_MS = 500, COORDINATOR_LEASE_MS, LATE_ROOT_POLL_MS, MIRROR_INTERVAL_MS = 1e4, STDERR_TAIL_CHARACTERS = 8192, STDERR_DRAIN_GRACE_MS = 1000, WORKER_EXIT_CONFIRMATION_MS = 5000, WORKER_EXIT_POLL_MS = 50, MAX_CHILD_ATTEMPTS = 3, ROUTE_REGISTRATION_WAIT_MS = 120000, STAGE_WATCH_BACKLOG_EVENTS = 2000, STAGE_WATCH_BACKLOG_BYTES, PENDING_STAGE_MESSAGE_EVENT2 = "atomic:workflow-pending-stage-message", STICKY_LIVE_DELIVERED_EVENT2 = "atomic:workflow-sticky-live-delivered", PENDING_STAGE_UNDELIVERABLE_EVENT2 = "atomic:workflow-pending-stage-undeliverable", ENDED_WATCHES_REMEMBERED = 1024;
|
|
66620
67012
|
var init_pool_coordinator = __esm(() => {
|
|
66621
67013
|
init_internal();
|
|
66622
67014
|
init_pool_remote_journal();
|
|
@@ -66631,6 +67023,7 @@ var init_pool_coordinator = __esm(() => {
|
|
|
66631
67023
|
init_pending_stage_route_readiness();
|
|
66632
67024
|
init_store();
|
|
66633
67025
|
init_workflow_infrastructure_error();
|
|
67026
|
+
init_admission_host();
|
|
66634
67027
|
init_pool_journal();
|
|
66635
67028
|
init_pooled_watch_trace();
|
|
66636
67029
|
init_resource_admission();
|
|
@@ -66667,26 +67060,6 @@ function isWorkflowPendingStageDeliveryFailure(error) {
|
|
|
66667
67060
|
return true;
|
|
66668
67061
|
return error?.code === "pending_stage_delivery_failed";
|
|
66669
67062
|
}
|
|
66670
|
-
function untilDrainedOrStalled(drain) {
|
|
66671
|
-
let lastProgressAt = Date.now();
|
|
66672
|
-
const running = drain(() => {
|
|
66673
|
-
lastProgressAt = Date.now();
|
|
66674
|
-
});
|
|
66675
|
-
return new Promise((resolve, reject) => {
|
|
66676
|
-
let timer;
|
|
66677
|
-
const check = () => {
|
|
66678
|
-
const remaining = lastProgressAt + PENDING_STAGE_READY_STALL_MS - Date.now();
|
|
66679
|
-
if (remaining <= 0) {
|
|
66680
|
-
resolve();
|
|
66681
|
-
return;
|
|
66682
|
-
}
|
|
66683
|
-
timer = setTimeout(check, remaining);
|
|
66684
|
-
timer.unref?.();
|
|
66685
|
-
};
|
|
66686
|
-
running.then(resolve, reject).finally(() => clearTimeout(timer));
|
|
66687
|
-
check();
|
|
66688
|
-
});
|
|
66689
|
-
}
|
|
66690
67063
|
function createWorkflowPendingStageDelivery(activeStore, runId, stageId, stageName, commit = (action) => action(), options = {}) {
|
|
66691
67064
|
let resolveReady;
|
|
66692
67065
|
let rejectReady;
|
|
@@ -66879,7 +67252,7 @@ function createWorkflowPendingStageDelivery(activeStore, runId, stageId, stageNa
|
|
|
66879
67252
|
};
|
|
66880
67253
|
}
|
|
66881
67254
|
async function deliverPendingStageMessages(activeStore, runId, stageId, stageName, deliver, commit, recipient) {
|
|
66882
|
-
const stickyEntries = stickyPendingStageEntriesForStage(activeStore, runId, stageId, stageName, recipient);
|
|
67255
|
+
const stickyEntries = stickyPendingStageEntriesForStage(activeStore, runId, stageId, stageName, recipient, recipient?.receivedMessageIds);
|
|
66883
67256
|
const candidateIds = new Set([
|
|
66884
67257
|
...activeStore.pendingStageMessagesFor(runId, stageId),
|
|
66885
67258
|
...activeStore.pendingStageMessagesFor(runId, stageName),
|
|
@@ -66935,7 +67308,7 @@ async function deliverPendingStageMessages(activeStore, runId, stageId, stageNam
|
|
|
66935
67308
|
}
|
|
66936
67309
|
}
|
|
66937
67310
|
}
|
|
66938
|
-
function stickyPendingStageEntriesForStage(activeStore, runId, stageId, stageName, recipient) {
|
|
67311
|
+
function stickyPendingStageEntriesForStage(activeStore, runId, stageId, stageName, recipient, receivedByEndedStage = []) {
|
|
66939
67312
|
const runs = activeStore.runs();
|
|
66940
67313
|
const rootRunId = durableRootRunIdForRun(runs, runId);
|
|
66941
67314
|
if (rootRunId === undefined)
|
|
@@ -66944,22 +67317,28 @@ function stickyPendingStageEntriesForStage(activeStore, runId, stageId, stageNam
|
|
|
66944
67317
|
if (rootRun === undefined)
|
|
66945
67318
|
return [];
|
|
66946
67319
|
const stage = runs.find((run) => run.id === runId)?.stages.find((candidate) => candidate.id === stageId);
|
|
66947
|
-
|
|
67320
|
+
const ended = stage?.status === "completed" || stage?.status === "skipped";
|
|
67321
|
+
if (ended && receivedByEndedStage.length === 0)
|
|
66948
67322
|
return [];
|
|
66949
67323
|
const hops = workflowBoundaryHops(runs, runId);
|
|
66950
67324
|
if (hops === undefined)
|
|
66951
67325
|
return [];
|
|
66952
67326
|
return (rootRun.pendingStageMessages ?? []).filter((entry) => {
|
|
66953
|
-
if (entry.sticky !== true || entry.status !== "queued")
|
|
66954
|
-
return false;
|
|
66955
|
-
const parsed = entry.targetPath === undefined ? undefined : parseWorkflowStageTarget(entry.targetPath);
|
|
66956
|
-
if (parsed === undefined || parsed.rootRunId !== rootRunId)
|
|
66957
|
-
return false;
|
|
66958
67327
|
if ((entry.deliveries ?? []).some((delivery) => delivery.runId === runId && delivery.stageId === stageId && (recipient === undefined || delivery.admission === "context" && delivery.sessionId === recipient.sessionId && (recipient.receivedMessageIds === undefined || recipient.receivedMessageIds.includes(entry.message.id)))))
|
|
66959
67328
|
return false;
|
|
66960
|
-
|
|
67329
|
+
if (ended && !receivedByEndedStage.includes(entry.message.id))
|
|
67330
|
+
return false;
|
|
67331
|
+
return stickyEntryTargetsStage(entry, rootRunId, hops, stageId, stageName);
|
|
66961
67332
|
});
|
|
66962
67333
|
}
|
|
67334
|
+
function stickyEntryTargetsStage(entry, rootRunId, hops, stageId, stageName) {
|
|
67335
|
+
if (entry.sticky !== true || entry.status !== "queued")
|
|
67336
|
+
return false;
|
|
67337
|
+
const parsed = entry.targetPath === undefined ? undefined : parseWorkflowStageTarget(entry.targetPath);
|
|
67338
|
+
if (parsed === undefined || parsed.rootRunId !== rootRunId)
|
|
67339
|
+
return false;
|
|
67340
|
+
return stageMatchesPathPattern(parsed.segments, hops, [stageId, stageName]);
|
|
67341
|
+
}
|
|
66963
67342
|
function claimPendingDelivery(store, runId, stageKey, messageId) {
|
|
66964
67343
|
let claims = pendingDeliveryClaims.get(store);
|
|
66965
67344
|
if (claims === undefined) {
|
|
@@ -67023,6 +67402,7 @@ class PoolStageHandoff {
|
|
|
67023
67402
|
owners = new Map;
|
|
67024
67403
|
publications = new Map;
|
|
67025
67404
|
drains = new Map;
|
|
67405
|
+
deliveryIssues = 0;
|
|
67026
67406
|
constructor(backend, store, queue, lease, claimRun) {
|
|
67027
67407
|
this.backend = backend;
|
|
67028
67408
|
this.store = store;
|
|
@@ -67103,11 +67483,23 @@ class PoolStageHandoff {
|
|
|
67103
67483
|
this.drains.set(stage, drain);
|
|
67104
67484
|
try {
|
|
67105
67485
|
await drain;
|
|
67486
|
+
} catch (error) {
|
|
67487
|
+
const current = this.attempts.get(work.id) === attempt && !attempt.fenced;
|
|
67488
|
+
if (current && attempt.sessions.get(stage) === recipient.sessionId && error.message !== STALE)
|
|
67489
|
+
this.recordDeliveryIssue(error);
|
|
67490
|
+
throw error;
|
|
67106
67491
|
} finally {
|
|
67107
67492
|
if (this.drains.get(stage) === drain)
|
|
67108
67493
|
this.drains.delete(stage);
|
|
67109
67494
|
}
|
|
67110
67495
|
}
|
|
67496
|
+
recordDeliveryIssue(error) {
|
|
67497
|
+
this.deliveryIssues++;
|
|
67498
|
+
const drains = this.deliveryIssues === 1 ? "1 stage drain" : `${this.deliveryIssues} stage drains`;
|
|
67499
|
+
this.store.recordRunExecutionState(this.lease.runId, {
|
|
67500
|
+
intercomDeliveryIssue: `${drains} on local workers failed; undelivered messages stay queued and arrive live or on the stage's next drain (last: ${error.message})`
|
|
67501
|
+
});
|
|
67502
|
+
}
|
|
67111
67503
|
async complete(work) {
|
|
67112
67504
|
const root = this.lease.runId;
|
|
67113
67505
|
const result = await this.queue.result(root, work.id);
|
|
@@ -68463,7 +68855,7 @@ var init_pooled_stage_registry = __esm(() => {
|
|
|
68463
68855
|
// dist/builtin/workflows/src/workers/local-pool.ts
|
|
68464
68856
|
import { fork as fork2 } from "node:child_process";
|
|
68465
68857
|
import { randomUUID as randomUUID18 } from "node:crypto";
|
|
68466
|
-
import { resolve as
|
|
68858
|
+
import { resolve as resolve14 } from "node:path";
|
|
68467
68859
|
import { setTimeout as delay3 } from "node:timers/promises";
|
|
68468
68860
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
68469
68861
|
import {
|
|
@@ -68581,6 +68973,7 @@ class LocalWorkerPool {
|
|
|
68581
68973
|
works = new Map;
|
|
68582
68974
|
attempts = new Map;
|
|
68583
68975
|
results = new Map;
|
|
68976
|
+
healthEvents = [];
|
|
68584
68977
|
workFailures = new Map;
|
|
68585
68978
|
changed = Promise.withResolvers();
|
|
68586
68979
|
waiting = 0;
|
|
@@ -68638,6 +69031,9 @@ class LocalWorkerPool {
|
|
|
68638
69031
|
if (options.admission && options.rootId === undefined)
|
|
68639
69032
|
workflowResourceAdmission.set(backend, options.admission);
|
|
68640
69033
|
}
|
|
69034
|
+
get health() {
|
|
69035
|
+
return this.healthEvents;
|
|
69036
|
+
}
|
|
68641
69037
|
get workerPids() {
|
|
68642
69038
|
return [...this.pids];
|
|
68643
69039
|
}
|
|
@@ -68693,7 +69089,7 @@ class LocalWorkerPool {
|
|
|
68693
69089
|
async poolJournal() {
|
|
68694
69090
|
const journal = this.backend.journal;
|
|
68695
69091
|
if (this.options.durable === undefined) {
|
|
68696
|
-
if (!(journal instanceof SqlRunJournal) ||
|
|
69092
|
+
if (!(journal instanceof SqlRunJournal) || resolve14(this.options.journalPath) !== resolve14(journal.path) || sqlPoolStorage(journal)?.tables.runs !== "wf_runs")
|
|
68697
69093
|
throw new Error("Local workers require the same default-prefix SQLite journal");
|
|
68698
69094
|
return journal;
|
|
68699
69095
|
}
|
|
@@ -69231,14 +69627,8 @@ class LocalWorkerPool {
|
|
|
69231
69627
|
try {
|
|
69232
69628
|
return await this.dispatch(definition, inputs, options);
|
|
69233
69629
|
} catch (error) {
|
|
69234
|
-
if (
|
|
69235
|
-
|
|
69236
|
-
try {
|
|
69237
|
-
if (this.desiredControl !== "quit" || !findWorkflowGracefulQuit(error))
|
|
69238
|
-
await this.shutdown();
|
|
69239
|
-
} catch (closeError) {
|
|
69240
|
-
throw asWorkflowInfrastructureError(closeError) ?? closeError;
|
|
69241
|
-
}
|
|
69630
|
+
if (this.failure !== undefined || this.coordinatorLost !== undefined)
|
|
69631
|
+
await this.shutdown().catch(() => {});
|
|
69242
69632
|
throw asWorkflowInfrastructureError(error) ?? error;
|
|
69243
69633
|
}
|
|
69244
69634
|
}
|
|
@@ -69306,8 +69696,13 @@ class LocalWorkerPool {
|
|
|
69306
69696
|
try {
|
|
69307
69697
|
for (;; ) {
|
|
69308
69698
|
options.signal?.throwIfAborted();
|
|
69309
|
-
if (this.closed)
|
|
69310
|
-
|
|
69699
|
+
if (!this.results.has(work.id) && (this.closed || this.failure !== undefined)) {
|
|
69700
|
+
const recorded = await this.queue?.result(root, work.id).catch(() => {
|
|
69701
|
+
return;
|
|
69702
|
+
});
|
|
69703
|
+
if (recorded !== undefined)
|
|
69704
|
+
this.results.set(work.id, recorded);
|
|
69705
|
+
}
|
|
69311
69706
|
const result = this.results.get(work.id);
|
|
69312
69707
|
if (result) {
|
|
69313
69708
|
this.completing++;
|
|
@@ -69331,6 +69726,8 @@ class LocalWorkerPool {
|
|
|
69331
69726
|
this.endFailedChild(work, failed);
|
|
69332
69727
|
throw failed;
|
|
69333
69728
|
}
|
|
69729
|
+
if (this.closed)
|
|
69730
|
+
throw this.closedError(root);
|
|
69334
69731
|
if (this.failure)
|
|
69335
69732
|
throw this.failure;
|
|
69336
69733
|
if (this.budgetBlocked)
|
|
@@ -69365,6 +69762,19 @@ class LocalWorkerPool {
|
|
|
69365
69762
|
if (this.reconnectingViews.size > 0 || this.outboxes.size > 0)
|
|
69366
69763
|
this.reconnectStageViews();
|
|
69367
69764
|
}
|
|
69765
|
+
recordHealth(message) {
|
|
69766
|
+
this.healthEvents.push(message);
|
|
69767
|
+
const root = this.root;
|
|
69768
|
+
if (root === undefined || this.store === undefined)
|
|
69769
|
+
return;
|
|
69770
|
+
this.store.recordNotice({
|
|
69771
|
+
id: `local-worker-health:${root}:${this.healthEvents.length}`,
|
|
69772
|
+
runId: root,
|
|
69773
|
+
level: "warning",
|
|
69774
|
+
message: `Local worker pool: ${message}`,
|
|
69775
|
+
createdAt: Date.now()
|
|
69776
|
+
});
|
|
69777
|
+
}
|
|
69368
69778
|
evaluateBudget() {
|
|
69369
69779
|
const decision = this.rootBudget?.checkpoint();
|
|
69370
69780
|
const blocked = decision?.kind === "exhausted" || decision?.kind === "wrap_up";
|
|
@@ -69483,6 +69893,9 @@ class LocalWorkerPool {
|
|
|
69483
69893
|
this.failure ??= new Error(event.error);
|
|
69484
69894
|
this.signalChange();
|
|
69485
69895
|
return;
|
|
69896
|
+
case "health":
|
|
69897
|
+
this.recordHealth(event.message);
|
|
69898
|
+
return;
|
|
69486
69899
|
case "stage-events":
|
|
69487
69900
|
this.stageWatches.get(event.watch)?.({ kind: "events", events: event.events });
|
|
69488
69901
|
this.notice({ kind: "stage-events-applied", watch: event.watch });
|
|
@@ -69703,7 +70116,7 @@ var init_local_pool = __esm(() => {
|
|
|
69703
70116
|
var DEFAULT_POOLED_STAGE_START_MAX_LOAD_PER_CPU = 3;
|
|
69704
70117
|
|
|
69705
70118
|
// dist/builtin/workflows/src/workers/public-pool.ts
|
|
69706
|
-
import { resolve as
|
|
70119
|
+
import { resolve as resolve15 } from "node:path";
|
|
69707
70120
|
import { isDeepStrictEqual as isDeepStrictEqual2 } from "node:util";
|
|
69708
70121
|
async function restoreWorkerOptions(backend, options) {
|
|
69709
70122
|
const source = options.continuation?.source.id ?? options.runId;
|
|
@@ -69753,16 +70166,16 @@ async function activatePublicPool(backend, options) {
|
|
|
69753
70166
|
startupTimeoutMs: undefined,
|
|
69754
70167
|
leaseMs: undefined,
|
|
69755
70168
|
maxLoadPerCpu: undefined,
|
|
69756
|
-
definitions: options.localWorkers.definitions.map((source) => ({ ...source, path:
|
|
70169
|
+
definitions: options.localWorkers.definitions.map((source) => ({ ...source, path: resolve15(cwd, source.path) }))
|
|
69757
70170
|
};
|
|
69758
70171
|
const admission = options.admission && {
|
|
69759
70172
|
...options.admission,
|
|
69760
|
-
...options.admission.userDatabase ? { userDatabase:
|
|
70173
|
+
...options.admission.userDatabase ? { userDatabase: resolve15(cwd, options.admission.userDatabase) } : {}
|
|
69761
70174
|
};
|
|
69762
70175
|
if (durable !== undefined && admission?.userLimits) {
|
|
69763
|
-
if (admission.userDatabase !== undefined && admission.userDatabase !==
|
|
69764
|
-
throw new Error(`admission.userDatabase must be omitted, or ${
|
|
69765
|
-
admission.userDatabase =
|
|
70176
|
+
if (admission.userDatabase !== undefined && admission.userDatabase !== resolve15(poolPath))
|
|
70177
|
+
throw new Error(`admission.userDatabase must be omitted, or ${resolve15(poolPath)}, for a journal that is not a local SQLite file`);
|
|
70178
|
+
admission.userDatabase = resolve15(poolPath);
|
|
69766
70179
|
}
|
|
69767
70180
|
const sources = localWorkers && await Promise.all(localWorkers.definitions.map(async (source) => ({ ...source, digest: await definitionDigest(source.path) })));
|
|
69768
70181
|
const resources = JSON.parse(JSON.stringify({
|
|
@@ -70368,6 +70781,7 @@ function createTaskPrimitive(runtime) {
|
|
|
70368
70781
|
}
|
|
70369
70782
|
})();
|
|
70370
70783
|
const stageMeta = stage.__modelFallbackMeta();
|
|
70784
|
+
stage.__releaseIfIdle?.();
|
|
70371
70785
|
return {
|
|
70372
70786
|
name,
|
|
70373
70787
|
stageName: name,
|
|
@@ -71166,7 +71580,7 @@ function hasUsableContentBlock(block) {
|
|
|
71166
71580
|
|
|
71167
71581
|
// dist/builtin/workflows/src/shared/stage-session-store.ts
|
|
71168
71582
|
import { randomUUID as randomUUID20 } from "node:crypto";
|
|
71169
|
-
import { dirname as
|
|
71583
|
+
import { dirname as dirname15, resolve as resolve16 } from "node:path";
|
|
71170
71584
|
import {
|
|
71171
71585
|
JsonlSessionStore,
|
|
71172
71586
|
resolveSessionStore,
|
|
@@ -71202,11 +71616,11 @@ async function forkStageSession(store, sessionId, cwd, options, destination = st
|
|
|
71202
71616
|
async function migrateLegacyStageSession(store, sessionId, sessionFile) {
|
|
71203
71617
|
if (sessionFile === undefined || await store.readHeader(sessionId) !== undefined)
|
|
71204
71618
|
return;
|
|
71205
|
-
const location =
|
|
71206
|
-
const legacy = await JsonlSessionStore.open(
|
|
71619
|
+
const location = resolve16(sessionFile);
|
|
71620
|
+
const legacy = await JsonlSessionStore.open(dirname15(location), { exactDirectory: true, scanExisting: false });
|
|
71207
71621
|
try {
|
|
71208
71622
|
for await (const file of legacy.scanFiles()) {
|
|
71209
|
-
if (
|
|
71623
|
+
if (resolve16(file.location) !== location || file.header.id !== sessionId)
|
|
71210
71624
|
continue;
|
|
71211
71625
|
const snapshot = await file.read();
|
|
71212
71626
|
if (await store.readHeader(sessionId) === undefined)
|
|
@@ -74032,6 +74446,8 @@ function createTrackedStageCaller(input) {
|
|
|
74032
74446
|
await runtime.releaseLiveHandle().catch(() => {});
|
|
74033
74447
|
}
|
|
74034
74448
|
runtime.state.sessionCheckpointed = finalizationError === undefined;
|
|
74449
|
+
if (runtime.state.contextUnreachable)
|
|
74450
|
+
runtime.stageRegistry.releaseIdle(runtime.runId, runtime.stageId);
|
|
74035
74451
|
input.limiter.release();
|
|
74036
74452
|
if (finalizationError !== undefined)
|
|
74037
74453
|
throwFinalizationError(finalizationError.error);
|
|
@@ -74195,7 +74611,8 @@ function createStageContext(input) {
|
|
|
74195
74611
|
recordStageNotice({ kind: "abort", to: "interrupted" });
|
|
74196
74612
|
await innerCtx.abort();
|
|
74197
74613
|
},
|
|
74198
|
-
__modelFallbackMeta: () => innerCtx.__modelFallbackMeta()
|
|
74614
|
+
__modelFallbackMeta: () => innerCtx.__modelFallbackMeta(),
|
|
74615
|
+
__releaseIfIdle: () => runtime.stageRegistry.releaseIdle(runtime.runId, runtime.stageId)
|
|
74199
74616
|
};
|
|
74200
74617
|
}
|
|
74201
74618
|
|
|
@@ -75884,10 +76301,10 @@ function isRetryableSameCandidateFailure(error) {
|
|
|
75884
76301
|
return isTransientExtensionStartupFailure(error);
|
|
75885
76302
|
return isRetryableSameModelFailure(error);
|
|
75886
76303
|
}
|
|
75887
|
-
function mayAdvanceToNextCandidate(error) {
|
|
76304
|
+
function mayAdvanceToNextCandidate(error, options) {
|
|
75888
76305
|
if (isExtensionStartupFailure(error))
|
|
75889
76306
|
return isTransientExtensionStartupFailure(error);
|
|
75890
|
-
return isRetryableModelFailure(error);
|
|
76307
|
+
return isRetryableModelFailure(error, options);
|
|
75891
76308
|
}
|
|
75892
76309
|
function hasMeaningfulUsage(usage) {
|
|
75893
76310
|
if (usage === undefined)
|
|
@@ -75979,6 +76396,7 @@ class StageSessionController {
|
|
|
75979
76396
|
structuredOutputExecutionFailureValue;
|
|
75980
76397
|
structuredOutputCycleAttemptMark;
|
|
75981
76398
|
unsubscribeTerminateWatcher;
|
|
76399
|
+
unbindAbortSignal;
|
|
75982
76400
|
sessionInternalRetries = 0;
|
|
75983
76401
|
unresolvedContextOverflowMessage;
|
|
75984
76402
|
generationSealed = false;
|
|
@@ -76468,6 +76886,8 @@ class StageSessionController {
|
|
|
76468
76886
|
this.pendingListeners.clear();
|
|
76469
76887
|
this.unsubscribeTerminateWatcher?.();
|
|
76470
76888
|
this.unsubscribeTerminateWatcher = undefined;
|
|
76889
|
+
this.unbindAbortSignal?.();
|
|
76890
|
+
this.unbindAbortSignal = undefined;
|
|
76471
76891
|
this.terminatingToolCallIds.clear();
|
|
76472
76892
|
this.messageAdmission.dispose();
|
|
76473
76893
|
this.deliveryActivity.dispose();
|
|
@@ -76562,8 +76982,10 @@ class StageSessionController {
|
|
|
76562
76982
|
};
|
|
76563
76983
|
if (signal.aborted)
|
|
76564
76984
|
onAbort();
|
|
76565
|
-
else
|
|
76985
|
+
else {
|
|
76566
76986
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
76987
|
+
this.unbindAbortSignal = () => signal.removeEventListener("abort", onAbort);
|
|
76988
|
+
}
|
|
76567
76989
|
}
|
|
76568
76990
|
markAbort(reason) {
|
|
76569
76991
|
this.abortGeneration += 1;
|
|
@@ -76590,6 +77012,9 @@ class StageSessionController {
|
|
|
76590
77012
|
state.controller.abort(reason);
|
|
76591
77013
|
this.thrownErrorRetryStates.clear();
|
|
76592
77014
|
}
|
|
77015
|
+
fallbackFailureOptions() {
|
|
77016
|
+
return { fallbackOnContentFilter: this.retrySettings()?.fallbackOnContentFilter !== false };
|
|
77017
|
+
}
|
|
76593
77018
|
retrySettings() {
|
|
76594
77019
|
const managers = [
|
|
76595
77020
|
this.sessionSettingsManager,
|
|
@@ -76864,7 +77289,7 @@ class StageSessionController {
|
|
|
76864
77289
|
const errorSettingsManager = retrySettingsManagerFromError(error);
|
|
76865
77290
|
if (errorSettingsManager !== undefined)
|
|
76866
77291
|
this.sessionSettingsManager = errorSettingsManager;
|
|
76867
|
-
const retryableFailure = isRetryableModelFailure(error);
|
|
77292
|
+
const retryableFailure = isRetryableModelFailure(error, this.fallbackFailureOptions());
|
|
76868
77293
|
const sameCandidateRetryable = isRetryableSameModelFailure(error) && !isUnresolvedContextOverflowFailure(error);
|
|
76869
77294
|
recordProviderRateLimit(this.currentProvider(), error, modelIdForModel(this.currentModelValue()));
|
|
76870
77295
|
if (this.hasExplicitModelFallbackConfig && isProviderRateLimit(error)) {
|
|
@@ -77489,7 +77914,7 @@ class StageSessionController {
|
|
|
77489
77914
|
}
|
|
77490
77915
|
throw new WorkflowPromptModelFailure(terminalFailure);
|
|
77491
77916
|
} catch (err) {
|
|
77492
|
-
if (this.capturedStructuredOutputForAttempt() && isRetryableModelFailure(err)) {
|
|
77917
|
+
if (this.capturedStructuredOutputForAttempt() && isRetryableModelFailure(err, this.fallbackFailureOptions())) {
|
|
77493
77918
|
this.artifactCapture.settleAttempt(true);
|
|
77494
77919
|
const usage = this.takeAttemptUsage(resumedSession);
|
|
77495
77920
|
this.modelAttempts.push({
|
|
@@ -77511,7 +77936,7 @@ class StageSessionController {
|
|
|
77511
77936
|
...usage === undefined ? {} : { usage },
|
|
77512
77937
|
error: message
|
|
77513
77938
|
});
|
|
77514
|
-
if (this.opts.signal?.aborted || !isRetryableModelFailure(err)) {
|
|
77939
|
+
if (this.opts.signal?.aborted || !isRetryableModelFailure(err, this.fallbackFailureOptions())) {
|
|
77515
77940
|
this.modelWarnings.push(...this.pendingFallbackWarnings);
|
|
77516
77941
|
this.pendingFallbackWarnings.length = 0;
|
|
77517
77942
|
this.notifyModelFallbackMetaChange();
|
|
@@ -77560,7 +77985,7 @@ class StageSessionController {
|
|
|
77560
77985
|
throw err;
|
|
77561
77986
|
const message = errorMessage3(err);
|
|
77562
77987
|
const terminalStageDelivery = isWorkflowPendingStageDeliveryFailure(err);
|
|
77563
|
-
if (!terminalStageDelivery && this.capturedStructuredOutputForAttempt() && isRetryableModelFailure(err)) {
|
|
77988
|
+
if (!terminalStageDelivery && this.capturedStructuredOutputForAttempt() && isRetryableModelFailure(err, this.fallbackFailureOptions())) {
|
|
77564
77989
|
this.recordSuccessfulAttempt(candidate);
|
|
77565
77990
|
return "handled";
|
|
77566
77991
|
}
|
|
@@ -77577,7 +78002,7 @@ class StageSessionController {
|
|
|
77577
78002
|
if (walk !== undefined) {
|
|
77578
78003
|
this.recordCandidateFailure(walk, index, err, candidates);
|
|
77579
78004
|
nextIndex = undefined;
|
|
77580
|
-
if (!this.opts.signal?.aborted && !this.startupWait.signal.aborted && !terminalStageDelivery && mayAdvanceToNextCandidate(err)) {
|
|
78005
|
+
if (!this.opts.signal?.aborted && !this.startupWait.signal.aborted && !terminalStageDelivery && mayAdvanceToNextCandidate(err, this.fallbackFailureOptions())) {
|
|
77581
78006
|
try {
|
|
77582
78007
|
nextIndex = await this.selectReadyCandidate(candidates, walk, index + 1);
|
|
77583
78008
|
} catch (error) {
|
|
@@ -77590,7 +78015,7 @@ class StageSessionController {
|
|
|
77590
78015
|
}
|
|
77591
78016
|
}
|
|
77592
78017
|
}
|
|
77593
|
-
if (this.opts.signal?.aborted || this.startupWait.signal.aborted || terminalStageDelivery || !mayAdvanceToNextCandidate(err) || nextIndex === undefined) {
|
|
78018
|
+
if (this.opts.signal?.aborted || this.startupWait.signal.aborted || terminalStageDelivery || !mayAdvanceToNextCandidate(err, this.fallbackFailureOptions()) || nextIndex === undefined) {
|
|
77594
78019
|
await this.settleFailedCandidate();
|
|
77595
78020
|
return "throw";
|
|
77596
78021
|
}
|
|
@@ -77685,7 +78110,7 @@ var init_stage_runner_controller = __esm(() => {
|
|
|
77685
78110
|
// dist/builtin/workflows/src/runs/foreground/stage-runner-output.ts
|
|
77686
78111
|
import { createHash as createHash5 } from "node:crypto";
|
|
77687
78112
|
import { mkdir as mkdir7, writeFile as writeFile10 } from "node:fs/promises";
|
|
77688
|
-
import { basename as basename5, dirname as
|
|
78113
|
+
import { basename as basename5, dirname as dirname16, isAbsolute as isAbsolute11, join as join31, resolve as resolve17 } from "node:path";
|
|
77689
78114
|
function normalizeMaxOutput2(maxOutput) {
|
|
77690
78115
|
return {
|
|
77691
78116
|
bytes: maxOutput?.bytes ?? DEFAULT_MAX_OUTPUT_BYTES,
|
|
@@ -77748,7 +78173,7 @@ function formatByteSize(bytes) {
|
|
|
77748
78173
|
}
|
|
77749
78174
|
function transcriptPath(runId, outputPath) {
|
|
77750
78175
|
const digest = createHash5("sha256").update(outputPath).digest("hex").slice(0, 16);
|
|
77751
|
-
return
|
|
78176
|
+
return join31(workflowArtifactRunPath(runId), "transcripts", `${digest}-${basename5(outputPath)}.transcript.md`);
|
|
77752
78177
|
}
|
|
77753
78178
|
function appendContent(lines, content) {
|
|
77754
78179
|
if (typeof content === "string") {
|
|
@@ -77793,7 +78218,7 @@ function renderTranscript(messages, fallbackOutput) {
|
|
|
77793
78218
|
`);
|
|
77794
78219
|
}
|
|
77795
78220
|
function savedOutputReference(outputPath, fullOutput, transcriptPathValue, transcriptText, transcriptAvailable, warning) {
|
|
77796
|
-
const absolutePath =
|
|
78221
|
+
const absolutePath = resolve17(outputPath);
|
|
77797
78222
|
const bytes = Buffer.byteLength(fullOutput, "utf8");
|
|
77798
78223
|
const lines = countLines(fullOutput);
|
|
77799
78224
|
const transcriptBytes = Buffer.byteLength(transcriptText, "utf8");
|
|
@@ -77808,8 +78233,8 @@ function resolveOutputPath(output, runtimeCwd, requestedCwd) {
|
|
|
77808
78233
|
return;
|
|
77809
78234
|
if (isAbsolute11(output))
|
|
77810
78235
|
return output;
|
|
77811
|
-
const baseCwd = requestedCwd === undefined ? runtimeCwd : isAbsolute11(requestedCwd) ? requestedCwd :
|
|
77812
|
-
return
|
|
78236
|
+
const baseCwd = requestedCwd === undefined ? runtimeCwd : isAbsolute11(requestedCwd) ? requestedCwd : resolve17(runtimeCwd, requestedCwd);
|
|
78237
|
+
return resolve17(baseCwd, output);
|
|
77813
78238
|
}
|
|
77814
78239
|
function degenerateWarning(fullOutput) {
|
|
77815
78240
|
return fullOutput.trim().length === 0 ? "WARNING: the stage artifact is empty; search the companion transcript for this stage's work." : undefined;
|
|
@@ -77868,7 +78293,7 @@ async function finalizePromptOutput(fullOutput, outputOptions, runtimeCwd, runId
|
|
|
77868
78293
|
const transcriptFile = transcriptPath(runId, outputPath);
|
|
77869
78294
|
const transcript = renderTranscript(messages, fullOutput);
|
|
77870
78295
|
try {
|
|
77871
|
-
await mkdir7(
|
|
78296
|
+
await mkdir7(dirname16(outputPath), { recursive: true });
|
|
77872
78297
|
await writeFile10(outputPath, fullOutput, "utf8");
|
|
77873
78298
|
} catch (err) {
|
|
77874
78299
|
return `${displayOutput}
|
|
@@ -77880,7 +78305,7 @@ ${err instanceof Error ? err.message : String(err)}`;
|
|
|
77880
78305
|
let transcriptError;
|
|
77881
78306
|
try {
|
|
77882
78307
|
await ensureWorkflowArtifactRunDirectory(runId);
|
|
77883
|
-
await mkdir7(
|
|
78308
|
+
await mkdir7(dirname16(transcriptFile), { recursive: true });
|
|
77884
78309
|
await writeFile10(transcriptFile, transcript, "utf8");
|
|
77885
78310
|
transcriptAvailable = true;
|
|
77886
78311
|
} catch (err) {
|
|
@@ -78391,7 +78816,8 @@ function createWorkflowStageFactory(input) {
|
|
|
78391
78816
|
stageFinalized: false,
|
|
78392
78817
|
sessionCheckpointed: false,
|
|
78393
78818
|
skippedForParallelFailFast: false,
|
|
78394
|
-
stageControlDropped: false
|
|
78819
|
+
stageControlDropped: false,
|
|
78820
|
+
contextUnreachable: false
|
|
78395
78821
|
};
|
|
78396
78822
|
const activeAskUserQuestionCalls = new Set;
|
|
78397
78823
|
const hasActiveAskUserQuestion = () => activeAskUserQuestionCalls.size > 0 || state.activeAskUserQuestionAnonymousCalls > 0;
|
|
@@ -78637,9 +79063,18 @@ function createWorkflowStageFactory(input) {
|
|
|
78637
79063
|
hasContinuation: input.opts.continuation !== undefined,
|
|
78638
79064
|
hasScopedParents: stageFailFastScope?.parentIds !== undefined
|
|
78639
79065
|
});
|
|
78640
|
-
|
|
79066
|
+
const context = createStageContext({ runtime, runTrackedStageCall });
|
|
79067
|
+
unreachableStageContexts.register(context, releaseWhenUnreachable(state, input.stageRegistry, input.runId, stageId));
|
|
79068
|
+
return context;
|
|
79069
|
+
};
|
|
79070
|
+
}
|
|
79071
|
+
function releaseWhenUnreachable(state, registry, runId, stageId) {
|
|
79072
|
+
return () => {
|
|
79073
|
+
state.contextUnreachable = true;
|
|
79074
|
+
registry.releaseIdle(runId, stageId);
|
|
78641
79075
|
};
|
|
78642
79076
|
}
|
|
79077
|
+
var unreachableStageContexts;
|
|
78643
79078
|
var init_executor_stage_factory = __esm(() => {
|
|
78644
79079
|
init_intercom_group();
|
|
78645
79080
|
init_pending_stage_route_readiness();
|
|
@@ -78654,6 +79089,7 @@ var init_executor_stage_factory = __esm(() => {
|
|
|
78654
79089
|
init_pending_stage_delivery2();
|
|
78655
79090
|
init_stage_runner();
|
|
78656
79091
|
init_stage_runner_session();
|
|
79092
|
+
unreachableStageContexts = new FinalizationRegistry((release) => release());
|
|
78657
79093
|
});
|
|
78658
79094
|
|
|
78659
79095
|
// dist/builtin/workflows/src/engine/runtime.ts
|
|
@@ -78995,7 +79431,7 @@ async function executeRun(def, resolvedInputs, opts) {
|
|
|
78995
79431
|
const ownsCancellationRegistration = opts.signal === undefined && opts.cancellation !== undefined;
|
|
78996
79432
|
const tracker = new GraphFrontierTracker;
|
|
78997
79433
|
const inputConcurrency = resolveInputConcurrency(def.inputs, resolvedInputs);
|
|
78998
|
-
const inputRuntimeDefaults = resolveInputRuntimeDefaults(def, resolvedInputs), gitWorktreeSetupCacheOwner = createGitWorktreeSetupCacheOwner(opts.gitWorktreeSetupCache);
|
|
79434
|
+
const inputRuntimeDefaults = resolveInputRuntimeDefaults(def, resolvedInputs), gitWorktreeSetupCacheOwner = createGitWorktreeSetupCacheOwner(opts.gitWorktreeSetupCache, runId);
|
|
78999
79435
|
const gitWorktreeSetupCache = gitWorktreeSetupCacheOwner.cache;
|
|
79000
79436
|
const workflowInvocationCwd = opts.cwd ?? process.cwd();
|
|
79001
79437
|
let workflowCwd;
|
|
@@ -80094,8 +80530,8 @@ var init_workflows_host_module = __esm(() => {
|
|
|
80094
80530
|
|
|
80095
80531
|
// dist/builtin/workflows/src/workers/pool-journal.ts
|
|
80096
80532
|
import { createHash as createHash6, randomUUID as randomUUID22 } from "node:crypto";
|
|
80097
|
-
import { existsSync as existsSync7, mkdirSync as
|
|
80098
|
-
import { basename as basename6, dirname as
|
|
80533
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync9, readFileSync as readFileSync8, writeFileSync as writeFileSync7 } from "node:fs";
|
|
80534
|
+
import { basename as basename6, dirname as dirname17, join as join32, resolve as resolve18 } from "node:path";
|
|
80099
80535
|
import { storageSelectionKey as storageSelectionKey2 } from "@bastani/atomic/storage";
|
|
80100
80536
|
function encodeOption(value2) {
|
|
80101
80537
|
if (typeof value2 === "bigint")
|
|
@@ -80117,7 +80553,7 @@ function isReopenableSelection(selection) {
|
|
|
80117
80553
|
}
|
|
80118
80554
|
function encodeSelection(selection) {
|
|
80119
80555
|
if (selection.kind === "sqlite")
|
|
80120
|
-
return JSON.stringify({ ...selection, path:
|
|
80556
|
+
return JSON.stringify({ ...selection, path: resolve18(selection.path) });
|
|
80121
80557
|
return JSON.stringify({
|
|
80122
80558
|
...selection,
|
|
80123
80559
|
...selection.options === undefined ? {} : {
|
|
@@ -80149,7 +80585,7 @@ function openDurableJournal(durable) {
|
|
|
80149
80585
|
return openSelectedRunJournal(decodeSelection(durable.selection), durable.agentDir);
|
|
80150
80586
|
}
|
|
80151
80587
|
function poolStateDirectory(agentDir) {
|
|
80152
|
-
return
|
|
80588
|
+
return join32(dirname17(agentDir), "workflows", "pool");
|
|
80153
80589
|
}
|
|
80154
80590
|
function databaseIdentity(selection, agentDir) {
|
|
80155
80591
|
return storageSelectionKey2(selection.kind === "adapter" ? { ...selection, secrets: undefined } : selection, agentDir);
|
|
@@ -80157,16 +80593,16 @@ function databaseIdentity(selection, agentDir) {
|
|
|
80157
80593
|
function coordinationPath(durable) {
|
|
80158
80594
|
const selection = decodeSelection(durable.selection);
|
|
80159
80595
|
const identity = createHash6("sha256").update(databaseIdentity(selection, durable.agentDir)).digest("hex");
|
|
80160
|
-
return
|
|
80596
|
+
return join32(poolStateDirectory(durable.agentDir), `${identity.slice(0, 24)}.sqlite`);
|
|
80161
80597
|
}
|
|
80162
80598
|
function coordinationDatabase(durable) {
|
|
80163
80599
|
return basename6(coordinationPath(durable), ".sqlite");
|
|
80164
80600
|
}
|
|
80165
80601
|
function openCoordinationJournal(path) {
|
|
80166
|
-
const file =
|
|
80602
|
+
const file = resolve18(path);
|
|
80167
80603
|
let opened = coordinationJournals.get(file);
|
|
80168
80604
|
if (opened === undefined) {
|
|
80169
|
-
|
|
80605
|
+
mkdirSync9(dirname17(file), { recursive: true });
|
|
80170
80606
|
opened = resolveRunJournal({ journal: { path: file } });
|
|
80171
80607
|
coordinationJournals.set(file, opened);
|
|
80172
80608
|
opened.catch(() => {
|
|
@@ -80183,20 +80619,20 @@ function poolJournalFor(journal) {
|
|
|
80183
80619
|
return coordinations.get(journal) ?? journal;
|
|
80184
80620
|
}
|
|
80185
80621
|
function poolHostId(agentDir) {
|
|
80186
|
-
const file =
|
|
80622
|
+
const file = join32(poolStateDirectory(agentDir), "host-id");
|
|
80187
80623
|
try {
|
|
80188
|
-
const existing =
|
|
80624
|
+
const existing = readFileSync8(file, "utf8").trim();
|
|
80189
80625
|
if (existing !== "")
|
|
80190
80626
|
return existing;
|
|
80191
80627
|
} catch {}
|
|
80192
|
-
|
|
80628
|
+
mkdirSync9(dirname17(file), { recursive: true });
|
|
80193
80629
|
const id = randomUUID22();
|
|
80194
80630
|
try {
|
|
80195
|
-
|
|
80631
|
+
writeFileSync7(file, `${id}
|
|
80196
80632
|
`, { flag: "wx" });
|
|
80197
80633
|
return id;
|
|
80198
80634
|
} catch {
|
|
80199
|
-
return
|
|
80635
|
+
return readFileSync8(file, "utf8").trim();
|
|
80200
80636
|
}
|
|
80201
80637
|
}
|
|
80202
80638
|
var POOL_DURABLE_JOURNAL_ENV = "ATOMIC_POOL_DURABLE_JOURNAL", coordinationJournals, coordinations;
|
|
@@ -80219,7 +80655,6 @@ init_run();
|
|
|
80219
80655
|
init_run_tool_control_registry();
|
|
80220
80656
|
init_workflow_tool_abort();
|
|
80221
80657
|
init_wiring();
|
|
80222
|
-
init_pending_stage_delivery2();
|
|
80223
80658
|
init_stage_control_registry();
|
|
80224
80659
|
init_stage_queued_user_messages();
|
|
80225
80660
|
init_stage_ui_broker();
|
|
@@ -80245,6 +80680,94 @@ function poolWorkerCommit(input) {
|
|
|
80245
80680
|
return receipt;
|
|
80246
80681
|
};
|
|
80247
80682
|
}
|
|
80683
|
+
// dist/builtin/workflows/src/workers/pooled-stage-delivery.ts
|
|
80684
|
+
init_pending_stage_delivery2();
|
|
80685
|
+
init_pending_stage_delivery();
|
|
80686
|
+
init_store_pending_stage_delivery_methods();
|
|
80687
|
+
function pooledStageQueuedEntries(journal, store, root, stage) {
|
|
80688
|
+
const messages = journal.getWorkflow(root)?.pendingStageMessages ?? [];
|
|
80689
|
+
const runs = store.runs();
|
|
80690
|
+
const run = runs.find((candidate) => candidate.id === stage.runId);
|
|
80691
|
+
if (run === undefined)
|
|
80692
|
+
return [];
|
|
80693
|
+
const exact = new Set;
|
|
80694
|
+
for (const key of new Set([stage.stageId, stage.stageName]))
|
|
80695
|
+
for (const entry of pendingStageMessagesFor(messages, stage.runId, key, resolvePendingStageIdentity(run, key)))
|
|
80696
|
+
exact.add(entry);
|
|
80697
|
+
const hops = pooledStageHops(journal, runs, root, stage.runId);
|
|
80698
|
+
const sticky = hops === undefined ? [] : messages.filter((entry) => entry.runId === root && stickyEntryTargetsStage(entry, root, hops, stage.stageId, stage.stageName));
|
|
80699
|
+
return [
|
|
80700
|
+
...[...exact].map((entry) => ({ entry, index: messages.indexOf(entry) })),
|
|
80701
|
+
...sticky.map((entry) => ({ entry, index: Number.MAX_SAFE_INTEGER }))
|
|
80702
|
+
].map(({ entry, index }) => ({ entry, index, order: pendingStageMessageAdmissionOrder(entry) })).sort((left, right) => left.order - right.order || left.index - right.index).map(({ entry }) => entry);
|
|
80703
|
+
}
|
|
80704
|
+
function pooledStageHops(journal, runs, root, runId) {
|
|
80705
|
+
const byId = new Map(runs.map((run) => [run.id, run]));
|
|
80706
|
+
const hops = [];
|
|
80707
|
+
let current = byId.get(runId);
|
|
80708
|
+
while (current !== undefined && current.parentRunId !== root) {
|
|
80709
|
+
const child = current;
|
|
80710
|
+
const parent = child.parentRunId === undefined ? undefined : byId.get(child.parentRunId);
|
|
80711
|
+
const boundary = parent?.stages.find((stage) => stage.id === child.parentStageId);
|
|
80712
|
+
if (parent === undefined || boundary === undefined)
|
|
80713
|
+
return;
|
|
80714
|
+
hops.unshift({ name: boundary.name, runId: child.id });
|
|
80715
|
+
current = parent;
|
|
80716
|
+
}
|
|
80717
|
+
if (current === undefined)
|
|
80718
|
+
return;
|
|
80719
|
+
const childRunId = current.id;
|
|
80720
|
+
const boundary = journal.listCheckpoints(root).find((checkpoint) => checkpoint.kind === "stage" && checkpoint.topology?.boundary?.child.runId === childRunId);
|
|
80721
|
+
if (boundary === undefined)
|
|
80722
|
+
return;
|
|
80723
|
+
hops.unshift({ name: boundary.name, runId: childRunId });
|
|
80724
|
+
return hops;
|
|
80725
|
+
}
|
|
80726
|
+
function createPooledStageDelivery(options) {
|
|
80727
|
+
const admissions = new Map;
|
|
80728
|
+
const sinks = new Map;
|
|
80729
|
+
return {
|
|
80730
|
+
routeCapability: options.routeCapability,
|
|
80731
|
+
async deliverPending(deliver, recipient) {
|
|
80732
|
+
const sessionId = recipient?.sessionId ?? "";
|
|
80733
|
+
sinks.set(sessionId, deliver);
|
|
80734
|
+
let admitted = admissions.get(sessionId);
|
|
80735
|
+
if (admitted === undefined) {
|
|
80736
|
+
admitted = new Map;
|
|
80737
|
+
admissions.set(sessionId, admitted);
|
|
80738
|
+
}
|
|
80739
|
+
const held = admitted;
|
|
80740
|
+
for (const id of recipient?.receivedMessageIds ?? [])
|
|
80741
|
+
if (!held.has(id))
|
|
80742
|
+
held.set(id, Promise.resolve());
|
|
80743
|
+
const admit = (from, message) => {
|
|
80744
|
+
const existing = held.get(message.id);
|
|
80745
|
+
if (existing !== undefined)
|
|
80746
|
+
return existing;
|
|
80747
|
+
const sink = sinks.get(sessionId) ?? deliver;
|
|
80748
|
+
const delivery = Promise.resolve().then(() => sink(from, message));
|
|
80749
|
+
held.set(message.id, delivery);
|
|
80750
|
+
delivery.catch(() => {
|
|
80751
|
+
if (held.get(message.id) === delivery)
|
|
80752
|
+
held.delete(message.id);
|
|
80753
|
+
});
|
|
80754
|
+
return delivery;
|
|
80755
|
+
};
|
|
80756
|
+
try {
|
|
80757
|
+
for (const entry of await options.queued())
|
|
80758
|
+
await admit(toPendingStageSender(entry), entry.message);
|
|
80759
|
+
} catch {}
|
|
80760
|
+
const deliveries = [...held];
|
|
80761
|
+
const settled = await Promise.allSettled(deliveries.map(([, delivery]) => delivery));
|
|
80762
|
+
const receivedMessageIds = deliveries.flatMap(([id], index) => settled[index]?.status === "fulfilled" ? [id] : []);
|
|
80763
|
+
options.forward({ sessionId, receivedMessageIds }, admit).catch(() => {});
|
|
80764
|
+
},
|
|
80765
|
+
ready() {
|
|
80766
|
+
return;
|
|
80767
|
+
},
|
|
80768
|
+
fail: options.fail
|
|
80769
|
+
};
|
|
80770
|
+
}
|
|
80248
80771
|
|
|
80249
80772
|
// dist/builtin/workflows/src/workers/local-worker.ts
|
|
80250
80773
|
init_pooled_watch_trace();
|
|
@@ -81314,6 +81837,7 @@ async function runWorker(start) {
|
|
|
81314
81837
|
await reader.catchUpWorkflow(start.root);
|
|
81315
81838
|
const work = await request({ kind: "claim" });
|
|
81316
81839
|
attemptFenced = false;
|
|
81840
|
+
let poolFault;
|
|
81317
81841
|
if (!work) {
|
|
81318
81842
|
await delay4(25 + Math.random() * 25);
|
|
81319
81843
|
continue;
|
|
@@ -81378,25 +81902,18 @@ async function runWorker(start) {
|
|
|
81378
81902
|
const capability = await request({ kind: "route", runId: meta.runId });
|
|
81379
81903
|
if (typeof capability !== "string")
|
|
81380
81904
|
throw new Error("Worker root route is unavailable");
|
|
81381
|
-
const pendingStageDelivery = {
|
|
81905
|
+
const pendingStageDelivery = createPooledStageDelivery({
|
|
81382
81906
|
routeCapability: capability,
|
|
81383
|
-
async
|
|
81384
|
-
await
|
|
81385
|
-
|
|
81386
|
-
progress();
|
|
81387
|
-
try {
|
|
81388
|
-
await deliver(from, message);
|
|
81389
|
-
} finally {
|
|
81390
|
-
progress();
|
|
81391
|
-
}
|
|
81392
|
-
}).then(() => {}));
|
|
81907
|
+
queued: async () => {
|
|
81908
|
+
await reader.catchUpWorkflow(start.root);
|
|
81909
|
+
return pooledStageQueuedEntries(reader, store, start.root, identity);
|
|
81393
81910
|
},
|
|
81394
|
-
async
|
|
81911
|
+
forward: async (recipient, deliver) => {
|
|
81395
81912
|
await publication;
|
|
81396
|
-
await request({ kind: "delivery
|
|
81913
|
+
await request({ kind: "delivery", ...identity, recipient }, deliver);
|
|
81397
81914
|
},
|
|
81398
81915
|
fail: options.orchestrationContext.pendingStageDelivery.fail
|
|
81399
|
-
};
|
|
81916
|
+
});
|
|
81400
81917
|
return baseAdapter.create({ ...options, orchestrationContext: { ...options.orchestrationContext, pendingStageDelivery } }, meta);
|
|
81401
81918
|
}
|
|
81402
81919
|
};
|
|
@@ -81486,6 +82003,11 @@ async function runWorker(start) {
|
|
|
81486
82003
|
}
|
|
81487
82004
|
if (attemptFenced)
|
|
81488
82005
|
throw new WorkAttemptFencedError("Work attempt fenced before completion");
|
|
82006
|
+
if (failedOnInfrastructureFault(result)) {
|
|
82007
|
+
poolFault = infrastructureFaultMessage(result);
|
|
82008
|
+
attemptFenced = true;
|
|
82009
|
+
throw new WorkAttemptFencedError(poolFault);
|
|
82010
|
+
}
|
|
81489
82011
|
await request({ kind: "complete", result });
|
|
81490
82012
|
} catch (error) {
|
|
81491
82013
|
if (!attemptFenced)
|
|
@@ -81503,7 +82025,7 @@ async function runWorker(start) {
|
|
|
81503
82025
|
await renewal;
|
|
81504
82026
|
await report;
|
|
81505
82027
|
await controlPulse;
|
|
81506
|
-
await request({ kind: "fenced" });
|
|
82028
|
+
await request({ kind: "fenced", ...poolFault === undefined ? {} : { fault: poolFault } });
|
|
81507
82029
|
}
|
|
81508
82030
|
if (rootLeaseLost)
|
|
81509
82031
|
break;
|