@ricsam/r5d-worker 0.0.131 → 0.0.133
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/atomic-rename.cjs +303 -0
- package/dist/cjs/git-blob-hash.cjs +41 -0
- package/dist/cjs/main.cjs +187 -38
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/three-way-merge.cjs +346 -0
- package/dist/cjs/working-tree-mirror.cjs +1049 -64
- package/dist/cjs/workspace-command-sync-policy.cjs +8 -4
- package/dist/cjs/workspace-command-targets.cjs +63 -0
- package/dist/cjs/workspace-filesystem-job-types.cjs +11 -1
- package/dist/cjs/workspace-filesystem-jobs.cjs +2 -0
- package/dist/cjs/workspace-git-sync.cjs +846 -61
- package/dist/cjs/workspace-hydration-ledger.cjs +66 -0
- package/dist/cjs/workspace-hydration-merge.cjs +433 -0
- package/dist/cjs/workspace-hydration-recovery-state.cjs +53 -0
- package/dist/cjs/workspace-merge-projection.cjs +81 -10
- package/dist/cjs/workspace-project-config-policy.cjs +19 -12
- package/dist/mjs/atomic-rename.mjs +261 -0
- package/dist/mjs/git-blob-hash.mjs +16 -0
- package/dist/mjs/main.mjs +196 -39
- package/dist/mjs/package.json +1 -1
- package/dist/mjs/three-way-merge.mjs +318 -0
- package/dist/mjs/working-tree-mirror.mjs +1035 -64
- package/dist/mjs/workspace-command-sync-policy.mjs +8 -4
- package/dist/mjs/workspace-command-targets.mjs +37 -0
- package/dist/mjs/workspace-filesystem-job-types.mjs +11 -1
- package/dist/mjs/workspace-filesystem-jobs.mjs +4 -0
- package/dist/mjs/workspace-git-sync.mjs +854 -62
- package/dist/mjs/workspace-hydration-ledger.mjs +42 -0
- package/dist/mjs/workspace-hydration-merge.mjs +399 -0
- package/dist/mjs/workspace-hydration-recovery-state.mjs +29 -0
- package/dist/mjs/workspace-merge-projection.mjs +85 -11
- package/dist/mjs/workspace-project-config-policy.mjs +16 -10
- package/dist/types/atomic-rename.d.ts +78 -0
- package/dist/types/git-blob-hash.d.ts +10 -0
- package/dist/types/main.d.ts +21 -2
- package/dist/types/three-way-merge.d.ts +77 -0
- package/dist/types/working-tree-mirror.d.ts +270 -7
- package/dist/types/workspace-command-sync-policy.d.ts +12 -6
- package/dist/types/workspace-command-targets.d.ts +37 -0
- package/dist/types/workspace-filesystem-job-types.d.ts +46 -4
- package/dist/types/workspace-git-sync.d.ts +125 -3
- package/dist/types/workspace-hydration-ledger.d.ts +43 -0
- package/dist/types/workspace-hydration-merge.d.ts +95 -0
- package/dist/types/workspace-hydration-recovery-state.d.ts +10 -0
- package/dist/types/workspace-merge-projection.d.ts +19 -1
- package/dist/types/workspace-project-config-policy.d.ts +17 -3
- package/package.json +2 -2
package/dist/cjs/main.cjs
CHANGED
|
@@ -87,7 +87,9 @@ var import_workspace_automatic_sync_policy = require("./workspace-automatic-sync
|
|
|
87
87
|
var import_workspace_command_sync_policy = require("./workspace-command-sync-policy.cjs");
|
|
88
88
|
var import_workspace_mutation_gate = require("./workspace-mutation-gate.cjs");
|
|
89
89
|
var import_workspace_mount_hold_fence = require("./workspace-mount-hold-fence.cjs");
|
|
90
|
+
var import_workspace_command_targets = require("./workspace-command-targets.cjs");
|
|
90
91
|
var import_workspace_filesystem_executor = require("./workspace-filesystem-executor.cjs");
|
|
92
|
+
var import_workspace_hydration_recovery_state = require("./workspace-hydration-recovery-state.cjs");
|
|
91
93
|
var import_workspace_projection_ledger = require("./workspace-projection-ledger.cjs");
|
|
92
94
|
var import_workspace_sync_coalescer = require("./workspace-sync-coalescer.cjs");
|
|
93
95
|
var import_workspace_path_move = require("./workspace-path-move.cjs");
|
|
@@ -149,6 +151,16 @@ const workerLifecycleTestHarness = {
|
|
|
149
151
|
workerCommandHasWorkspaceEffect
|
|
150
152
|
};
|
|
151
153
|
class ProjectWorkspaceConfigurationDeferredError extends Error {
|
|
154
|
+
constructor(changes) {
|
|
155
|
+
super(
|
|
156
|
+
`Project workspace configuration deferred while visible checkout activity is active: ${(0, import_workspace_project_config_policy.describeBusyProjectConfigurationChanges)(changes)}`
|
|
157
|
+
);
|
|
158
|
+
this.changes = changes;
|
|
159
|
+
this.name = "ProjectWorkspaceConfigurationDeferredError";
|
|
160
|
+
this.projectIds = changes.map(({ projectId }) => projectId);
|
|
161
|
+
}
|
|
162
|
+
changes;
|
|
163
|
+
projectIds;
|
|
152
164
|
}
|
|
153
165
|
class WorkerServerUnavailableError extends Error {
|
|
154
166
|
name = "WorkerServerUnavailableError";
|
|
@@ -372,13 +384,12 @@ const workspaceSyncSingleFlight = {
|
|
|
372
384
|
afterCurrent() {
|
|
373
385
|
return workspaceSyncQueue;
|
|
374
386
|
},
|
|
375
|
-
mountHold(
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
if (!workspaceMountHoldFence.isHeld(mountIds)) return null;
|
|
387
|
+
mountHold(targets, signal) {
|
|
388
|
+
const mountIds = targets.flatMap((target) => target.type === "project" ? [projectMountId(target.projectId, target.branchName)] : []);
|
|
389
|
+
if (mountIds.length === 0 || !workspaceMountHoldFence.isHeld(mountIds)) return null;
|
|
379
390
|
const holder = workspaceMountHoldFence.describeHold(mountIds);
|
|
380
391
|
process.stdout.write(
|
|
381
|
-
`[r5d-worker] command for ${
|
|
392
|
+
`[r5d-worker] command for ${targets.map(describeWorkerSessionTarget).join(", ")} waits for ${holder ?? "a workspace filesystem job"}
|
|
382
393
|
`
|
|
383
394
|
);
|
|
384
395
|
return workspaceMountHoldFence.waitForRelease(mountIds, { signal });
|
|
@@ -991,6 +1002,7 @@ function validateProjectId(projectId) {
|
|
|
991
1002
|
function validateBranchName(branchName) {
|
|
992
1003
|
(0, import_managed_paths.validateManagedBranchName)(branchName);
|
|
993
1004
|
}
|
|
1005
|
+
const commandTargetValidators = { validateProjectId, validateBranchName };
|
|
994
1006
|
const NON_RECURSIVE_GIT_CONFIG_ARGS = [
|
|
995
1007
|
"-c",
|
|
996
1008
|
"submodule.recurse=false",
|
|
@@ -2942,6 +2954,7 @@ async function executeCommand(input) {
|
|
|
2942
2954
|
activeProcesses.set(input.message.runId, {
|
|
2943
2955
|
process: subprocess,
|
|
2944
2956
|
target: input.resolvedTarget.target,
|
|
2957
|
+
additionalTargets: [...input.additionalTargets],
|
|
2945
2958
|
sessionId: input.message.sessionId ?? "",
|
|
2946
2959
|
mode: "foreground",
|
|
2947
2960
|
pid: subprocess.pid,
|
|
@@ -3075,6 +3088,7 @@ async function executeStreamingCommand(input) {
|
|
|
3075
3088
|
activeProcesses.set(input.message.runId, {
|
|
3076
3089
|
process: subprocess,
|
|
3077
3090
|
target: input.resolvedTarget.target,
|
|
3091
|
+
additionalTargets: [...input.additionalTargets],
|
|
3078
3092
|
sessionId: input.message.sessionId,
|
|
3079
3093
|
mode: input.message.mode,
|
|
3080
3094
|
pid: subprocess.pid,
|
|
@@ -3315,6 +3329,7 @@ function buildActiveProcessReports() {
|
|
|
3315
3329
|
return Array.from(activeProcesses.entries()).filter(([, active]) => active.sessionId.length > 0).map(([runId, active]) => ({
|
|
3316
3330
|
runId,
|
|
3317
3331
|
target: active.target,
|
|
3332
|
+
...active.additionalTargets.length > 0 ? { additionalTargets: active.additionalTargets.flatMap((target) => target.type === "project" ? [target] : []) } : {},
|
|
3318
3333
|
sessionId: active.sessionId,
|
|
3319
3334
|
platform: process.platform,
|
|
3320
3335
|
arch: process.arch,
|
|
@@ -3722,6 +3737,7 @@ async function openPty(input) {
|
|
|
3722
3737
|
activePtys.set(input.message.ptyId, {
|
|
3723
3738
|
...ptyProcess,
|
|
3724
3739
|
target: input.resolvedTarget.target,
|
|
3740
|
+
additionalTargets: [...input.additionalTargets],
|
|
3725
3741
|
foregroundBusy: true,
|
|
3726
3742
|
lastInputAt: 0,
|
|
3727
3743
|
...input.releaseWorkspaceMutation ? { releaseWorkspaceMutation: input.releaseWorkspaceMutation } : {}
|
|
@@ -3947,6 +3963,7 @@ async function startWorker(options, projectRuntime = {
|
|
|
3947
3963
|
let workspaceCredentialUsername = null;
|
|
3948
3964
|
let workspaceGitIdentity = projectRuntime.gitIdentity;
|
|
3949
3965
|
let workspaceConfigured = false;
|
|
3966
|
+
let hydrationRecoveryRequired;
|
|
3950
3967
|
let activeWorkspaceIncidentId = null;
|
|
3951
3968
|
let deferredWorkspaceConfiguration = null;
|
|
3952
3969
|
let deferredWorkspaceConfigurationRefreshTimer;
|
|
@@ -4246,12 +4263,12 @@ async function startWorker(options, projectRuntime = {
|
|
|
4246
4263
|
});
|
|
4247
4264
|
};
|
|
4248
4265
|
const activeWorkspaceMutationTargets = () => [
|
|
4249
|
-
...Array.from(activeProcesses.values()).filter(workerCommandHasWorkspaceEffect).
|
|
4266
|
+
...Array.from(activeProcesses.values()).filter(workerCommandHasWorkspaceEffect).flatMap(({ target, additionalTargets }) => [target, ...additionalTargets]),
|
|
4250
4267
|
...credentialBearingProcessGroupTargets.values(),
|
|
4251
|
-
...workspaceSyncPriorityProcessTargets.values(),
|
|
4268
|
+
...Array.from(workspaceSyncPriorityProcessTargets.values()).flat(),
|
|
4252
4269
|
...workspaceSyncPriorityOperationTargets.values(),
|
|
4253
|
-
...Array.from(activePtys.values()).filter((pty) => workerPtyIsWorkspaceBusy(pty)).
|
|
4254
|
-
...workspaceSyncPriorityPtyTargets.values()
|
|
4270
|
+
...Array.from(activePtys.values()).filter((pty) => workerPtyIsWorkspaceBusy(pty)).flatMap(({ target, additionalTargets }) => [target, ...additionalTargets]),
|
|
4271
|
+
...Array.from(workspaceSyncPriorityPtyTargets.values()).flat()
|
|
4255
4272
|
];
|
|
4256
4273
|
let visibleWorkspaceMutationEpoch = 0;
|
|
4257
4274
|
const projectWorkspaceMutationEpochs = /* @__PURE__ */ new Map();
|
|
@@ -4263,6 +4280,9 @@ async function startWorker(options, projectRuntime = {
|
|
|
4263
4280
|
const key = projectBranchKey(target.projectId, target.branchName);
|
|
4264
4281
|
projectWorkspaceMutationEpochs.set(key, (projectWorkspaceMutationEpochs.get(key) ?? 0) + 1);
|
|
4265
4282
|
};
|
|
4283
|
+
const recordAdditionalTargetMutations = (additional) => {
|
|
4284
|
+
for (const target of additional) recordVisibleWorkspaceMutation(target);
|
|
4285
|
+
};
|
|
4266
4286
|
const projectWorkspaceMutationToken = (projectId, branchName) => `${visibleWorkspaceMutationEpoch}:${projectWorkspaceMutationEpochs.get(projectBranchKey(projectId, branchName)) ?? 0}`;
|
|
4267
4287
|
const canonicalWorkspaceMutationIsActive = (targets) => targets.some((target) => target.type === "workspace" && target.rootProfile === "canonical_sync");
|
|
4268
4288
|
const projectBranchMountActivityBusy = (projectId, branchName, branchPath) => {
|
|
@@ -4348,6 +4368,24 @@ async function startWorker(options, projectRuntime = {
|
|
|
4348
4368
|
preserveLocalOnInitialOuterAbsence: creatorLocalIncarnation,
|
|
4349
4369
|
...initialPublicationSourceBranch ? { initialPublicationSourceRelativePath: workspaceProjectRelativePath(project.projectId, initialPublicationSourceBranch) } : {},
|
|
4350
4370
|
preserveLocalOnHydrationBasisChange: preservesCheckoutPathMove,
|
|
4371
|
+
describeHolders: () => [
|
|
4372
|
+
...new Set(
|
|
4373
|
+
[
|
|
4374
|
+
...[...activeProcesses.values()].filter(
|
|
4375
|
+
(entry) => (0, import_workspace_automatic_sync_policy.projectBranchHasActiveWorkspaceTarget)(project.projectId, branch.branchName, [
|
|
4376
|
+
entry.target,
|
|
4377
|
+
...entry.additionalTargets
|
|
4378
|
+
])
|
|
4379
|
+
).map((entry) => entry.sessionId),
|
|
4380
|
+
...[...activePtys.entries()].filter(
|
|
4381
|
+
([, entry]) => (0, import_workspace_automatic_sync_policy.projectBranchHasActiveWorkspaceTarget)(project.projectId, branch.branchName, [
|
|
4382
|
+
entry.target,
|
|
4383
|
+
...entry.additionalTargets
|
|
4384
|
+
])
|
|
4385
|
+
).map(([ptyId]) => `pty:${ptyId}`)
|
|
4386
|
+
].filter((id) => typeof id === "string")
|
|
4387
|
+
)
|
|
4388
|
+
].join(", ") || "no live declared holder",
|
|
4351
4389
|
busy: () => projectBranchMountBusy(project.projectId, branch.branchName, branchPath),
|
|
4352
4390
|
mutationToken: () => projectWorkspaceMutationToken(project.projectId, branch.branchName),
|
|
4353
4391
|
busyForRecovery: () => projectBranchMountExclusiveSyncBusy(project.projectId, branch.branchName, branchPath)
|
|
@@ -4873,6 +4911,7 @@ async function startWorker(options, projectRuntime = {
|
|
|
4873
4911
|
affectedPaths: result.affectedPaths,
|
|
4874
4912
|
activeMountIds: result.activeMountIds,
|
|
4875
4913
|
skippedMountIds: result.skippedMountIds,
|
|
4914
|
+
...result.mountSkipReasons ? { mountSkipReasons: result.mountSkipReasons } : {},
|
|
4876
4915
|
...missingCheckouts ? { missingCheckouts } : {},
|
|
4877
4916
|
activeProjectBranchPublications,
|
|
4878
4917
|
discardedPaths: [],
|
|
@@ -4885,8 +4924,11 @@ async function startWorker(options, projectRuntime = {
|
|
|
4885
4924
|
};
|
|
4886
4925
|
};
|
|
4887
4926
|
const failedWorkspaceSyncResult = async (attemptId, trigger, error) => {
|
|
4927
|
+
const recovery = (0, import_workspace_hydration_recovery_state.findHydrationRecovery)(error);
|
|
4928
|
+
if (recovery) hydrationRecoveryRequired = recovery;
|
|
4888
4929
|
const localHead = await workspaceLocalHead();
|
|
4889
4930
|
return {
|
|
4931
|
+
...recovery ? { failureReason: "hydration_recovery_required", hydrationRecovery: recovery } : {},
|
|
4890
4932
|
type: "workspace_sync",
|
|
4891
4933
|
attemptId,
|
|
4892
4934
|
workerLabel: label,
|
|
@@ -5109,6 +5151,12 @@ async function startWorker(options, projectRuntime = {
|
|
|
5109
5151
|
} catch (error) {
|
|
5110
5152
|
workspaceProjectionLedger.reset();
|
|
5111
5153
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
5154
|
+
if ((0, import_workspace_hydration_recovery_state.findHydrationRecovery)(error)) {
|
|
5155
|
+
workspaceConfigured = false;
|
|
5156
|
+
projectRuntime.configuration = void 0;
|
|
5157
|
+
advanceWorkerAdmissionGeneration();
|
|
5158
|
+
return await failedWorkspaceSyncResult(attemptId, input.trigger, error);
|
|
5159
|
+
}
|
|
5112
5160
|
const hydrationRecoverable = workspaceSyncFailureHydrationIsSafe({
|
|
5113
5161
|
error,
|
|
5114
5162
|
resetToCanonical: input.resetToCanonical === true,
|
|
@@ -5352,6 +5400,10 @@ async function startWorker(options, projectRuntime = {
|
|
|
5352
5400
|
projectRuntime.markWorkspaceDirty = markWorkspaceDirty;
|
|
5353
5401
|
const targetMayMutateVisibleWorkspace = (target) => target.type === "project" || target.rootProfile === "visible_projects";
|
|
5354
5402
|
const resolveMessageTarget = (target) => {
|
|
5403
|
+
if (hydrationRecoveryRequired)
|
|
5404
|
+
throw new Error(
|
|
5405
|
+
`Workspace hydration recovery required for transaction ${hydrationRecoveryRequired.transactionId}; inspect r5dctl workspace status`
|
|
5406
|
+
);
|
|
5355
5407
|
if (!workspaceConfigured) throw new Error("Worker workspace configuration has not completed successfully");
|
|
5356
5408
|
if (!deferredWorkspaceTargetIsAllowed(deferredWorkspaceConfiguration, activeWorkspaceIncidentId, target)) {
|
|
5357
5409
|
throw new Error(
|
|
@@ -5371,6 +5423,11 @@ async function startWorker(options, projectRuntime = {
|
|
|
5371
5423
|
projectConfigById
|
|
5372
5424
|
});
|
|
5373
5425
|
};
|
|
5426
|
+
const resolveCommandMessageTarget = (target, additionalTargets) => {
|
|
5427
|
+
const primary = resolveMessageTarget(target);
|
|
5428
|
+
for (const additional of additionalTargets) resolveMessageTarget(additional);
|
|
5429
|
+
return primary;
|
|
5430
|
+
};
|
|
5374
5431
|
const configureWorkerWorkspace = async (message, receiptGeneration) => {
|
|
5375
5432
|
const configurationOperationId = crypto.randomUUID();
|
|
5376
5433
|
sendLifecycleProgress({
|
|
@@ -5545,7 +5602,7 @@ async function startWorker(options, projectRuntime = {
|
|
|
5545
5602
|
throw new Error("Workspace configuration was superseded by a different active workspace incident");
|
|
5546
5603
|
}
|
|
5547
5604
|
for (const project of message.projects) (0, import_repository_transition_policy.assertRepositoryTransitionState)(project);
|
|
5548
|
-
const busyConfigurationChanges = (0, import_workspace_project_config_policy.
|
|
5605
|
+
const busyConfigurationChanges = (0, import_workspace_project_config_policy.busyProjectConfigurationChanges)({
|
|
5549
5606
|
currentProjects: [...projectConfigById.values()],
|
|
5550
5607
|
incomingProjects: message.projects,
|
|
5551
5608
|
activeTargets: activeWorkspaceMutationTargets(),
|
|
@@ -5556,12 +5613,44 @@ async function startWorker(options, projectRuntime = {
|
|
|
5556
5613
|
return readyProjectIds.has(project.projectId) && reconciledProjectConfigFingerprints.get(project.projectId) === fingerprint && project.branches.every(({ branchName }) => hasProjectWorktree(configuredProjectBranchPath(projectsRoot, project, branchName)));
|
|
5557
5614
|
}
|
|
5558
5615
|
});
|
|
5559
|
-
if (busyConfigurationChanges.length > 0)
|
|
5560
|
-
|
|
5561
|
-
|
|
5616
|
+
if (busyConfigurationChanges.length > 0) throw new ProjectWorkspaceConfigurationDeferredError(busyConfigurationChanges);
|
|
5617
|
+
workspaceConfigured = false;
|
|
5618
|
+
if (message.recoverHydrationTransactionId) {
|
|
5619
|
+
if (incidentDeferral.incidentId || message.resetToCanonical)
|
|
5620
|
+
throw new Error("Hydration preservation cannot run during a canonical reset or another workspace incident");
|
|
5621
|
+
if (activeProcesses.size || activePtys.size || credentialBearingProcessGroups.size || pendingProcessTerminals.size || activeWorkspaceMutationTargets().length) {
|
|
5622
|
+
throw new Error("Stop active worker processes and terminals before preserving interrupted hydration");
|
|
5623
|
+
}
|
|
5624
|
+
const preservedHydration = await (0, import_workspace_filesystem_executor.workspaceFilesystemExecutor)().run("hydration_preserve_interrupted", {
|
|
5625
|
+
workspacePath: workspaceShadowRoot,
|
|
5626
|
+
expectedTransactionId: message.recoverHydrationTransactionId
|
|
5627
|
+
});
|
|
5628
|
+
process.stdout.write(
|
|
5629
|
+
`[r5d-worker] Preserved hydration ${preservedHydration.transactionId}; archived evidence: ${preservedHydration.archivePath}
|
|
5630
|
+
`
|
|
5631
|
+
);
|
|
5632
|
+
for (const entry of preservedHydration.retainedEntries ?? []) {
|
|
5633
|
+
process.stdout.write(
|
|
5634
|
+
`[r5d-worker] Retained writer evidence for ${entry.mountId}: ${entry.sourcePath}; archived copy: ${entry.archivePath}
|
|
5635
|
+
`
|
|
5636
|
+
);
|
|
5637
|
+
}
|
|
5638
|
+
hydrationRecoveryRequired = void 0;
|
|
5639
|
+
}
|
|
5640
|
+
const interruptedHydration = await (0, import_workspace_filesystem_executor.workspaceFilesystemExecutor)().run("hydration_recovery_inspect", {
|
|
5641
|
+
workspacePath: workspaceShadowRoot
|
|
5642
|
+
});
|
|
5643
|
+
if (interruptedHydration) {
|
|
5644
|
+
throw Object.assign(
|
|
5645
|
+
new Error(
|
|
5646
|
+
`Interrupted hydration ${interruptedHydration.transactionId} requires explicit recovery; checkout files and snapshots are preserved`
|
|
5647
|
+
),
|
|
5648
|
+
{
|
|
5649
|
+
code: "hydration_recovery_required",
|
|
5650
|
+
hydrationRecovery: interruptedHydration
|
|
5651
|
+
}
|
|
5562
5652
|
);
|
|
5563
5653
|
}
|
|
5564
|
-
workspaceConfigured = false;
|
|
5565
5654
|
workspaceProjectionLedger.reset();
|
|
5566
5655
|
const preserveOnlyBranches = message.projects.flatMap(
|
|
5567
5656
|
(project) => project.preserveOnlyBranches.map(({ branchId, branchName }) => ({ branchId, projectId: project.projectId, branchName }))
|
|
@@ -5827,6 +5916,7 @@ async function startWorker(options, projectRuntime = {
|
|
|
5827
5916
|
deferredWorkspaceConfigurationRefreshTimer = void 0;
|
|
5828
5917
|
}
|
|
5829
5918
|
deferredWorkspaceConfiguration = null;
|
|
5919
|
+
hydrationRecoveryRequired = void 0;
|
|
5830
5920
|
workspaceConfigured = true;
|
|
5831
5921
|
const pending = [...pendingCheckouts.values()].sort(
|
|
5832
5922
|
(left, right) => left.projectId.localeCompare(right.projectId) || left.branchName.localeCompare(right.branchName)
|
|
@@ -5869,7 +5959,8 @@ async function startWorker(options, projectRuntime = {
|
|
|
5869
5959
|
requireCredentialGenerationRestart(true, void 0, 1);
|
|
5870
5960
|
}
|
|
5871
5961
|
workspaceConfigured = false;
|
|
5872
|
-
const
|
|
5962
|
+
const busyDeferral = error instanceof ProjectWorkspaceConfigurationDeferredError ? error : void 0;
|
|
5963
|
+
const deferred = busyDeferral !== void 0 || error instanceof import_project_workspace_state.ProjectWorkspacePendingBranchPathChangeError;
|
|
5873
5964
|
if (deferred) pendingCheckouts.clear();
|
|
5874
5965
|
for (const pending of (0, import_workspace_project_config_policy.deferredProjectConfigurationPendingBranches)(message.projects)) {
|
|
5875
5966
|
pendingCheckouts.set(projectBranchKey(pending.projectId, pending.branchName), pending);
|
|
@@ -5879,7 +5970,11 @@ async function startWorker(options, projectRuntime = {
|
|
|
5879
5970
|
pending: [...pendingCheckouts.values()].sort(
|
|
5880
5971
|
(left, right) => left.projectId.localeCompare(right.projectId) || left.branchName.localeCompare(right.branchName)
|
|
5881
5972
|
),
|
|
5882
|
-
aheadOfOriginBranches: []
|
|
5973
|
+
aheadOfOriginBranches: [],
|
|
5974
|
+
// A busy deferral is typed so the server can keep this socket, finish
|
|
5975
|
+
// receipt recovery and renew the execution lease instead of closing
|
|
5976
|
+
// the connection and retrying registration until the lease expires.
|
|
5977
|
+
...busyDeferral ? { deferredForBusyProjectIds: busyDeferral.projectIds } : {}
|
|
5883
5978
|
};
|
|
5884
5979
|
} finally {
|
|
5885
5980
|
workspaceSyncRequestsInFlight -= 1;
|
|
@@ -6068,7 +6163,9 @@ async function startWorker(options, projectRuntime = {
|
|
|
6068
6163
|
commandControlLaneV1: workerCommandLauncher?.supportsControlLane === true,
|
|
6069
6164
|
projectMirrorLeaseV1: true,
|
|
6070
6165
|
projectMirrorRefsTokensV1: true,
|
|
6071
|
-
projectBranchWorkingTreeModeV1: true
|
|
6166
|
+
projectBranchWorkingTreeModeV1: true,
|
|
6167
|
+
commandAdditionalTargetsV1: true,
|
|
6168
|
+
workspaceHydrationRecoveryV1: true
|
|
6072
6169
|
},
|
|
6073
6170
|
root: rootDir
|
|
6074
6171
|
}
|
|
@@ -6304,7 +6401,7 @@ async function startWorker(options, projectRuntime = {
|
|
|
6304
6401
|
}
|
|
6305
6402
|
const digest = (0, import_recovery_store.workerOperationDigest)(message);
|
|
6306
6403
|
const cached = projectRuntime.configuration;
|
|
6307
|
-
const canReuseConfiguration = cached?.digest === digest && cached.workspaceSync?.remoteUrl === message.workspaceRemoteUrl && Boolean(cached.workspaceSync.credentialHelper) && cached.workspaceSync.credentialUsername === workerCredentialUsername && cached.workspaceSync.gitIdentity?.name === message.gitIdentity.name && cached.workspaceSync.gitIdentity?.email === message.gitIdentity.email && !unsafeWorkspaceSyncBouncePending && credentialGenerationFingerprint({ ...message, workerBaseUrl: baseUrl, workerAuthHeader: bearerAuthHeader }) === configuredCredentialGenerationFingerprint;
|
|
6404
|
+
const canReuseConfiguration = !message.recoverHydrationTransactionId && !hydrationRecoveryRequired && cached?.digest === digest && cached.workspaceSync?.remoteUrl === message.workspaceRemoteUrl && Boolean(cached.workspaceSync.credentialHelper) && cached.workspaceSync.credentialUsername === workerCredentialUsername && cached.workspaceSync.gitIdentity?.name === message.gitIdentity.name && cached.workspaceSync.gitIdentity?.email === message.gitIdentity.email && !unsafeWorkspaceSyncBouncePending && credentialGenerationFingerprint({ ...message, workerBaseUrl: baseUrl, workerAuthHeader: bearerAuthHeader }) === configuredCredentialGenerationFingerprint;
|
|
6308
6405
|
if (!canReuseConfiguration) {
|
|
6309
6406
|
advanceWorkerAdmissionGeneration();
|
|
6310
6407
|
projectRuntime.configuration = void 0;
|
|
@@ -6316,7 +6413,8 @@ async function startWorker(options, projectRuntime = {
|
|
|
6316
6413
|
result: configured.result,
|
|
6317
6414
|
pendingCheckouts: configured.pending,
|
|
6318
6415
|
aheadOfOriginBranches: configured.aheadOfOriginBranches,
|
|
6319
|
-
...configured.deferredWorkspaceSyncForIncidentId ? { deferredWorkspaceSyncForIncidentId: configured.deferredWorkspaceSyncForIncidentId } : {}
|
|
6416
|
+
...configured.deferredWorkspaceSyncForIncidentId ? { deferredWorkspaceSyncForIncidentId: configured.deferredWorkspaceSyncForIncidentId } : {},
|
|
6417
|
+
...configured.deferredForBusyProjectIds ? { deferredForBusyProjectIds: configured.deferredForBusyProjectIds } : {}
|
|
6320
6418
|
});
|
|
6321
6419
|
if (receiptGeneration !== workspaceConfigurationReceiptGeneration || currentWorkerSocket !== ws || ws.readyState !== WebSocket.OPEN) {
|
|
6322
6420
|
sendConfigured();
|
|
@@ -6347,7 +6445,17 @@ async function startWorker(options, projectRuntime = {
|
|
|
6347
6445
|
if (configured.result.outcome !== "failed") unsafeWorkspaceSyncRecoveryAttempts = 0;
|
|
6348
6446
|
sendConfigured();
|
|
6349
6447
|
sendLifecycleProgress(
|
|
6350
|
-
configured.result.
|
|
6448
|
+
configured.result.hydrationRecovery ? {
|
|
6449
|
+
phase: "failed",
|
|
6450
|
+
operationId: configured.result.attemptId,
|
|
6451
|
+
detail: `Hydration recovery required: ${configured.result.hydrationRecovery.transactionId}; files and snapshots preserved`
|
|
6452
|
+
} : configured.deferredForBusyProjectIds ? {
|
|
6453
|
+
// Not a failure: the previous generation stays on disk and the
|
|
6454
|
+
// server retries once the busy checkouts are idle.
|
|
6455
|
+
phase: "configuring",
|
|
6456
|
+
operationId: configured.result.attemptId,
|
|
6457
|
+
detail: `Workspace configuration deferred while checkout activity is active on ${configured.deferredForBusyProjectIds.length} project(s)`
|
|
6458
|
+
} : configured.result.outcome === "failed" ? {
|
|
6351
6459
|
phase: "failed",
|
|
6352
6460
|
operationId: configured.result.attemptId,
|
|
6353
6461
|
detail: configured.result.error ?? "Workspace configuration failed"
|
|
@@ -6360,10 +6468,12 @@ async function startWorker(options, projectRuntime = {
|
|
|
6360
6468
|
}
|
|
6361
6469
|
);
|
|
6362
6470
|
process.stdout.write(
|
|
6363
|
-
`[r5d-worker] workspace
|
|
6471
|
+
configured.result.hydrationRecovery ? `[r5d-worker] workspace hydration recovery required for ${configured.result.hydrationRecovery.transactionId}; execution remains fenced, files and snapshots preserved
|
|
6472
|
+
` : configured.deferredForBusyProjectIds ? `[r5d-worker] workspace configuration deferred while visible checkout activity is active on ${configured.deferredForBusyProjectIds.join(", ")}: ${configured.pending.length} checkout(s) pending until the server retries
|
|
6473
|
+
` : `[r5d-worker] workspace configured: ${message.projects.length} project(s), ${configured.pending.length} pending checkout(s)
|
|
6364
6474
|
`
|
|
6365
6475
|
);
|
|
6366
|
-
if (refreshesDeferredConfiguration && configured.result.outcome === "failed" && deferredWorkspaceConfiguration !== null && currentWorkerSocket === ws) {
|
|
6476
|
+
if (refreshesDeferredConfiguration && configured.result.outcome === "failed" && configured.result.failureReason !== "hydration_recovery_required" && !configured.deferredForBusyProjectIds && deferredWorkspaceConfiguration !== null && currentWorkerSocket === ws) {
|
|
6367
6477
|
workspaceConfigured = false;
|
|
6368
6478
|
advanceWorkerAdmissionGeneration();
|
|
6369
6479
|
ws.close(1012, "Deferred workspace configuration refresh failed");
|
|
@@ -6755,6 +6865,7 @@ async function startWorker(options, projectRuntime = {
|
|
|
6755
6865
|
if (active.workspaceEffect !== "none" && targetMayMutateVisibleWorkspace(active.target)) {
|
|
6756
6866
|
recordVisibleWorkspaceMutation(active.target);
|
|
6757
6867
|
}
|
|
6868
|
+
if (active.workspaceEffect !== "none") recordAdditionalTargetMutations(active.additionalTargets);
|
|
6758
6869
|
try {
|
|
6759
6870
|
let bytesWritten = 0;
|
|
6760
6871
|
if (message.data !== void 0 && message.data.length > 0) {
|
|
@@ -6804,16 +6915,19 @@ async function startWorker(options, projectRuntime = {
|
|
|
6804
6915
|
let resourcesTransferred = false;
|
|
6805
6916
|
let releaseWorkspaceMutation;
|
|
6806
6917
|
let mutationLeaseTransferred = false;
|
|
6918
|
+
let additionalTargets = [];
|
|
6807
6919
|
try {
|
|
6920
|
+
additionalTargets = (0, import_workspace_command_targets.normalizeCommandAdditionalTargets)(message.target, message.additionalTargets, commandTargetValidators);
|
|
6808
6921
|
resources = await acquireWorkerCommandLaunch(ws, message, assertMessageAdmission);
|
|
6809
6922
|
const reservationAbort = registerPendingReservation(`pty:${message.ptyId}`, void 0);
|
|
6810
6923
|
try {
|
|
6811
6924
|
await (0, import_workspace_command_sync_policy.reserveWorkspaceCommandAfterCurrentSync)(
|
|
6812
|
-
message.target,
|
|
6925
|
+
(0, import_workspace_command_targets.commandTargets)(message.target, additionalTargets),
|
|
6813
6926
|
workspaceSyncSingleFlight,
|
|
6814
6927
|
() => {
|
|
6815
|
-
workspaceSyncPriorityPtyTargets.set(message.ptyId, message.target);
|
|
6928
|
+
workspaceSyncPriorityPtyTargets.set(message.ptyId, (0, import_workspace_command_targets.commandTargets)(message.target, additionalTargets));
|
|
6816
6929
|
if (targetMayMutateVisibleWorkspace(message.target)) recordVisibleWorkspaceMutation(message.target);
|
|
6930
|
+
recordAdditionalTargetMutations(additionalTargets);
|
|
6817
6931
|
},
|
|
6818
6932
|
{ signal: reservationAbort.signal }
|
|
6819
6933
|
);
|
|
@@ -6826,19 +6940,26 @@ async function startWorker(options, projectRuntime = {
|
|
|
6826
6940
|
`Shell opening was fenced while queued by workspace incident ${deferredWorkspaceConfiguration?.incidentId ?? activeWorkspaceIncidentId}`
|
|
6827
6941
|
);
|
|
6828
6942
|
}
|
|
6829
|
-
const resolvedTarget =
|
|
6830
|
-
process.stdout.write(
|
|
6831
|
-
`)
|
|
6943
|
+
const resolvedTarget = resolveCommandMessageTarget(message.target, additionalTargets);
|
|
6944
|
+
process.stdout.write(
|
|
6945
|
+
`[r5d-worker] pty ${message.ptyId}: ${describeWorkerSessionTarget(message.target)}${(0, import_workspace_command_targets.describeCommandAdditionalTargets)(additionalTargets)}
|
|
6946
|
+
`
|
|
6947
|
+
);
|
|
6832
6948
|
await openPty({
|
|
6833
6949
|
resources,
|
|
6834
6950
|
ws,
|
|
6835
6951
|
message,
|
|
6836
6952
|
resolvedTarget,
|
|
6953
|
+
additionalTargets,
|
|
6837
6954
|
rootDir,
|
|
6838
|
-
assertAdmission:
|
|
6955
|
+
assertAdmission: () => {
|
|
6956
|
+
assertMessageAdmission();
|
|
6957
|
+
resolveCommandMessageTarget(message.target, additionalTargets);
|
|
6958
|
+
},
|
|
6839
6959
|
...releaseWorkspaceMutation ? { releaseWorkspaceMutation } : {},
|
|
6840
6960
|
...targetMayMutateVisibleWorkspace(message.target) ? {
|
|
6841
6961
|
onTerminal: () => {
|
|
6962
|
+
recordAdditionalTargetMutations(additionalTargets);
|
|
6842
6963
|
markWorkspaceDirty({ type: "process_terminal", detail: `pty ${message.ptyId} completed` }, true, message.target);
|
|
6843
6964
|
}
|
|
6844
6965
|
} : {}
|
|
@@ -6871,6 +6992,7 @@ async function startWorker(options, projectRuntime = {
|
|
|
6871
6992
|
return;
|
|
6872
6993
|
}
|
|
6873
6994
|
if (activePty && targetMayMutateVisibleWorkspace(activePty.target)) {
|
|
6995
|
+
recordAdditionalTargetMutations(activePty.additionalTargets);
|
|
6874
6996
|
markWorkspaceDirty({ type: "shell_inline", detail: `pty ${message.ptyId}` }, false, activePty.target);
|
|
6875
6997
|
}
|
|
6876
6998
|
writePty(ws, message);
|
|
@@ -6887,6 +7009,7 @@ async function startWorker(options, projectRuntime = {
|
|
|
6887
7009
|
abortPendingReservation(`pty:${message.ptyId}`, "Shell closed before it started");
|
|
6888
7010
|
await closePty(message);
|
|
6889
7011
|
if (activePty && targetMayMutateVisibleWorkspace(activePty.target)) {
|
|
7012
|
+
recordAdditionalTargetMutations(activePty.additionalTargets);
|
|
6890
7013
|
markWorkspaceDirty({ type: "process_terminal", detail: `pty ${message.ptyId} closed` }, true, activePty.target);
|
|
6891
7014
|
}
|
|
6892
7015
|
sendWorkerMessage(ws, { type: "operation_completed", requestId: message.requestId });
|
|
@@ -6918,18 +7041,21 @@ async function startWorker(options, projectRuntime = {
|
|
|
6918
7041
|
let resources;
|
|
6919
7042
|
let targetReserved = false;
|
|
6920
7043
|
const hasWorkspaceEffect = workerCommandHasWorkspaceEffect(message);
|
|
7044
|
+
let additionalTargets = [];
|
|
6921
7045
|
try {
|
|
7046
|
+
additionalTargets = (0, import_workspace_command_targets.normalizeCommandAdditionalTargets)(message.target, message.additionalTargets, commandTargetValidators);
|
|
6922
7047
|
resources = await acquireWorkerCommandLaunch(ws, message, assertMessageAdmission);
|
|
6923
7048
|
if (hasWorkspaceEffect) {
|
|
6924
7049
|
const reservationAbort = registerPendingReservation(`run:${message.runId}`, message.sessionId);
|
|
6925
7050
|
try {
|
|
6926
7051
|
await (0, import_workspace_command_sync_policy.reserveWorkspaceCommandAfterCurrentSync)(
|
|
6927
|
-
message.target,
|
|
7052
|
+
(0, import_workspace_command_targets.commandTargets)(message.target, additionalTargets),
|
|
6928
7053
|
workspaceSyncSingleFlight,
|
|
6929
7054
|
() => {
|
|
6930
|
-
workspaceSyncPriorityProcessTargets.set(message.runId, message.target);
|
|
7055
|
+
workspaceSyncPriorityProcessTargets.set(message.runId, (0, import_workspace_command_targets.commandTargets)(message.target, additionalTargets));
|
|
6931
7056
|
targetReserved = true;
|
|
6932
7057
|
if (targetMayMutateVisibleWorkspace(message.target)) recordVisibleWorkspaceMutation(message.target);
|
|
7058
|
+
recordAdditionalTargetMutations(additionalTargets);
|
|
6933
7059
|
},
|
|
6934
7060
|
{ signal: reservationAbort.signal }
|
|
6935
7061
|
);
|
|
@@ -6943,13 +7069,16 @@ async function startWorker(options, projectRuntime = {
|
|
|
6943
7069
|
`One-shot command was fenced while queued by workspace incident ${deferredWorkspaceConfiguration?.incidentId ?? activeWorkspaceIncidentId}`
|
|
6944
7070
|
);
|
|
6945
7071
|
}
|
|
6946
|
-
const resolvedTarget =
|
|
6947
|
-
process.stdout.write(
|
|
6948
|
-
`)
|
|
7072
|
+
const resolvedTarget = resolveCommandMessageTarget(message.target, additionalTargets);
|
|
7073
|
+
process.stdout.write(
|
|
7074
|
+
`[r5d-worker] exec ${message.runId}: ${message.argv.join(" ")}${(0, import_workspace_command_targets.describeCommandAdditionalTargets)(additionalTargets)}
|
|
7075
|
+
`
|
|
7076
|
+
);
|
|
6949
7077
|
return await executeCommand({
|
|
6950
7078
|
resources,
|
|
6951
7079
|
message,
|
|
6952
7080
|
resolvedTarget,
|
|
7081
|
+
additionalTargets,
|
|
6953
7082
|
baseUrl,
|
|
6954
7083
|
token,
|
|
6955
7084
|
rootDir,
|
|
@@ -6967,6 +7096,7 @@ async function startWorker(options, projectRuntime = {
|
|
|
6967
7096
|
);
|
|
6968
7097
|
}
|
|
6969
7098
|
assertMessageAdmission();
|
|
7099
|
+
resolveCommandMessageTarget(message.target, additionalTargets);
|
|
6970
7100
|
}
|
|
6971
7101
|
});
|
|
6972
7102
|
};
|
|
@@ -6992,6 +7122,7 @@ async function startWorker(options, projectRuntime = {
|
|
|
6992
7122
|
}
|
|
6993
7123
|
sendSerializedWorkerMessage(ws, JSON.stringify(result));
|
|
6994
7124
|
if (hasWorkspaceEffect && targetMayMutateVisibleWorkspace(message.target)) {
|
|
7125
|
+
recordAdditionalTargetMutations(additionalTargets);
|
|
6995
7126
|
markWorkspaceDirty(
|
|
6996
7127
|
{ type: "shell_inline", sessionId: message.sessionId, processRunId: message.runId, detail: "foreground command completed" },
|
|
6997
7128
|
true,
|
|
@@ -7017,11 +7148,23 @@ async function startWorker(options, projectRuntime = {
|
|
|
7017
7148
|
});
|
|
7018
7149
|
const hasWorkspaceEffect = workerCommandHasWorkspaceEffect(message);
|
|
7019
7150
|
let resources;
|
|
7151
|
+
let additionalTargets;
|
|
7152
|
+
try {
|
|
7153
|
+
additionalTargets = (0, import_workspace_command_targets.normalizeCommandAdditionalTargets)(message.target, message.additionalTargets, commandTargetValidators);
|
|
7154
|
+
} catch (error) {
|
|
7155
|
+
sendWorkerMessageFromCurrentSource(ws, {
|
|
7156
|
+
type: "exec_start_error",
|
|
7157
|
+
requestId: message.requestId,
|
|
7158
|
+
runId: message.runId,
|
|
7159
|
+
error: error instanceof Error ? error.message : String(error)
|
|
7160
|
+
});
|
|
7161
|
+
return;
|
|
7162
|
+
}
|
|
7020
7163
|
const runCommand = async () => {
|
|
7021
7164
|
try {
|
|
7022
|
-
const resolvedTarget =
|
|
7165
|
+
const resolvedTarget = resolveCommandMessageTarget(message.target, additionalTargets);
|
|
7023
7166
|
process.stdout.write(
|
|
7024
|
-
`[r5d-worker] exec_start ${message.runId}${message.commandClass === "control" ? " (control)" : ""}: ${message.argv.join(" ")}
|
|
7167
|
+
`[r5d-worker] exec_start ${message.runId}${message.commandClass === "control" ? " (control)" : ""}: ${message.argv.join(" ")}${(0, import_workspace_command_targets.describeCommandAdditionalTargets)(additionalTargets)}
|
|
7025
7168
|
`
|
|
7026
7169
|
);
|
|
7027
7170
|
await executeStreamingCommand({
|
|
@@ -7029,6 +7172,7 @@ async function startWorker(options, projectRuntime = {
|
|
|
7029
7172
|
ws,
|
|
7030
7173
|
message,
|
|
7031
7174
|
resolvedTarget,
|
|
7175
|
+
additionalTargets,
|
|
7032
7176
|
baseUrl,
|
|
7033
7177
|
token,
|
|
7034
7178
|
rootDir,
|
|
@@ -7039,10 +7183,14 @@ async function startWorker(options, projectRuntime = {
|
|
|
7039
7183
|
active
|
|
7040
7184
|
});
|
|
7041
7185
|
},
|
|
7042
|
-
assertAdmission:
|
|
7186
|
+
assertAdmission: () => {
|
|
7187
|
+
assertMessageAdmission();
|
|
7188
|
+
resolveCommandMessageTarget(message.target, additionalTargets);
|
|
7189
|
+
}
|
|
7043
7190
|
});
|
|
7044
7191
|
} finally {
|
|
7045
7192
|
if (hasWorkspaceEffect && targetMayMutateVisibleWorkspace(message.target)) {
|
|
7193
|
+
recordAdditionalTargetMutations(additionalTargets);
|
|
7046
7194
|
markWorkspaceDirty(
|
|
7047
7195
|
{
|
|
7048
7196
|
type: "process_terminal",
|
|
@@ -7064,12 +7212,13 @@ async function startWorker(options, projectRuntime = {
|
|
|
7064
7212
|
const reservationAbort = registerPendingReservation(`run:${message.runId}`, message.sessionId);
|
|
7065
7213
|
try {
|
|
7066
7214
|
await (0, import_workspace_command_sync_policy.reserveWorkspaceCommandAfterCurrentSync)(
|
|
7067
|
-
message.target,
|
|
7215
|
+
(0, import_workspace_command_targets.commandTargets)(message.target, additionalTargets),
|
|
7068
7216
|
workspaceSyncSingleFlight,
|
|
7069
7217
|
() => {
|
|
7070
|
-
workspaceSyncPriorityProcessTargets.set(message.runId, message.target);
|
|
7218
|
+
workspaceSyncPriorityProcessTargets.set(message.runId, (0, import_workspace_command_targets.commandTargets)(message.target, additionalTargets));
|
|
7071
7219
|
targetReserved = true;
|
|
7072
7220
|
if (targetMayMutateVisibleWorkspace(message.target)) recordVisibleWorkspaceMutation(message.target);
|
|
7221
|
+
recordAdditionalTargetMutations(additionalTargets);
|
|
7073
7222
|
},
|
|
7074
7223
|
{ signal: reservationAbort.signal }
|
|
7075
7224
|
);
|
package/dist/cjs/package.json
CHANGED