@ricsam/r5d-worker 0.0.78 → 0.0.80

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/README.md +6 -0
  2. package/dist/cjs/main.cjs +1550 -335
  3. package/dist/cjs/managed-paths.cjs +101 -1
  4. package/dist/cjs/package.json +1 -1
  5. package/dist/cjs/project-workspace-state.cjs +184 -5
  6. package/dist/cjs/project-worktrees.cjs +676 -58
  7. package/dist/cjs/registry-auth.cjs +310 -0
  8. package/dist/cjs/repository-transition-policy.cjs +49 -0
  9. package/dist/cjs/supervisor.cjs +62 -11
  10. package/dist/cjs/working-tree-mirror.cjs +196 -36
  11. package/dist/cjs/workspace-automatic-sync-policy.cjs +69 -0
  12. package/dist/cjs/workspace-branch-incarnation-policy.cjs +37 -0
  13. package/dist/cjs/workspace-command-sync-policy.cjs +18 -0
  14. package/dist/cjs/workspace-git-sync.cjs +1037 -54
  15. package/dist/cjs/workspace-mount-boundary.cjs +71 -0
  16. package/dist/cjs/workspace-path-move.cjs +195 -0
  17. package/dist/cjs/workspace-preserve-only-policy.cjs +36 -0
  18. package/dist/cjs/workspace-project-config-policy.cjs +46 -0
  19. package/dist/cjs/workspace-publication-evidence.cjs +46 -0
  20. package/dist/mjs/main.mjs +1584 -341
  21. package/dist/mjs/managed-paths.mjs +100 -1
  22. package/dist/mjs/package.json +1 -1
  23. package/dist/mjs/project-workspace-state.mjs +181 -5
  24. package/dist/mjs/project-worktrees.mjs +667 -57
  25. package/dist/mjs/registry-auth.mjs +269 -0
  26. package/dist/mjs/repository-transition-policy.mjs +22 -0
  27. package/dist/mjs/supervisor.mjs +61 -11
  28. package/dist/mjs/working-tree-mirror.mjs +195 -36
  29. package/dist/mjs/workspace-automatic-sync-policy.mjs +40 -0
  30. package/dist/mjs/workspace-branch-incarnation-policy.mjs +13 -0
  31. package/dist/mjs/workspace-command-sync-policy.mjs +17 -0
  32. package/dist/mjs/workspace-git-sync.mjs +1032 -54
  33. package/dist/mjs/workspace-mount-boundary.mjs +37 -0
  34. package/dist/mjs/workspace-path-move.mjs +160 -0
  35. package/dist/mjs/workspace-preserve-only-policy.mjs +11 -0
  36. package/dist/mjs/workspace-project-config-policy.mjs +21 -0
  37. package/dist/mjs/workspace-publication-evidence.mjs +21 -0
  38. package/dist/types/credential-authority-lock-fixture.d.ts +1 -0
  39. package/dist/types/main.d.ts +175 -1
  40. package/dist/types/managed-paths.d.ts +11 -0
  41. package/dist/types/project-workspace-state.d.ts +45 -1
  42. package/dist/types/project-worktrees.d.ts +119 -2
  43. package/dist/types/registry-auth.d.ts +47 -0
  44. package/dist/types/repository-transition-policy.d.ts +26 -0
  45. package/dist/types/supervisor-daemonized-fixture.d.ts +1 -0
  46. package/dist/types/supervisor-signal-fixture.d.ts +1 -0
  47. package/dist/types/supervisor.d.ts +6 -4
  48. package/dist/types/working-tree-mirror.d.ts +14 -0
  49. package/dist/types/workspace-automatic-sync-policy.d.ts +37 -0
  50. package/dist/types/workspace-branch-incarnation-policy.d.ts +14 -0
  51. package/dist/types/workspace-command-sync-policy.d.ts +9 -0
  52. package/dist/types/workspace-git-sync.d.ts +33 -3
  53. package/dist/types/workspace-mount-boundary.d.ts +10 -0
  54. package/dist/types/workspace-path-move.d.ts +21 -0
  55. package/dist/types/workspace-preserve-only-policy.d.ts +15 -0
  56. package/dist/types/workspace-project-config-policy.d.ts +34 -0
  57. package/dist/types/workspace-publication-evidence.d.ts +17 -0
  58. package/package.json +1 -1
@@ -0,0 +1,37 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ function containedRelative(root, candidate) {
4
+ const relative = path.relative(root, candidate);
5
+ if (!relative || relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {
6
+ throw new Error(`Managed directory path escapes its trusted root: ${candidate}`);
7
+ }
8
+ return relative;
9
+ }
10
+ function assertManagedDirectoryPath(input) {
11
+ const trustedRoot = path.resolve(input.trustedRoot);
12
+ const candidate = path.resolve(input.candidate);
13
+ const relative = containedRelative(trustedRoot, candidate);
14
+ const rootStatus = fs.lstatSync(trustedRoot);
15
+ if (!rootStatus.isDirectory() || rootStatus.isSymbolicLink()) {
16
+ throw new Error(`${input.label} trusted root is not a regular directory: ${trustedRoot}`);
17
+ }
18
+ const realRoot = fs.realpathSync(trustedRoot);
19
+ let current = trustedRoot;
20
+ for (const segment of relative.split(path.sep)) {
21
+ current = path.join(current, segment);
22
+ let status;
23
+ try {
24
+ status = fs.lstatSync(current);
25
+ } catch (error) {
26
+ if (error.code === "ENOENT") return;
27
+ throw error;
28
+ }
29
+ if (!status.isDirectory() || status.isSymbolicLink()) {
30
+ throw new Error(`${input.label} contains a symlink or non-directory component: ${current}`);
31
+ }
32
+ containedRelative(realRoot, fs.realpathSync(current));
33
+ }
34
+ }
35
+ export {
36
+ assertManagedDirectoryPath
37
+ };
@@ -0,0 +1,160 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { createHash } from "node:crypto";
4
+ import { projectWorktreeOperationInProgress } from "./project-worktrees.mjs";
5
+ import { validateManagedBranchName } from "./managed-paths.mjs";
6
+ import { assertWorkingTreeHasNoPortableGitMetadataAliases, mirrorWorkingTree } from "./working-tree-mirror.mjs";
7
+ import { assertManagedDirectoryPath } from "./workspace-mount-boundary.mjs";
8
+ function checkoutPathMovePublicationComplete(requiredMountIds, activeMountIds) {
9
+ return requiredMountIds.length > 0 && requiredMountIds.every((mountId) => activeMountIds.has(mountId));
10
+ }
11
+ function pathIsInside(root, candidate) {
12
+ const relative = path.relative(path.resolve(root), path.resolve(candidate));
13
+ return relative !== "" && relative !== ".." && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);
14
+ }
15
+ function fsyncTree(targetPath) {
16
+ const stat = fs.lstatSync(targetPath);
17
+ if (stat.isSymbolicLink()) return;
18
+ if (stat.isDirectory()) {
19
+ for (const entry of fs.readdirSync(targetPath).sort()) fsyncTree(path.join(targetPath, entry));
20
+ }
21
+ if (!stat.isDirectory() && !stat.isFile()) return;
22
+ const descriptor = fs.openSync(targetPath, "r");
23
+ try {
24
+ fs.fsyncSync(descriptor);
25
+ } finally {
26
+ fs.closeSync(descriptor);
27
+ }
28
+ }
29
+ function fsyncDirectory(directoryPath) {
30
+ const descriptor = fs.openSync(directoryPath, "r");
31
+ try {
32
+ fs.fsyncSync(descriptor);
33
+ } finally {
34
+ fs.closeSync(descriptor);
35
+ }
36
+ }
37
+ function fsyncAncestorsToRoot(targetPath, durabilityRoot) {
38
+ const root = path.resolve(durabilityRoot);
39
+ let current = path.dirname(path.resolve(targetPath));
40
+ while (true) {
41
+ if (fs.existsSync(current)) {
42
+ const status = fs.lstatSync(current);
43
+ if (!status.isDirectory() || status.isSymbolicLink()) {
44
+ throw new Error(`Project checkout path move durability boundary is not a regular directory: ${current}`);
45
+ }
46
+ fsyncDirectory(current);
47
+ }
48
+ if (current === root) return;
49
+ const parent = path.dirname(current);
50
+ if (parent === current) throw new Error(`Project checkout path move escapes durability root ${root}`);
51
+ current = parent;
52
+ }
53
+ }
54
+ function stableTree(targetPath, durabilityRoot) {
55
+ fsyncTree(targetPath);
56
+ fsyncAncestorsToRoot(targetPath, durabilityRoot);
57
+ }
58
+ function directoryEntryExists(targetPath) {
59
+ try {
60
+ fs.lstatSync(targetPath);
61
+ return true;
62
+ } catch (error) {
63
+ if (error.code === "ENOENT") return false;
64
+ throw error;
65
+ }
66
+ }
67
+ function retiredCheckoutPath(projectsDurabilityRoot, oldProjectRoot) {
68
+ const storageRoot = path.join(projectsDurabilityRoot, ".r5d-retired-checkouts");
69
+ const digest = createHash("sha256").update(oldProjectRoot).digest("hex");
70
+ return { storageRoot, retiredRoot: path.join(storageRoot, digest) };
71
+ }
72
+ function removeRetiredCheckout(retiredRoot, storageRoot) {
73
+ if (!directoryEntryExists(retiredRoot)) return;
74
+ fs.rmSync(retiredRoot, { recursive: true, force: true });
75
+ fsyncDirectory(storageRoot);
76
+ }
77
+ function preserveProjectCheckoutPathMove(input) {
78
+ const oldProjectRoot = path.resolve(input.oldProjectRoot);
79
+ const newProjectRoot = path.resolve(input.newProjectRoot);
80
+ const projectsDurabilityRoot = path.resolve(input.projectsDurabilityRoot);
81
+ const outerWorkspaceRoot = path.resolve(input.outerWorkspaceRoot);
82
+ if (oldProjectRoot === newProjectRoot || pathIsInside(oldProjectRoot, newProjectRoot) || pathIsInside(newProjectRoot, oldProjectRoot)) {
83
+ throw new Error("Project checkout path move roots overlap");
84
+ }
85
+ const branchNames = input.branches.map(({ branchName }) => branchName);
86
+ if (branchNames.length === 0 || new Set(branchNames).size !== branchNames.length) {
87
+ throw new Error("Project checkout path move requires unique branches");
88
+ }
89
+ assertManagedDirectoryPath({
90
+ trustedRoot: projectsDurabilityRoot,
91
+ candidate: oldProjectRoot,
92
+ label: "Old project checkout root"
93
+ });
94
+ assertManagedDirectoryPath({
95
+ trustedRoot: projectsDurabilityRoot,
96
+ candidate: newProjectRoot,
97
+ label: "New project checkout root"
98
+ });
99
+ for (const branchName of branchNames) validateManagedBranchName(branchName);
100
+ if (directoryEntryExists(oldProjectRoot)) {
101
+ assertWorkingTreeHasNoPortableGitMetadataAliases(oldProjectRoot);
102
+ }
103
+ const { storageRoot: retirementStorageRoot, retiredRoot } = retiredCheckoutPath(projectsDurabilityRoot, oldProjectRoot);
104
+ assertManagedDirectoryPath({
105
+ trustedRoot: projectsDurabilityRoot,
106
+ candidate: retirementStorageRoot,
107
+ label: "Retired project checkout storage"
108
+ });
109
+ fs.mkdirSync(retirementStorageRoot, { recursive: true, mode: 448 });
110
+ assertManagedDirectoryPath({
111
+ trustedRoot: projectsDurabilityRoot,
112
+ candidate: retirementStorageRoot,
113
+ label: "Retired project checkout storage"
114
+ });
115
+ fsyncDirectory(retirementStorageRoot);
116
+ fsyncDirectory(projectsDurabilityRoot);
117
+ assertManagedDirectoryPath({
118
+ trustedRoot: projectsDurabilityRoot,
119
+ candidate: retiredRoot,
120
+ label: "Retired project checkout"
121
+ });
122
+ removeRetiredCheckout(retiredRoot, retirementStorageRoot);
123
+ if (!directoryEntryExists(oldProjectRoot)) return { preservedBranchNames: [] };
124
+ const preservedBranchNames = [];
125
+ for (const branch of input.branches) {
126
+ validateManagedBranchName(branch.branchName);
127
+ const oldBranchPath = path.join(oldProjectRoot, ...branch.branchName.split("/"));
128
+ if (!fs.existsSync(oldBranchPath)) continue;
129
+ const newBranchPath = path.join(newProjectRoot, ...branch.branchName.split("/"));
130
+ const outerBranchPath = path.resolve(outerWorkspaceRoot, ...branch.outerWorkspaceRelativePath.replace(/\\/g, "/").split("/"));
131
+ if (!pathIsInside(outerWorkspaceRoot, outerBranchPath)) {
132
+ throw new Error(`Project checkout path move escapes the outer workspace: ${branch.outerWorkspaceRelativePath}`);
133
+ }
134
+ assertManagedDirectoryPath({ trustedRoot: projectsDurabilityRoot, candidate: oldBranchPath, label: "Old project checkout branch" });
135
+ assertManagedDirectoryPath({ trustedRoot: projectsDurabilityRoot, candidate: newBranchPath, label: "New project checkout branch" });
136
+ assertManagedDirectoryPath({ trustedRoot: outerWorkspaceRoot, candidate: outerBranchPath, label: "Outer workspace branch" });
137
+ mirrorWorkingTree({ sourceRoot: oldBranchPath, targetRoot: outerBranchPath, sourceMode: "git", deletionMode: "all" });
138
+ mirrorWorkingTree({ sourceRoot: oldBranchPath, targetRoot: newBranchPath, sourceMode: "all", deletionMode: "all" });
139
+ assertManagedDirectoryPath({ trustedRoot: projectsDurabilityRoot, candidate: newBranchPath, label: "New project checkout branch" });
140
+ assertManagedDirectoryPath({ trustedRoot: outerWorkspaceRoot, candidate: outerBranchPath, label: "Outer workspace branch" });
141
+ stableTree(outerBranchPath, outerWorkspaceRoot);
142
+ stableTree(newBranchPath, projectsDurabilityRoot);
143
+ preservedBranchNames.push(branch.branchName);
144
+ }
145
+ for (const branchName of branchNames) {
146
+ const checkoutPath = path.join(oldProjectRoot, ...branchName.split("/"));
147
+ if (fs.existsSync(checkoutPath) && projectWorktreeOperationInProgress(checkoutPath)) {
148
+ throw new Error(`Project branch ${branchName} has an in-progress Git operation`);
149
+ }
150
+ }
151
+ fs.renameSync(oldProjectRoot, retiredRoot);
152
+ fsyncAncestorsToRoot(oldProjectRoot, projectsDurabilityRoot);
153
+ fsyncDirectory(retirementStorageRoot);
154
+ removeRetiredCheckout(retiredRoot, retirementStorageRoot);
155
+ return { preservedBranchNames: preservedBranchNames.sort() };
156
+ }
157
+ export {
158
+ checkoutPathMovePublicationComplete,
159
+ preserveProjectCheckoutPathMove
160
+ };
@@ -0,0 +1,11 @@
1
+ function creatorLocalProjectBranchIsAuthorized(input) {
2
+ return Boolean(input.pendingBranchId && (!input.preserveOnlyBranchId || input.pendingBranchId === input.preserveOnlyBranchId));
3
+ }
4
+ function workerProjectBranchDisposition(input) {
5
+ if (input.activeConfigured) return "active";
6
+ return input.locallyPendingCreated ? "creator_local" : "preserve_only";
7
+ }
8
+ export {
9
+ creatorLocalProjectBranchIsAuthorized,
10
+ workerProjectBranchDisposition
11
+ };
@@ -0,0 +1,21 @@
1
+ function deferredProjectConfigurationPendingBranches(projects) {
2
+ return projects.flatMap((project) => project.branches.map(({ branchName }) => ({ projectId: project.projectId, branchName }))).sort((left, right) => left.projectId.localeCompare(right.projectId) || left.branchName.localeCompare(right.branchName));
3
+ }
4
+ function busyProjectConfigurationChangeIds(input) {
5
+ const currentById = new Map(input.currentProjects.map((project) => [project.projectId, project]));
6
+ const incomingById = new Map(input.incomingProjects.map((project) => [project.projectId, project]));
7
+ const targets = [...input.activeTargets];
8
+ const busyProjectIds = targets.some((target) => target.type === "workspace" && target.rootProfile === "visible_projects") ? /* @__PURE__ */ new Set([...currentById.keys(), ...incomingById.keys()]) : new Set(targets.flatMap((target) => target.type === "project" ? [target.projectId] : []));
9
+ return [...busyProjectIds].filter((projectId) => {
10
+ const current = currentById.get(projectId);
11
+ const incoming = incomingById.get(projectId);
12
+ if (!current && !incoming) return false;
13
+ if (!current || !incoming) return true;
14
+ if (!input.currentProjectReady(current)) return true;
15
+ return input.currentFingerprint(current) !== input.incomingFingerprint(incoming);
16
+ }).sort();
17
+ }
18
+ export {
19
+ busyProjectConfigurationChangeIds,
20
+ deferredProjectConfigurationPendingBranches
21
+ };
@@ -0,0 +1,21 @@
1
+ function projectBranchMountId(projectId, branchName) {
2
+ return `project:${projectId}:${encodeURIComponent(branchName)}`;
3
+ }
4
+ function activeProjectBranchNames(projectId, branchNames, activeMountIds) {
5
+ const active = new Set(activeMountIds);
6
+ return [...branchNames].filter((branchName) => active.has(projectBranchMountId(projectId, branchName))).sort((left, right) => left.localeCompare(right));
7
+ }
8
+ function activeProjectBranchPublicationEvidence(projects, activeMountIds) {
9
+ const active = new Set(activeMountIds);
10
+ return projects.flatMap(
11
+ (project) => project.executionDisabled || project.mirrorWritesDisabled ? [] : project.branches.flatMap(
12
+ (branch) => active.has(projectBranchMountId(project.projectId, branch.branchName)) ? [{ branchId: branch.branchId, projectId: project.projectId, branchName: branch.branchName }] : []
13
+ )
14
+ ).sort(
15
+ (left, right) => left.projectId.localeCompare(right.projectId) || left.branchName.localeCompare(right.branchName) || left.branchId.localeCompare(right.branchId)
16
+ );
17
+ }
18
+ export {
19
+ activeProjectBranchNames,
20
+ activeProjectBranchPublicationEvidence
21
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/env bun
2
+ import { Database } from "bun:sqlite";
3
+ import { configureGitHubRegistryAuthFiles, type PreparedPrivateAuthFileGeneration } from "./registry-auth";
2
4
  type WorkerProjectConfig = {
3
5
  projectId: string;
4
6
  checkoutPathSegments: [namespace: string, project: string];
@@ -6,7 +8,15 @@ type WorkerProjectConfig = {
6
8
  repoHttpUrl: string | null;
7
9
  repoAuthHeader: string | null;
8
10
  defaultBranch: string;
11
+ repositoryTransitionId: string | null;
12
+ executionDisabled: boolean;
13
+ mirrorWritesDisabled: boolean;
14
+ preserveOnlyBranches: Array<{
15
+ branchId: string;
16
+ branchName: string;
17
+ }>;
9
18
  branches: Array<{
19
+ branchId: string;
10
20
  branchName: string;
11
21
  sourceBranchName: string | null;
12
22
  baseCommitHash: string;
@@ -33,6 +43,13 @@ type WorkerReadFileResult = {
33
43
  truncated: boolean;
34
44
  continuation?: string;
35
45
  };
46
+ type WorkerGitHubCredential = {
47
+ host: "github.com";
48
+ registry: "ghcr.io";
49
+ username: string;
50
+ token: string;
51
+ missingScopes: Array<"repo" | "read:org" | "gist" | "workflow" | "read:packages" | "write:packages">;
52
+ };
36
53
  type WorkerWriteFileResult = {
37
54
  type: "write";
38
55
  file: string;
@@ -89,6 +106,14 @@ type WorkerCodeReadResult = {
89
106
  mtime: string;
90
107
  base64: string;
91
108
  };
109
+ declare function assertWorkerChildAdmission(input: {
110
+ capturedGeneration: number;
111
+ currentGeneration: number;
112
+ sourceSocketIsCurrent: boolean;
113
+ sourceSocketOpen: boolean;
114
+ workspaceConfigured: boolean;
115
+ runtimeHealthy: boolean;
116
+ }): void;
92
117
  export type WorkerBuiltInToolPaths = {
93
118
  artifactsDir?: string;
94
119
  plansDir?: string;
@@ -131,17 +156,118 @@ type ResolvedWorkerFilePath = {
131
156
  };
132
157
  export declare function resolveWorkerFilePath(branchPath: string, inputPath: string, builtInPaths?: WorkerBuiltInToolPaths): ResolvedWorkerFilePath;
133
158
  declare function readOnlyCredentialStoreHelper(storePath: string): string;
159
+ declare function credentialGenerationReceiptPath(directoryPath?: string): string;
160
+ declare function pendingCredentialGenerationPath(directoryPath?: string): string;
161
+ declare function bootstrapCredentialGenerationPath(directoryPath?: string): string;
162
+ type PendingCredentialGenerationBoundary = {
163
+ kind: "systemd";
164
+ machineId: string | null;
165
+ invocationId: string;
166
+ } | {
167
+ kind: "boot";
168
+ machineId: string | null;
169
+ bootSessionId: string | null;
170
+ };
171
+ type PendingCredentialGenerationIntent = {
172
+ fingerprint: string;
173
+ boundary: PendingCredentialGenerationBoundary;
174
+ };
175
+ declare function pendingCredentialGenerationIntentContent(intent: PendingCredentialGenerationIntent): string;
176
+ declare function readPendingCredentialGenerationIntent(intentPath?: string): PendingCredentialGenerationIntent | null;
177
+ type BootSessionIdentifierProbe = {
178
+ platform: NodeJS.Platform;
179
+ kernelValue: string;
180
+ };
181
+ declare function readBootSessionIdentifier(probe?: BootSessionIdentifierProbe): string | null;
182
+ type MachineIdentifierProbe = {
183
+ platform: NodeJS.Platform;
184
+ kernelValue: string;
185
+ };
186
+ declare function readMachineIdentifier(probe?: MachineIdentifierProbe): string | null;
187
+ type CredentialAuthorityLockHandle = {
188
+ lockPath: string;
189
+ database: Database;
190
+ };
191
+ declare function credentialAuthorityLockPath(rootPath?: string): string;
192
+ declare function acquireCredentialAuthorityLock(input: {
193
+ rootPath?: string;
194
+ machineId: string | null;
195
+ }): CredentialAuthorityLockHandle;
196
+ declare function releaseCredentialAuthorityLockHandle(handle: CredentialAuthorityLockHandle): void;
134
197
  declare function legacySharedCredentialStorePath(): string;
135
198
  declare function credentialStorePathForProject(projectId: string): string;
136
199
  declare function credentialStorePathForWorkspace(remoteUrl: string): string;
137
200
  declare function removeLegacySharedCredentialStore(storePath?: string): void;
138
201
  declare function resetCredentialStoreDirectory(directoryPath?: string): void;
202
+ declare function readCredentialGenerationReceipt(receiptPath?: string): string | null;
139
203
  declare function pruneCredentialStoreFiles(requiredStorePaths: ReadonlySet<string>, directoryPath?: string): string[];
140
204
  declare function replaceCredentialStoreFile(storePath: string, content: string): void;
141
205
  declare function credentialHelperForRemotes(remotes: readonly {
142
206
  remoteUrl: string;
143
207
  authHeader: string | null | undefined;
144
- }[], storePath: string): string | null;
208
+ }[], storePath: string, desiredFiles?: Map<string, string | null>): string | null;
209
+ type CredentialGenerationConfiguration = {
210
+ workerBaseUrl: string;
211
+ workerAuthHeader: string;
212
+ workspaceRemoteUrl: string;
213
+ githubCredential: WorkerGitHubCredential | null;
214
+ projects: readonly Pick<WorkerProjectConfig, "projectId" | "repoHttpUrl" | "repoAuthHeader">[];
215
+ };
216
+ declare function credentialGenerationFingerprint(config: CredentialGenerationConfiguration): string;
217
+ type CredentialGenerationTransitionPhase = "current" | "stage_pending_restart" | "publish_from_clean_restart";
218
+ declare function credentialGenerationTransitionPhase(input: {
219
+ committedFingerprint: string | null;
220
+ pendingFingerprintAtProcessStart: string | null;
221
+ incomingFingerprint: string;
222
+ }): CredentialGenerationTransitionPhase;
223
+ declare function initialCredentialBootstrapIsSafe(input: {
224
+ transitionPhase: CredentialGenerationTransitionPhase;
225
+ committedFingerprint: string | null;
226
+ pendingFingerprintAtProcessStart: string | null;
227
+ bootstrapFingerprintAtProcessStart: string | null;
228
+ incomingFingerprint: string;
229
+ credentialArtifactsProvenAbsentAtProcessStart: boolean;
230
+ trackedChildTerminationProven: boolean;
231
+ activeProcessCount: number;
232
+ credentialProcessGroupCount: number;
233
+ activePtyCount: number;
234
+ }): boolean;
235
+ declare function fenceCredentialGenerationAtConfigureEntry(input: {
236
+ currentFingerprint: string | null;
237
+ incomingFingerprint: string;
238
+ revokeInMemory: () => void;
239
+ terminatePreviousGeneration: () => Promise<void>;
240
+ }): Promise<boolean>;
241
+ declare function requireCredentialGenerationRestart(required: boolean, fatalExit?: (exitCode: number) => void, exitCode?: number): void;
242
+ declare function pendingCredentialGenerationBoundary(input: {
243
+ systemdContract: boolean;
244
+ systemdInvocationId: string | null;
245
+ bootSessionId: string | null;
246
+ machineId: string | null;
247
+ }): PendingCredentialGenerationBoundary;
248
+ declare function credentialGenerationBoundaryIsComplete(boundary: PendingCredentialGenerationBoundary): boolean;
249
+ declare function credentialGenerationRestartIsContained(input: {
250
+ systemdContract: boolean;
251
+ pendingBoundary: PendingCredentialGenerationBoundary | null;
252
+ currentSystemdInvocationId: string | null;
253
+ currentBootSessionId: string | null;
254
+ currentMachineId: string | null;
255
+ }): boolean;
256
+ type CredentialReapContractProbe = {
257
+ platform: NodeJS.Platform;
258
+ invocationId: string | undefined;
259
+ cgroupText: string;
260
+ unitProperties: string;
261
+ };
262
+ type CredentialReapContractStatus = "direct" | "systemd_unverified" | "verified_systemd";
263
+ declare function credentialReapContractStatus(probe?: CredentialReapContractProbe): CredentialReapContractStatus;
264
+ declare function verifiedCredentialReapContract(probe?: CredentialReapContractProbe): boolean;
265
+ declare function fenceUnsafeWorkspaceSyncFailure(input: {
266
+ hydrationCurrent: boolean;
267
+ invalidateExecution: () => void;
268
+ fatalExit?: (exitCode: number) => void;
269
+ exitCode?: number;
270
+ }): boolean;
145
271
  export declare const workerGitSecurityTestHarness: {
146
272
  commandArgs: (args: string[]) => string[];
147
273
  credentialHelperForRemotes: typeof credentialHelperForRemotes;
@@ -153,8 +279,47 @@ export declare const workerGitSecurityTestHarness: {
153
279
  readOnlyCredentialStoreHelper: typeof readOnlyCredentialStoreHelper;
154
280
  resetCredentialStoreDirectory: typeof resetCredentialStoreDirectory;
155
281
  replaceCredentialStoreFile: typeof replaceCredentialStoreFile;
282
+ configureGitHubAuth(credential: WorkerGitHubCredential | null, filePaths: readonly string[], configureFiles?: typeof configureGitHubRegistryAuthFiles): void;
283
+ githubCredential: () => WorkerGitHubCredential | null;
284
+ credentialGenerationFingerprint: typeof credentialGenerationFingerprint;
285
+ credentialGenerationTransitionPhase: typeof credentialGenerationTransitionPhase;
286
+ initialCredentialBootstrapIsSafe: typeof initialCredentialBootstrapIsSafe;
287
+ fenceCredentialGenerationAtConfigureEntry: typeof fenceCredentialGenerationAtConfigureEntry;
288
+ requireCredentialGenerationRestart: typeof requireCredentialGenerationRestart;
289
+ pendingCredentialGenerationBoundary: typeof pendingCredentialGenerationBoundary;
290
+ credentialGenerationBoundaryIsComplete: typeof credentialGenerationBoundaryIsComplete;
291
+ credentialGenerationRestartIsContained: typeof credentialGenerationRestartIsContained;
292
+ credentialReapContractStatus: typeof credentialReapContractStatus;
293
+ verifiedCredentialReapContract: typeof verifiedCredentialReapContract;
294
+ fenceUnsafeWorkspaceSyncFailure: typeof fenceUnsafeWorkspaceSyncFailure;
295
+ assertWorkerChildAdmission: typeof assertWorkerChildAdmission;
296
+ terminateCredentialBearingChildren: typeof terminateCredentialBearingChildren;
297
+ terminateCredentialBearingChildrenWithRetention: typeof terminateCredentialBearingChildrenWithRetention;
298
+ commitCredentialGeneration(prepared: PreparedPrivateAuthFileGeneration, credential: WorkerGitHubCredential | null, children: readonly {
299
+ label: string;
300
+ terminate: () => void | Promise<void>;
301
+ }[], beforeMutation?: (filePath: string, index: number) => void, previousGenerationFenced?: boolean): Promise<boolean>;
302
+ credentialGenerationReceiptPath: typeof credentialGenerationReceiptPath;
303
+ credentialAuthorityLockPath: typeof credentialAuthorityLockPath;
304
+ acquireCredentialAuthorityLock: typeof acquireCredentialAuthorityLock;
305
+ releaseCredentialAuthorityLockHandle: typeof releaseCredentialAuthorityLockHandle;
306
+ pendingCredentialGenerationPath: typeof pendingCredentialGenerationPath;
307
+ pendingCredentialGenerationIntentContent: typeof pendingCredentialGenerationIntentContent;
308
+ readPendingCredentialGenerationIntent: typeof readPendingCredentialGenerationIntent;
309
+ readBootSessionIdentifier: typeof readBootSessionIdentifier;
310
+ readMachineIdentifier: typeof readMachineIdentifier;
311
+ bootstrapCredentialGenerationPath: typeof bootstrapCredentialGenerationPath;
312
+ readCredentialGenerationReceipt: typeof readCredentialGenerationReceipt;
156
313
  };
157
314
  export declare function githubCliEnv(token: string | null | undefined): Record<string, string>;
315
+ /**
316
+ * Build an agent-command environment without inheriting credentials that were
317
+ * supplied to bootstrap or authenticate the worker itself. Later layers are
318
+ * explicit server-issued per-run credentials or the currently committed GitHub
319
+ * generation and are intentionally allowed to use the same public variable
320
+ * names.
321
+ */
322
+ export declare function workerChildProcessEnvironment(layers: readonly NodeJS.ProcessEnv[], inherited?: NodeJS.ProcessEnv, platform?: NodeJS.Platform): NodeJS.ProcessEnv;
158
323
  type ResolvedWorkerSessionTarget = {
159
324
  target: WorkerSessionTarget;
160
325
  rootPath: string;
@@ -215,4 +380,13 @@ export declare function resolveHostShell(command?: string, platform?: NodeJS.Pla
215
380
  args: string[];
216
381
  env?: Record<string, string>;
217
382
  };
383
+ declare function terminateCredentialBearingChildren(children: readonly {
384
+ label: string;
385
+ terminate: () => void | Promise<void>;
386
+ }[]): Promise<void>;
387
+ declare function terminateCredentialBearingChildrenWithRetention(children: readonly {
388
+ label: string;
389
+ terminate: () => void | Promise<void>;
390
+ onTerminated: () => void;
391
+ }[]): Promise<void>;
218
392
  export {};
@@ -1,6 +1,17 @@
1
1
  export declare const BRANCH_NAME_MAX_LENGTH = 45;
2
2
  export declare const BRANCH_NAME_PATTERN: RegExp;
3
3
  export type CheckoutPathSegments = [namespace: string, project: string];
4
+ export type ManagedRoot = {
5
+ label: string;
6
+ rootPath: string;
7
+ };
8
+ /**
9
+ * Destructive worker roots must never alias or contain one another. Resolve
10
+ * and materialize every root first so symlinks, bind mounts, casing aliases,
11
+ * and Unicode-normalization aliases cannot disguise an overlap. This runs
12
+ * before any snapshot recovery or other destructive filesystem operation.
13
+ */
14
+ export declare function assertDisjointManagedRoots(roots: readonly ManagedRoot[]): void;
4
15
  export declare function validateManagedBranchName(branchName: string): void;
5
16
  export declare function validateCheckoutPathSegments(value: unknown): CheckoutPathSegments;
6
17
  export declare function assertPathInside(root: string, candidate: string, label: string): void;
@@ -17,9 +17,20 @@ export type ProjectWorkspaceDesiredProject = {
17
17
  }>;
18
18
  };
19
19
  export type LocallyPendingCreatedProjectBranch = {
20
+ /** Durable project_branches row incarnation; absent only in legacy state. */
21
+ branchId?: string;
20
22
  projectId: string;
21
23
  branchName: string;
22
24
  };
25
+ export type PreserveOnlyProjectBranch = {
26
+ branchId: string;
27
+ projectId: string;
28
+ branchName: string;
29
+ };
30
+ export type EnabledRepositoryTransition = {
31
+ projectId: string;
32
+ transitionId: string | null;
33
+ };
23
34
  type ProjectWorkspaceDeletionBase = {
24
35
  id: string;
25
36
  projectId: string;
@@ -66,10 +77,33 @@ export type ProjectWorkspaceStateView = {
66
77
  desiredProjects: ProjectWorkspaceDesiredProject[];
67
78
  effectiveDesiredProjects: ProjectWorkspaceDesiredProject[];
68
79
  locallyPendingCreatedBranches: LocallyPendingCreatedProjectBranch[];
80
+ enabledRepositoryTransitions: EnabledRepositoryTransition[];
69
81
  tombstones: ProjectWorkspaceDeletionTombstone[];
70
82
  pendingTreeDeletions: PendingProjectWorkspaceTreeDeletion[];
71
83
  pendingMirrorRefDeletions: PendingProjectMirrorRefDeletion[];
72
84
  };
85
+ export declare class ProjectWorkspacePendingBranchPathChangeError extends Error {
86
+ readonly projectId: string;
87
+ readonly branchName: string;
88
+ readonly currentCheckoutPathSegments: CheckoutPathSegments;
89
+ readonly incomingCheckoutPathSegments: CheckoutPathSegments;
90
+ constructor(input: {
91
+ projectId: string;
92
+ branchName: string;
93
+ currentCheckoutPathSegments: CheckoutPathSegments;
94
+ incomingCheckoutPathSegments: CheckoutPathSegments;
95
+ });
96
+ }
97
+ export declare class ProjectWorkspaceCheckoutPathCollisionError extends Error {
98
+ readonly projectId: string;
99
+ readonly occupyingProjectId: string;
100
+ readonly checkoutPathSegments: CheckoutPathSegments;
101
+ constructor(input: {
102
+ projectId: string;
103
+ occupyingProjectId: string;
104
+ checkoutPathSegments: CheckoutPathSegments;
105
+ });
106
+ }
73
107
  export type StaleOuterProjectWorkspaceBranch = {
74
108
  kind: "branch";
75
109
  projectId: string;
@@ -125,9 +159,16 @@ export declare class ProjectWorkspaceStateStore {
125
159
  reconcile(input: {
126
160
  desiredProjects: readonly ProjectWorkspaceDesiredProjectInput[];
127
161
  locallyPendingCreatedBranches?: readonly LocallyPendingCreatedProjectBranch[];
162
+ preserveOnlyBranches?: readonly PreserveOnlyProjectBranch[];
128
163
  }): ProjectWorkspaceStateView;
164
+ /** Record a successful enabled reseed without letting disabled config consume it. */
165
+ recordEnabledRepositoryTransition(input: EnabledRepositoryTransition): ProjectWorkspaceStateView;
129
166
  /** Persist the create intent before invoking Git so a crash cannot orphan an untracked worktree. */
130
- recordPendingCreatedBranch(input: LocallyPendingCreatedProjectBranch): ProjectWorkspaceStateView;
167
+ recordPendingCreatedBranch(input: LocallyPendingCreatedProjectBranch & {
168
+ branchId: string;
169
+ }): ProjectWorkspaceStateView;
170
+ /** Roll back an intent only when Git failed before creating its branch. */
171
+ rollbackPendingCreatedBranch(input: LocallyPendingCreatedProjectBranch): ProjectWorkspaceStateView;
131
172
  /** Clear a create intent after an authoritative desired config contains it. */
132
173
  clearPendingCreatedBranch(input: LocallyPendingCreatedProjectBranch): ProjectWorkspaceStateView;
133
174
  /** Persist a branch tombstone before mutating its linked worktree or local ref. */
@@ -141,4 +182,7 @@ export declare class ProjectWorkspaceStateStore {
141
182
  branchName: string;
142
183
  }): ProjectWorkspaceStateView;
143
184
  }
185
+ export declare const projectWorkspaceStateTestHarness: {
186
+ writeEmptyStateAtDurableRootBoundary(stateRoot: string, afterStateRootFsync: () => void): void;
187
+ };
144
188
  export {};