engineering-behavior-observatory 0.2.1

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 (178) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +151 -0
  3. package/contracts/codex-app-server-0.150.1/manifest.json +25 -0
  4. package/contracts/codex-app-server-0.150.1/schema/InitializeParams.json +84 -0
  5. package/contracts/codex-app-server-0.150.1/schema/JSONRPCMessage.json +137 -0
  6. package/contracts/codex-app-server-0.150.1/schema/v2/ThreadReadParams.json +17 -0
  7. package/contracts/codex-app-server-0.150.1/schema/v2/ThreadTokenUsageUpdatedNotification.json +82 -0
  8. package/contracts/codex-app-server-0.150.1/schema/v2/TurnInterruptParams.json +17 -0
  9. package/contracts/codex-app-server-0.150.1/types/AskForApproval.ts +5 -0
  10. package/contracts/codex-app-server-0.150.1/types/ClientNotification.ts +5 -0
  11. package/contracts/codex-app-server-0.150.1/types/SandboxMode.ts +5 -0
  12. package/contracts/codex-app-server-0.150.1/types/ThreadReadParams.ts +9 -0
  13. package/contracts/codex-app-server-0.150.1/types/TokenUsageBreakdown.ts +5 -0
  14. package/contracts/codex-app-server-0.150.1/types/TurnInterruptParams.ts +5 -0
  15. package/contracts/codex-app-server-0.153.4/manifest.json +27 -0
  16. package/contracts/codex-app-server-0.153.4/schema/JSONRPCMessage.json +137 -0
  17. package/contracts/codex-app-server-0.153.4/schema/v1/InitializeParams.json +84 -0
  18. package/contracts/codex-app-server-0.153.4/schema/v2/ThreadReadParams.json +17 -0
  19. package/contracts/codex-app-server-0.153.4/schema/v2/ThreadStartParams.json +515 -0
  20. package/contracts/codex-app-server-0.153.4/schema/v2/ThreadTokenUsageUpdatedNotification.json +82 -0
  21. package/contracts/codex-app-server-0.153.4/schema/v2/TurnInterruptParams.json +17 -0
  22. package/contracts/codex-app-server-0.153.4/schema/v2/TurnStartParams.json +921 -0
  23. package/contracts/codex-app-server-0.153.4/types/AskForApproval.ts +5 -0
  24. package/contracts/codex-app-server-0.153.4/types/ClientNotification.ts +5 -0
  25. package/contracts/codex-app-server-0.153.4/types/SandboxMode.ts +5 -0
  26. package/contracts/codex-app-server-0.153.4/types/ThreadReadParams.ts +12 -0
  27. package/contracts/codex-app-server-0.153.4/types/TokenUsageBreakdown.ts +5 -0
  28. package/contracts/codex-app-server-0.153.4/types/TurnInterruptParams.ts +5 -0
  29. package/contracts/openhands-agent-server-v1.44.1.json +65 -0
  30. package/contracts/openhands-agent-server-v1.46.0.json +46 -0
  31. package/dist/contracts/codex-app-server-0.153.4/types/AskForApproval.d.ts +9 -0
  32. package/dist/contracts/codex-app-server-0.153.4/types/AskForApproval.js +2 -0
  33. package/dist/contracts/codex-app-server-0.153.4/types/ClientNotification.d.ts +3 -0
  34. package/dist/contracts/codex-app-server-0.153.4/types/ClientNotification.js +2 -0
  35. package/dist/contracts/codex-app-server-0.153.4/types/SandboxMode.d.ts +1 -0
  36. package/dist/contracts/codex-app-server-0.153.4/types/SandboxMode.js +2 -0
  37. package/dist/contracts/codex-app-server-0.153.4/types/ThreadReadParams.d.ts +10 -0
  38. package/dist/contracts/codex-app-server-0.153.4/types/ThreadReadParams.js +2 -0
  39. package/dist/contracts/codex-app-server-0.153.4/types/TokenUsageBreakdown.d.ts +8 -0
  40. package/dist/contracts/codex-app-server-0.153.4/types/TokenUsageBreakdown.js +2 -0
  41. package/dist/contracts/codex-app-server-0.153.4/types/TurnInterruptParams.d.ts +4 -0
  42. package/dist/contracts/codex-app-server-0.153.4/types/TurnInterruptParams.js +2 -0
  43. package/dist/src/agent-sdk-normalizer.d.ts +22 -0
  44. package/dist/src/agent-sdk-normalizer.js +995 -0
  45. package/dist/src/agent-sdk-run.d.ts +31 -0
  46. package/dist/src/agent-sdk-run.js +242 -0
  47. package/dist/src/agent-sdk-runner.d.ts +96 -0
  48. package/dist/src/agent-sdk-runner.js +407 -0
  49. package/dist/src/agent-sdk.d.ts +294 -0
  50. package/dist/src/agent-sdk.js +644 -0
  51. package/dist/src/aggregation.d.ts +183 -0
  52. package/dist/src/aggregation.js +513 -0
  53. package/dist/src/artifacts.d.ts +36 -0
  54. package/dist/src/artifacts.js +1372 -0
  55. package/dist/src/atlas-grafana.d.ts +377 -0
  56. package/dist/src/atlas-grafana.js +47 -0
  57. package/dist/src/atlas-html.d.ts +2 -0
  58. package/dist/src/atlas-html.js +34 -0
  59. package/dist/src/atlas.d.ts +130 -0
  60. package/dist/src/atlas.js +235 -0
  61. package/dist/src/behavior-assertions.d.ts +95 -0
  62. package/dist/src/behavior-assertions.js +154 -0
  63. package/dist/src/cli.d.ts +5 -0
  64. package/dist/src/cli.js +957 -0
  65. package/dist/src/codex-judge.d.ts +44 -0
  66. package/dist/src/codex-judge.js +214 -0
  67. package/dist/src/codex-run.d.ts +114 -0
  68. package/dist/src/codex-run.js +519 -0
  69. package/dist/src/codex.d.ts +187 -0
  70. package/dist/src/codex.js +1028 -0
  71. package/dist/src/contracts.d.ts +155 -0
  72. package/dist/src/contracts.js +866 -0
  73. package/dist/src/corpus.d.ts +67 -0
  74. package/dist/src/corpus.js +540 -0
  75. package/dist/src/cursor-sdk-runner.d.ts +81 -0
  76. package/dist/src/cursor-sdk-runner.js +362 -0
  77. package/dist/src/cursor-sdk.d.ts +81 -0
  78. package/dist/src/cursor-sdk.js +1078 -0
  79. package/dist/src/deepseek-adapter.d.ts +152 -0
  80. package/dist/src/deepseek-adapter.js +777 -0
  81. package/dist/src/exports.d.ts +69 -0
  82. package/dist/src/exports.js +966 -0
  83. package/dist/src/human-calibration.d.ts +167 -0
  84. package/dist/src/human-calibration.js +618 -0
  85. package/dist/src/index.d.ts +71 -0
  86. package/dist/src/index.js +38 -0
  87. package/dist/src/lifecycle.d.ts +196 -0
  88. package/dist/src/lifecycle.js +1889 -0
  89. package/dist/src/normalization-integrity.d.ts +128 -0
  90. package/dist/src/normalization-integrity.js +429 -0
  91. package/dist/src/openhands-run.d.ts +36 -0
  92. package/dist/src/openhands-run.js +415 -0
  93. package/dist/src/openhands.d.ts +130 -0
  94. package/dist/src/openhands.js +859 -0
  95. package/dist/src/pi.d.ts +203 -0
  96. package/dist/src/pi.js +1345 -0
  97. package/dist/src/process-protocol.d.ts +245 -0
  98. package/dist/src/process-protocol.js +1261 -0
  99. package/dist/src/retained-evidence.d.ts +12 -0
  100. package/dist/src/retained-evidence.js +201 -0
  101. package/dist/src/run-bundles.d.ts +193 -0
  102. package/dist/src/run-bundles.js +993 -0
  103. package/dist/src/scheduler.d.ts +109 -0
  104. package/dist/src/scheduler.js +1080 -0
  105. package/dist/src/semantic-judge.d.ts +194 -0
  106. package/dist/src/semantic-judge.js +875 -0
  107. package/dist/src/structural-observations.d.ts +124 -0
  108. package/dist/src/structural-observations.js +671 -0
  109. package/dist/src/task-packets.d.ts +115 -0
  110. package/dist/src/task-packets.js +683 -0
  111. package/dist/src/uniform-events.d.ts +120 -0
  112. package/dist/src/uniform-events.js +158 -0
  113. package/dist/src/verifiers.d.ts +116 -0
  114. package/dist/src/verifiers.js +819 -0
  115. package/dist/src/workspaces.d.ts +56 -0
  116. package/dist/src/workspaces.js +1314 -0
  117. package/docs/README.md +60 -0
  118. package/docs/development/README.md +71 -0
  119. package/docs/development/documentation-sync.md +58 -0
  120. package/docs/development/extension-contracts.md +307 -0
  121. package/docs/evaluation/README.md +48 -0
  122. package/docs/evaluation/aggregation.md +112 -0
  123. package/docs/evaluation/behavior-assertions.md +45 -0
  124. package/docs/evaluation/human-calibration.md +170 -0
  125. package/docs/evaluation/normalization-integrity.md +80 -0
  126. package/docs/evaluation/semantic-judge.md +188 -0
  127. package/docs/evaluation/structural-observations.md +84 -0
  128. package/docs/evaluation/uniform-events.md +131 -0
  129. package/docs/guides/atlas.md +144 -0
  130. package/docs/guides/evidence-and-sharing.md +100 -0
  131. package/docs/guides/operator-guide.md +424 -0
  132. package/docs/guides/quickstart.md +152 -0
  133. package/docs/guides/telemetry.md +66 -0
  134. package/docs/harnesses/README.md +31 -0
  135. package/docs/harnesses/claude-agent-sdk.md +61 -0
  136. package/docs/harnesses/codex-harness.md +174 -0
  137. package/docs/harnesses/cursor-sdk.md +149 -0
  138. package/docs/harnesses/deepseek-harness.md +134 -0
  139. package/docs/harnesses/openhands-agent-server.md +153 -0
  140. package/docs/harnesses/pi-sdk.md +66 -0
  141. package/docs/reference/README.md +19 -0
  142. package/docs/reference/agent-sdk-operational-runner.md +118 -0
  143. package/docs/reference/cli.md +114 -0
  144. package/docs/reference/contracts.md +222 -0
  145. package/docs/reference/run-bundle-contract.md +354 -0
  146. package/docs/reference/run-lifecycle.md +54 -0
  147. package/examples/cursor-sdk/README.md +11 -0
  148. package/examples/cursor-sdk/capture-profile.json +1 -0
  149. package/examples/cursor-sdk/harness.json +1 -0
  150. package/examples/cursor-sdk/model.json +1 -0
  151. package/examples/cursor-sdk/native-limits.json +1 -0
  152. package/examples/cursor-sdk/native-tool-policy.json +1 -0
  153. package/ontology/behavior-categories.v1.json +46 -0
  154. package/package.json +66 -0
  155. package/release/0.1.0/KNOWN_LIMITATIONS.md +35 -0
  156. package/release/0.1.0/README.md +46 -0
  157. package/release/0.1.0/reproducibility.json +61 -0
  158. package/release/0.2.0/KNOWN_LIMITATIONS.md +40 -0
  159. package/release/0.2.0/README.md +42 -0
  160. package/release/0.2.0/reproducibility.json +73 -0
  161. package/release/0.2.1/KNOWN_LIMITATIONS.md +18 -0
  162. package/release/0.2.1/README.md +63 -0
  163. package/release/0.2.1/reproducibility.json +73 -0
  164. package/release/README.md +13 -0
  165. package/schemas/aggregation.v1.json +293 -0
  166. package/schemas/behavior-assertions.v1.json +220 -0
  167. package/schemas/deepseek-runtime-composition.v1.schema.json +122 -0
  168. package/schemas/experiment.v1.schema.json +188 -0
  169. package/schemas/human-calibration.v1.json +336 -0
  170. package/schemas/normalization-integrity.v1.json +302 -0
  171. package/schemas/run-bundles/v1.json +1040 -0
  172. package/schemas/run-queue.v1.schema.json +206 -0
  173. package/schemas/semantic-judge.v1.json +380 -0
  174. package/schemas/structural-observations.v1.json +131 -0
  175. package/schemas/task-packet-freeze.v1.schema.json +106 -0
  176. package/schemas/task-packet.v1.schema.json +234 -0
  177. package/schemas/uniform-events/v1.json +289 -0
  178. package/scripts/atlas-grafana.sh +18 -0
@@ -0,0 +1,1314 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import { spawn as spawnProcess } from "node:child_process";
3
+ import { closeSync, chmodSync, constants, fchmodSync, fsyncSync, fstatSync, futimesSync, lstatSync, mkdtempSync, mkdirSync, openSync, opendirSync, renameSync, rmdirSync, readSync, rmSync, realpathSync, unlinkSync, writeSync, } from "node:fs";
4
+ import { lstat, realpath } from "node:fs/promises";
5
+ import { tmpdir } from "node:os";
6
+ import { basename, dirname, isAbsolute, join, parse, relative, resolve, sep } from "node:path";
7
+ import { isSafeArtifactRelativePath, readTaskArchive, resolveTaskArchive, } from "./contracts.js";
8
+ import { assertTaskPacketAdmitted, statusTaskPacket } from "./task-packets.js";
9
+ const ATTEMPT_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
10
+ const DIRECTORY_MODE = 0o700;
11
+ const FILE_MODE = 0o600;
12
+ const EPOCH_SECONDS = 0;
13
+ /**
14
+ * Materialize one admitted, frozen task packet into an isolated attempt path.
15
+ * Only selected regular files and directories from the verified archive enter
16
+ * the workspace; restricted packet components never do.
17
+ */
18
+ export async function materializeWorkspace(options) {
19
+ const attemptId = options.attemptId ?? randomUUID();
20
+ assertAttemptId(attemptId);
21
+ const freeze = statusTaskPacket(options.bundleRoot, options.packetLocator, options.freezeLocator);
22
+ if (freeze.status !== "frozen") {
23
+ throw new Error(`Task packet "${options.packetLocator}" is not frozen (${freeze.status}).`);
24
+ }
25
+ if (freeze.packetDigest === null)
26
+ throw new Error("Frozen task packet is missing its packet digest.");
27
+ const inspection = assertTaskPacketAdmitted(options.bundleRoot, options.packetLocator);
28
+ const packet = inspection.packet;
29
+ if (inspection.packetDigest === null || !sameDigest(inspection.packetDigest, freeze.packetDigest)) {
30
+ throw new Error(`Frozen task packet "${options.packetLocator}" changed before materialization.`);
31
+ }
32
+ const source = packet.agentInput.fixture.source;
33
+ const archive = resolveTaskArchive(options.bundleRoot, source, source.limits.maxCompressedBytes);
34
+ const entries = readTaskArchive(archive, source.limits, packet.agentInput.fixture.materializer.includePaths);
35
+ assertModelVisibleEntries(entries);
36
+ assertFrozenSnapshot(options, freeze.packetDigest, freeze.aggregateDigest);
37
+ const bundleRoot = await realpath(options.bundleRoot);
38
+ const parent = await prepareWorkspaceParent(options.workspaceParent ?? options.workspaceRoot ?? tmpdir(), bundleRoot);
39
+ const createdWorkspace = createWorkspace(parent, attemptId);
40
+ let workspacePath = createdWorkspace.path;
41
+ if (!isDisjoint(bundleRoot, workspacePath)) {
42
+ closeSync(createdWorkspace.descriptor);
43
+ removeWorkspaceIfOwned(workspacePath, createdWorkspace.identity);
44
+ throw new Error("Workspace and task-bundle roots must be separate.");
45
+ }
46
+ const retainOnFailure = options.retainOnFailure === true;
47
+ let workspaceIdentity = createdWorkspace.identity;
48
+ let result;
49
+ try {
50
+ materializeEntries(workspacePath, workspaceIdentity, entries, createdWorkspace.descriptor);
51
+ workspacePath = sealWorkspace(workspacePath, join(parent.path, `.ebo-${attemptId}-sealed-${randomUUID()}`), workspaceIdentity);
52
+ await runSetup(options.setup, options.setupSteps, workspacePath);
53
+ normalizeWorkspace(workspacePath, workspaceIdentity, createdWorkspace.descriptor);
54
+ assertWorkspaceRoot(workspacePath, workspaceIdentity);
55
+ const workspaceFingerprint = digestMaterializedWorkspace(workspacePath, workspaceIdentity);
56
+ assertWorkspaceRoot(workspacePath, workspaceIdentity);
57
+ const settledFingerprint = digestMaterializedWorkspace(workspacePath, workspaceIdentity);
58
+ if (workspaceFingerprint !== settledFingerprint) {
59
+ throw new Error("Workspace changed while its final fingerprint was settling.");
60
+ }
61
+ result = createResult({
62
+ attemptId,
63
+ packet,
64
+ packetDigest: freeze.packetDigest,
65
+ sourceDigest: source.digest,
66
+ workspacePath,
67
+ workspaceIdentity,
68
+ workspaceFingerprint,
69
+ retainOnFailure,
70
+ retained: true,
71
+ workspaceRemoved: false,
72
+ });
73
+ return result;
74
+ }
75
+ catch (error) {
76
+ const message = errorMessage(error);
77
+ let rootIsSafe = false;
78
+ if (workspaceIdentity !== undefined) {
79
+ try {
80
+ if (await isOwnedWorkspaceRoot(workspacePath, workspaceIdentity)) {
81
+ normalizeWorkspace(workspacePath, workspaceIdentity, createdWorkspace.descriptor, true);
82
+ rootIsSafe = await isSafeWorkspaceRoot(workspacePath, workspaceIdentity)
83
+ && await isSafeWorkspaceTree(workspacePath);
84
+ }
85
+ }
86
+ catch {
87
+ rootIsSafe = false;
88
+ }
89
+ }
90
+ let workspaceRemoved = false;
91
+ if (!retainOnFailure || !rootIsSafe) {
92
+ workspaceRemoved = removeWorkspaceIfOwned(workspacePath, workspaceIdentity);
93
+ }
94
+ result = createResult({
95
+ attemptId,
96
+ packet,
97
+ packetDigest: freeze.packetDigest,
98
+ sourceDigest: source.digest,
99
+ workspacePath,
100
+ workspaceIdentity,
101
+ workspaceFingerprint: null,
102
+ retainOnFailure,
103
+ retained: retainOnFailure && rootIsSafe,
104
+ workspaceRemoved,
105
+ status: "failed",
106
+ error: message,
107
+ });
108
+ return result;
109
+ }
110
+ finally {
111
+ closeSync(createdWorkspace.descriptor);
112
+ }
113
+ }
114
+ export async function cleanupWorkspace(materialization, outcome = materialization.state === "failed" ? "failure" : "success") {
115
+ if (materialization.state === "cleaned")
116
+ return;
117
+ if (outcome === "failure" && materialization.retainOnFailure && materialization.retained)
118
+ return;
119
+ if (materialization.state === "failed" && !materialization.retained && materialization.workspaceRemoved) {
120
+ materialization.status = "cleaned";
121
+ materialization.state = "cleaned";
122
+ return;
123
+ }
124
+ if (!removeWorkspaceIfOwned(materialization.workspacePath, materialization.workspaceIdentity)) {
125
+ throw new Error("Workspace root changed before cleanup.");
126
+ }
127
+ materialization.status = "cleaned";
128
+ materialization.state = "cleaned";
129
+ materialization.retained = false;
130
+ materialization.workspaceRemoved = true;
131
+ }
132
+ function createResult(input) {
133
+ const result = {
134
+ attemptId: input.attemptId,
135
+ packetId: input.packet.id,
136
+ packetDigest: input.packetDigest,
137
+ fixtureDigest: input.sourceDigest,
138
+ startingDigest: input.sourceDigest,
139
+ workspacePath: input.workspacePath,
140
+ path: input.workspacePath,
141
+ ...(input.workspaceIdentity === undefined ? {} : { workspaceIdentity: input.workspaceIdentity }),
142
+ workspaceFingerprint: input.workspaceFingerprint,
143
+ workspaceDigest: input.workspaceFingerprint,
144
+ status: input.status ?? "ready",
145
+ state: input.status ?? "ready",
146
+ retainOnFailure: input.retainOnFailure,
147
+ retained: input.retained,
148
+ workspaceRemoved: input.workspaceRemoved,
149
+ ...(input.error === undefined ? {} : { error: input.error }),
150
+ cleanup: async (outcome) => cleanupWorkspace(result, outcome),
151
+ };
152
+ return result;
153
+ }
154
+ async function prepareWorkspaceParent(parent, bundleRoot) {
155
+ const absolute = await resolveFuturePath(resolve(parent));
156
+ const created = [];
157
+ const filesystemRoot = parse(absolute).root;
158
+ let current = filesystemRoot;
159
+ try {
160
+ for (const segment of absolute.slice(filesystemRoot.length).split(sep).filter(Boolean)) {
161
+ current = join(current, segment);
162
+ let metadata;
163
+ try {
164
+ metadata = await lstat(current);
165
+ }
166
+ catch (error) {
167
+ if (error.code !== "ENOENT")
168
+ throw error;
169
+ let createdByThisCall = false;
170
+ try {
171
+ mkdirPrivate(current);
172
+ createdByThisCall = true;
173
+ }
174
+ catch (mkdirError) {
175
+ if (mkdirError.code !== "EEXIST")
176
+ throw mkdirError;
177
+ }
178
+ metadata = await lstat(current);
179
+ if (createdByThisCall)
180
+ created.push({ path: current, dev: metadata.dev, ino: metadata.ino });
181
+ }
182
+ if (!metadata.isDirectory() || metadata.isSymbolicLink()) {
183
+ throw new Error("Workspace parent cannot cross a symbolic link or non-directory.");
184
+ }
185
+ const resolved = await realpath(current);
186
+ if (resolved !== current || isContained(bundleRoot, resolved)) {
187
+ throw new Error("Workspace parent must be outside the task-bundle root.");
188
+ }
189
+ }
190
+ const metadata = await lstat(current);
191
+ return { path: current, dev: metadata.dev, ino: metadata.ino };
192
+ }
193
+ catch (error) {
194
+ for (const entry of created.reverse()) {
195
+ try {
196
+ const metadata = await lstat(entry.path);
197
+ if (metadata.isDirectory() && !metadata.isSymbolicLink()
198
+ && metadata.dev === entry.dev && metadata.ino === entry.ino) {
199
+ rmdirSync(entry.path);
200
+ }
201
+ }
202
+ catch (cleanupError) {
203
+ if (cleanupError.code !== "ENOENT")
204
+ throw error;
205
+ }
206
+ }
207
+ throw error;
208
+ }
209
+ }
210
+ async function resolveFuturePath(path) {
211
+ try {
212
+ return await realpath(path);
213
+ }
214
+ catch (error) {
215
+ if (error.code !== "ENOENT")
216
+ throw error;
217
+ const parent = dirname(path);
218
+ if (parent === path)
219
+ return path;
220
+ return resolve(await resolveFuturePath(parent), basename(path));
221
+ }
222
+ }
223
+ function createWorkspace(parent, attemptId) {
224
+ const parentFd = openSync(parent.path, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
225
+ const originalCwd = process.cwd();
226
+ let createdPath;
227
+ let createdIdentity;
228
+ let descriptor;
229
+ try {
230
+ // This synchronous section has no await points, so concurrent callers cannot
231
+ // interleave cwd changes while mkdtemp is anchored to parentFd's directory.
232
+ process.chdir(parent.path);
233
+ if (!sameIdentity(fstatSync(parentFd), lstatSync("."))) {
234
+ throw new Error("Workspace parent changed before attempt creation.");
235
+ }
236
+ const originalUmask = process.umask(0);
237
+ try {
238
+ createdPath = mkdtempSync(`ebo-${attemptId}-`);
239
+ }
240
+ finally {
241
+ process.umask(originalUmask);
242
+ }
243
+ if (!sameIdentity(fstatSync(parentFd), lstatSync("."))) {
244
+ throw new Error("Workspace parent changed during attempt creation.");
245
+ }
246
+ descriptor = openSync(createdPath, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
247
+ const metadata = fstatSync(descriptor);
248
+ const pathMetadata = lstatSync(createdPath);
249
+ if (!metadata.isDirectory() || metadata.isSymbolicLink()
250
+ || !sameIdentity(metadata, pathMetadata) || pathMetadata.isSymbolicLink()) {
251
+ closeSync(descriptor);
252
+ descriptor = undefined;
253
+ throw new Error("Workspace root changed during attempt creation.");
254
+ }
255
+ createdIdentity = { dev: metadata.dev, ino: metadata.ino };
256
+ const result = { path: realpathSync(createdPath), identity: createdIdentity, descriptor };
257
+ descriptor = undefined;
258
+ return result;
259
+ }
260
+ catch (error) {
261
+ if (descriptor !== undefined)
262
+ closeSync(descriptor);
263
+ // There is no descriptor-relative remove API in Node. If adoption failed,
264
+ // leave the returned pathname untouched rather than risk deleting a raced
265
+ // replacement; successful lifecycles clean through their retained handle.
266
+ throw error;
267
+ }
268
+ finally {
269
+ process.chdir(originalCwd);
270
+ closeSync(parentFd);
271
+ }
272
+ }
273
+ function mkdirPrivate(path) {
274
+ const originalUmask = process.umask(0);
275
+ try {
276
+ mkdirSync(path, { mode: DIRECTORY_MODE });
277
+ }
278
+ finally {
279
+ process.umask(originalUmask);
280
+ }
281
+ }
282
+ function materializeEntries(root, expectedIdentity, entries, verifiedFd) {
283
+ const originalCwd = process.cwd();
284
+ const rootFd = verifiedFd
285
+ ?? openSync(root, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
286
+ try {
287
+ if (!sameIdentity(fstatSync(rootFd), expectedIdentity))
288
+ throw new Error("Workspace root changed before extraction.");
289
+ // Keep extraction synchronous. Relative opens stay anchored to the
290
+ // verified root directory descriptor, and concurrent materializers cannot
291
+ // interleave this short process-global cwd section.
292
+ process.chdir(root);
293
+ const cwdFd = openSync(".", constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
294
+ try {
295
+ if (!sameIdentity(fstatSync(cwdFd), expectedIdentity)) {
296
+ throw new Error("Workspace root changed before extraction.");
297
+ }
298
+ for (const entry of entries)
299
+ materializeEntryFromCwd(root, entry);
300
+ }
301
+ finally {
302
+ closeSync(cwdFd);
303
+ }
304
+ }
305
+ finally {
306
+ process.chdir(originalCwd);
307
+ if (verifiedFd === undefined)
308
+ closeSync(rootFd);
309
+ }
310
+ }
311
+ function materializeEntryFromCwd(root, entry) {
312
+ const entryPath = entry.kind === "directory" ? entry.path.replace(/\/+$/, "") : entry.path;
313
+ assertSafeWorkspacePath(root, entryPath);
314
+ const parts = entryPath.split("/");
315
+ const directoryParts = entry.kind === "directory" ? parts : parts.slice(0, -1);
316
+ for (const segment of directoryParts)
317
+ enterMaterializationDirectory(root, segment);
318
+ try {
319
+ if (entry.kind === "directory")
320
+ return;
321
+ if (entry.kind !== "file")
322
+ throw new Error(`Archive entry "${entry.path}" is unsafe.`);
323
+ const name = parts.at(-1);
324
+ if (name === undefined || name.length === 0)
325
+ throw new Error(`Archive entry "${entry.path}" is unsafe.`);
326
+ const mode = normalizedFileMode(entry.mode);
327
+ const fileFd = openSync(name, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, mode);
328
+ try {
329
+ for (let offset = 0; offset < entry.bytes.length;) {
330
+ const written = writeSync(fileFd, entry.bytes, offset, entry.bytes.length - offset);
331
+ if (written <= 0)
332
+ throw new Error(`Archive entry "${entry.path}" could not be written.`);
333
+ offset += written;
334
+ }
335
+ fsyncSync(fileFd);
336
+ fchmodSync(fileFd, mode);
337
+ }
338
+ finally {
339
+ closeSync(fileFd);
340
+ }
341
+ }
342
+ finally {
343
+ for (let index = directoryParts.length - 1; index >= 0; index -= 1)
344
+ process.chdir("..");
345
+ }
346
+ }
347
+ function enterMaterializationDirectory(root, segment) {
348
+ if (!isSafeArtifactRelativePath(segment))
349
+ throw new Error(`Workspace path "${segment}" is unsafe.`);
350
+ let metadata;
351
+ try {
352
+ metadata = lstatSync(segment);
353
+ }
354
+ catch (error) {
355
+ if (error.code !== "ENOENT")
356
+ throw error;
357
+ mkdirPrivate(segment);
358
+ metadata = lstatSync(segment);
359
+ }
360
+ if (!metadata.isDirectory() || metadata.isSymbolicLink()) {
361
+ throw new Error(`Workspace destination "${segment}" crosses a symbolic link.`);
362
+ }
363
+ const childFd = openSync(segment, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
364
+ try {
365
+ const childIdentity = fstatSync(childFd);
366
+ if (!sameIdentity(metadata, childIdentity))
367
+ throw new Error(`Workspace directory "${segment}" changed during extraction.`);
368
+ const alias = `.ebo-materialize-${randomUUID()}`;
369
+ try {
370
+ lstatSync(alias);
371
+ throw new Error(`Workspace extraction alias already exists: "${alias}".`);
372
+ }
373
+ catch (error) {
374
+ if (error.code !== "ENOENT")
375
+ throw error;
376
+ }
377
+ renameSync(segment, alias);
378
+ let moved = true;
379
+ try {
380
+ if (!sameIdentity(childIdentity, lstatSync(alias))) {
381
+ throw new Error(`Workspace directory "${segment}" changed during extraction.`);
382
+ }
383
+ process.chdir(alias);
384
+ const enteredFd = openSync(".", constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
385
+ try {
386
+ if (!sameIdentity(childIdentity, fstatSync(enteredFd)) || !isContained(root, realpathSync("."))) {
387
+ throw new Error(`Workspace directory "${segment}" changed during extraction.`);
388
+ }
389
+ }
390
+ finally {
391
+ closeSync(enteredFd);
392
+ }
393
+ let destinationAbsent = false;
394
+ try {
395
+ lstatSync(join("..", segment));
396
+ }
397
+ catch (error) {
398
+ if (error.code === "ENOENT")
399
+ destinationAbsent = true;
400
+ else
401
+ throw error;
402
+ }
403
+ if (!destinationAbsent)
404
+ throw new Error(`Workspace destination "${segment}" changed during extraction.`);
405
+ renameSync(join("..", alias), join("..", segment));
406
+ moved = false;
407
+ }
408
+ finally {
409
+ if (moved) {
410
+ try {
411
+ if (sameIdentity(childIdentity, lstatSync(join("..", alias)))) {
412
+ let destinationAbsent = false;
413
+ try {
414
+ lstatSync(join("..", segment));
415
+ }
416
+ catch (error) {
417
+ if (error.code === "ENOENT")
418
+ destinationAbsent = true;
419
+ else
420
+ throw error;
421
+ }
422
+ if (destinationAbsent)
423
+ renameSync(join("..", alias), join("..", segment));
424
+ }
425
+ }
426
+ catch {
427
+ // The caller will fail closed and remove only the original root.
428
+ }
429
+ }
430
+ }
431
+ }
432
+ finally {
433
+ closeSync(childFd);
434
+ }
435
+ }
436
+ function sealWorkspace(path, destination, expected) {
437
+ const current = lstatSync(path);
438
+ if (!current.isDirectory() || current.isSymbolicLink() || !sameIdentity(current, expected)) {
439
+ throw new Error("Workspace root changed before sealing.");
440
+ }
441
+ renameSync(path, destination);
442
+ try {
443
+ const sealed = lstatSync(destination);
444
+ if (!sealed.isDirectory() || sealed.isSymbolicLink() || !sameIdentity(sealed, expected)) {
445
+ if (sealed.isSymbolicLink())
446
+ rmSync(destination, { force: true });
447
+ throw new Error("Workspace root changed during sealing.");
448
+ }
449
+ return realpathSync(destination);
450
+ }
451
+ catch (error) {
452
+ if (error.code === "ENOENT") {
453
+ throw new Error("Workspace root disappeared during sealing.");
454
+ }
455
+ throw error;
456
+ }
457
+ }
458
+ function normalizeWorkspace(root, expectedIdentity, verifiedFd, preserveTimestamps = false) {
459
+ assertWorkspaceRoot(root, expectedIdentity);
460
+ try {
461
+ normalizeDirectory(root, root, "", verifiedFd, preserveTimestamps);
462
+ }
463
+ catch (error) {
464
+ if (error.code !== "EACCES")
465
+ throw error;
466
+ if (verifiedFd === undefined)
467
+ normalizeUnreadableDirectory(root, expectedIdentity);
468
+ else
469
+ fchmodSync(verifiedFd, DIRECTORY_MODE);
470
+ normalizeDirectory(root, root, "", verifiedFd, preserveTimestamps);
471
+ }
472
+ assertWorkspaceRoot(root, expectedIdentity);
473
+ }
474
+ function normalizeDirectory(root, directory, relativeDirectory, verifiedFd, preserveTimestamps = false) {
475
+ const directoryFd = verifiedFd
476
+ ?? openSync(directory, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
477
+ const ownsFd = verifiedFd === undefined;
478
+ try {
479
+ assertContainedRealpathSync(root, directory);
480
+ if (!sameIdentity(fstatSync(directoryFd), lstatSync(directory))) {
481
+ throw new Error(`Workspace directory "${relativeDirectory}" changed during materialization.`);
482
+ }
483
+ fchmodSync(directoryFd, DIRECTORY_MODE);
484
+ const entries = readDirectoryEntries(directory, directoryFd, preserveTimestamps);
485
+ entries.sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0);
486
+ for (const entry of entries) {
487
+ const path = join(directory, entry.name);
488
+ const relativePath = relativeDirectory === "" ? entry.name : `${relativeDirectory}/${entry.name}`;
489
+ if (relativePath.split("/", 1)[0]?.toLowerCase() === "restricted") {
490
+ throw new Error(`Restricted packet content cannot enter the workspace: "${relativePath}".`);
491
+ }
492
+ const current = lstatSync(path);
493
+ const childFd = openChildSync(path, entry.isDirectory(), current.isDirectory() ? DIRECTORY_MODE : normalizedFileMode(current.mode));
494
+ try {
495
+ const metadata = fstatSync(childFd);
496
+ assertContainedRealpathSync(root, path);
497
+ if (current.isSymbolicLink() || !sameIdentity(metadata, current)) {
498
+ throw new Error(`Workspace entry "${relativePath}" changed during materialization.`);
499
+ }
500
+ if (metadata.isDirectory()) {
501
+ normalizeDirectory(root, path, relativePath, childFd, preserveTimestamps);
502
+ fchmodSync(childFd, DIRECTORY_MODE);
503
+ if (!preserveTimestamps)
504
+ futimesSync(childFd, EPOCH_SECONDS, EPOCH_SECONDS);
505
+ }
506
+ else if (metadata.isFile()) {
507
+ if (metadata.nlink > 1)
508
+ throw new Error(`Workspace contains a hard-linked file at "${relativePath}".`);
509
+ fchmodSync(childFd, normalizedFileMode(metadata.mode));
510
+ if (!preserveTimestamps)
511
+ futimesSync(childFd, EPOCH_SECONDS, EPOCH_SECONDS);
512
+ }
513
+ else {
514
+ throw new Error(`Workspace contains an unsupported entry at "${relativePath}".`);
515
+ }
516
+ const completed = fstatSync(childFd);
517
+ const finalPath = lstatSync(path);
518
+ if (!sameIdentity(metadata, completed) || !sameIdentity(metadata, finalPath)
519
+ || finalPath.isSymbolicLink()) {
520
+ throw new Error(`Workspace entry "${relativePath}" changed during materialization.`);
521
+ }
522
+ }
523
+ finally {
524
+ closeSync(childFd);
525
+ }
526
+ }
527
+ fchmodSync(directoryFd, DIRECTORY_MODE);
528
+ if (!preserveTimestamps)
529
+ futimesSync(directoryFd, EPOCH_SECONDS, EPOCH_SECONDS);
530
+ }
531
+ finally {
532
+ if (ownsFd)
533
+ closeSync(directoryFd);
534
+ }
535
+ }
536
+ function openChildSync(path, directory, normalizeMode) {
537
+ const current = lstatSync(path);
538
+ if (current.isSymbolicLink())
539
+ throw new Error(`Workspace contains a symbolic link at "${path}".`);
540
+ if (!current.isDirectory() && !current.isFile()) {
541
+ throw new Error(`Workspace contains an unsupported entry at "${path}".`);
542
+ }
543
+ try {
544
+ return openSync(path, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK | (directory ? constants.O_DIRECTORY : 0));
545
+ }
546
+ catch (error) {
547
+ if (error.code === "ELOOP") {
548
+ throw new Error(`Workspace contains a symbolic link at "${path}".`);
549
+ }
550
+ if (error.code === "EACCES" && normalizeMode !== undefined) {
551
+ return normalizeUnreadableChild(path, directory, current, normalizeMode);
552
+ }
553
+ throw error;
554
+ }
555
+ }
556
+ function normalizeUnreadableDirectory(path, expected) {
557
+ const parent = dirname(path);
558
+ const name = basename(path);
559
+ const alias = `.ebo-root-normalize-${randomUUID()}`;
560
+ const originalCwd = process.cwd();
561
+ const parentFd = openSync(parent, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
562
+ let moved = false;
563
+ try {
564
+ if (!sameIdentity(fstatSync(parentFd), lstatSync(parent))) {
565
+ throw new Error(`Workspace parent "${parent}" changed during normalization.`);
566
+ }
567
+ process.chdir(parent);
568
+ if (!sameIdentity(fstatSync(parentFd), lstatSync("."))) {
569
+ throw new Error(`Workspace parent "${parent}" changed during normalization.`);
570
+ }
571
+ try {
572
+ lstatSync(alias);
573
+ throw new Error(`Workspace normalization alias already exists: "${alias}".`);
574
+ }
575
+ catch (error) {
576
+ if (error.code !== "ENOENT")
577
+ throw error;
578
+ }
579
+ renameSync(name, alias);
580
+ moved = true;
581
+ const movedMetadata = lstatSync(alias);
582
+ if (!movedMetadata.isDirectory() || movedMetadata.isSymbolicLink() || !sameIdentity(movedMetadata, expected)) {
583
+ throw new Error(`Workspace root changed during normalization.`);
584
+ }
585
+ chmodSync(alias, DIRECTORY_MODE);
586
+ const opened = openSync(alias, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
587
+ try {
588
+ if (!sameIdentity(fstatSync(opened), expected))
589
+ throw new Error(`Workspace root changed during normalization.`);
590
+ }
591
+ finally {
592
+ closeSync(opened);
593
+ }
594
+ renameSync(alias, name);
595
+ moved = false;
596
+ const restored = lstatSync(name);
597
+ if (!restored.isDirectory() || restored.isSymbolicLink() || !sameIdentity(restored, expected)) {
598
+ throw new Error(`Workspace root changed during normalization.`);
599
+ }
600
+ }
601
+ finally {
602
+ if (moved) {
603
+ try {
604
+ if (sameIdentity(lstatSync(alias), expected)) {
605
+ let destinationAbsent = false;
606
+ try {
607
+ lstatSync(name);
608
+ }
609
+ catch (error) {
610
+ if (error.code === "ENOENT")
611
+ destinationAbsent = true;
612
+ else
613
+ throw error;
614
+ }
615
+ if (destinationAbsent)
616
+ renameSync(alias, name);
617
+ }
618
+ }
619
+ catch {
620
+ // Preserve the original failure and let the root cleanup gate decide.
621
+ }
622
+ }
623
+ process.chdir(originalCwd);
624
+ closeSync(parentFd);
625
+ }
626
+ }
627
+ function normalizeUnreadableChild(path, directory, expected, mode) {
628
+ const parent = dirname(path);
629
+ const name = basename(path);
630
+ const alias = `.ebo-normalize-${randomUUID()}`;
631
+ const originalCwd = process.cwd();
632
+ const parentFd = openSync(parent, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
633
+ let moved = false;
634
+ let childFd;
635
+ try {
636
+ if (!sameIdentity(fstatSync(parentFd), lstatSync(parent))) {
637
+ throw new Error(`Workspace parent "${parent}" changed during normalization.`);
638
+ }
639
+ // This synchronous section keeps relative rename/chmod/open operations
640
+ // on the verified parent descriptor without allowing concurrent JS calls
641
+ // to interleave the process-global cwd.
642
+ process.chdir(parent);
643
+ if (!sameIdentity(fstatSync(parentFd), lstatSync("."))) {
644
+ throw new Error(`Workspace parent "${parent}" changed during normalization.`);
645
+ }
646
+ try {
647
+ lstatSync(alias);
648
+ throw new Error(`Workspace normalization alias already exists: "${alias}".`);
649
+ }
650
+ catch (error) {
651
+ if (error.code !== "ENOENT")
652
+ throw error;
653
+ }
654
+ renameSync(name, alias);
655
+ moved = true;
656
+ const movedMetadata = lstatSync(alias);
657
+ if (movedMetadata.isSymbolicLink() || !sameIdentity(movedMetadata, expected)) {
658
+ throw new Error(`Workspace entry "${path}" changed during normalization.`);
659
+ }
660
+ chmodSync(alias, mode);
661
+ childFd = openSync(alias, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK | (directory ? constants.O_DIRECTORY : 0));
662
+ const opened = fstatSync(childFd);
663
+ if (!sameIdentity(opened, expected))
664
+ throw new Error(`Workspace entry "${path}" changed during normalization.`);
665
+ renameSync(alias, name);
666
+ moved = false;
667
+ const restored = lstatSync(name);
668
+ if (restored.isSymbolicLink() || !sameIdentity(restored, expected)) {
669
+ throw new Error(`Workspace entry "${path}" changed during normalization.`);
670
+ }
671
+ const result = childFd;
672
+ childFd = undefined;
673
+ return result;
674
+ }
675
+ finally {
676
+ if (childFd !== undefined)
677
+ closeSync(childFd);
678
+ if (moved) {
679
+ try {
680
+ const current = lstatSync(alias);
681
+ if (sameIdentity(current, expected)) {
682
+ let destinationAbsent = false;
683
+ try {
684
+ lstatSync(name);
685
+ }
686
+ catch (error) {
687
+ if (error.code === "ENOENT")
688
+ destinationAbsent = true;
689
+ else
690
+ throw error;
691
+ }
692
+ if (destinationAbsent)
693
+ renameSync(alias, name);
694
+ }
695
+ }
696
+ catch {
697
+ // Preserve the original failure and let the root-level cleanup gate
698
+ // decide whether the partial tree is still safe to retain.
699
+ }
700
+ }
701
+ process.chdir(originalCwd);
702
+ closeSync(parentFd);
703
+ }
704
+ }
705
+ function readDirectoryEntries(directory, directoryFd, preserveTimestamps = false) {
706
+ const alias = join(dirname(directory), `.ebo-enumeration-${randomUUID()}`);
707
+ let moved = false;
708
+ let handle;
709
+ let expected;
710
+ let parentFd;
711
+ let parentExpected;
712
+ try {
713
+ expected = fstatSync(directoryFd);
714
+ if (!sameIdentity(expected, lstatSync(directory))) {
715
+ throw new Error(`Workspace directory "${directory}" changed during traversal.`);
716
+ }
717
+ parentFd = openSync(dirname(directory), constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
718
+ parentExpected = fstatSync(parentFd);
719
+ if (!sameIdentity(parentExpected, lstatSync(dirname(directory)))) {
720
+ throw new Error(`Workspace parent "${dirname(directory)}" changed during traversal.`);
721
+ }
722
+ // Node's public Dir API does not expose its descriptor. Temporarily move
723
+ // the already-verified inode to a private, unique pathname so opendirSync
724
+ // opens that inode, then restore the public path while retaining the
725
+ // handle. A raced replacement makes restoration or identity validation
726
+ // fail closed instead of enumerating an unbound pathname.
727
+ try {
728
+ lstatSync(alias);
729
+ throw new Error(`Workspace enumeration alias already exists: "${alias}".`);
730
+ }
731
+ catch (error) {
732
+ if (error.code !== "ENOENT")
733
+ throw error;
734
+ }
735
+ renameSync(directory, alias);
736
+ moved = true;
737
+ if (!sameIdentity(expected, lstatSync(alias))) {
738
+ throw new Error(`Workspace directory "${directory}" changed during traversal.`);
739
+ }
740
+ handle = opendirSync(alias);
741
+ if (!sameIdentity(expected, lstatSync(alias))) {
742
+ throw new Error(`Workspace directory "${directory}" changed during traversal.`);
743
+ }
744
+ renameSync(alias, directory);
745
+ moved = false;
746
+ // Renaming a child updates the parent directory's mtime. Restore the
747
+ // opened directory's timestamp so the operation stays digest-neutral.
748
+ const directoryAtime = preserveTimestamps ? expected.atimeMs / 1000 : expected.atime;
749
+ const directoryMtime = preserveTimestamps ? expected.mtimeMs / 1000 : expected.mtime;
750
+ futimesSync(directoryFd, directoryAtime, directoryMtime);
751
+ if (parentFd !== undefined && parentExpected !== undefined) {
752
+ const parentAtime = preserveTimestamps ? parentExpected.atimeMs / 1000 : parentExpected.atime;
753
+ const parentMtime = preserveTimestamps ? parentExpected.mtimeMs / 1000 : parentExpected.mtime;
754
+ futimesSync(parentFd, parentAtime, parentMtime);
755
+ }
756
+ if (!sameIdentity(expected, lstatSync(directory))) {
757
+ throw new Error(`Workspace directory "${directory}" changed during traversal.`);
758
+ }
759
+ const entries = [];
760
+ for (let entry = handle.readSync(); entry !== null; entry = handle.readSync()) {
761
+ if (!sameIdentity(expected, fstatSync(directoryFd)) || !sameIdentity(expected, lstatSync(directory))) {
762
+ throw new Error(`Workspace directory "${directory}" changed during traversal.`);
763
+ }
764
+ entries.push(entry);
765
+ }
766
+ if (!sameIdentity(expected, fstatSync(directoryFd)) || !sameIdentity(expected, lstatSync(directory))) {
767
+ throw new Error(`Workspace directory "${directory}" changed during traversal.`);
768
+ }
769
+ return entries;
770
+ }
771
+ finally {
772
+ handle?.closeSync();
773
+ if (moved) {
774
+ let aliasIsOwned = false;
775
+ try {
776
+ aliasIsOwned = expected !== undefined && sameIdentity(expected, lstatSync(alias));
777
+ }
778
+ catch {
779
+ aliasIsOwned = false;
780
+ }
781
+ if (aliasIsOwned) {
782
+ let destinationAbsent = false;
783
+ try {
784
+ lstatSync(directory);
785
+ }
786
+ catch (error) {
787
+ if (error.code === "ENOENT")
788
+ destinationAbsent = true;
789
+ else
790
+ throw error;
791
+ }
792
+ if (destinationAbsent) {
793
+ try {
794
+ renameSync(alias, directory);
795
+ }
796
+ catch {
797
+ if (expected !== undefined) {
798
+ removeWorkspaceIfOwned(alias, { dev: Number(expected.dev), ino: Number(expected.ino) });
799
+ }
800
+ }
801
+ }
802
+ else if (expected !== undefined) {
803
+ // Do not overwrite a raced replacement at the public path.
804
+ removeWorkspaceIfOwned(alias, { dev: Number(expected.dev), ino: Number(expected.ino) });
805
+ }
806
+ }
807
+ }
808
+ if (parentFd !== undefined)
809
+ closeSync(parentFd);
810
+ }
811
+ }
812
+ function assertContainedRealpathSync(root, path) {
813
+ const resolved = realpathSync(path);
814
+ if (resolved !== path || !isContained(root, resolved)) {
815
+ throw new Error(`Workspace path "${path}" escapes its root.`);
816
+ }
817
+ }
818
+ function digestMaterializedWorkspace(root, expectedIdentity) {
819
+ const hash = createHash("sha256");
820
+ const rootFd = openSync(root, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
821
+ try {
822
+ const metadata = fstatSync(rootFd, { bigint: true });
823
+ assertWorkspaceRoot(root, expectedIdentity);
824
+ assertContainedRealpathSync(root, root);
825
+ if (!metadata.isDirectory() || metadata.isSymbolicLink())
826
+ throw new Error("Workspace root is not a directory.");
827
+ hash.update("ebo.workspace/v1\0");
828
+ hash.update(`root\0${metadata.mode & 4095n}\0${workspaceTimestamp(metadata)}\0`);
829
+ hashWorkspaceDirectory(root, root, "", hash, rootFd);
830
+ }
831
+ finally {
832
+ closeSync(rootFd);
833
+ }
834
+ assertWorkspaceRoot(root, expectedIdentity);
835
+ return `sha256:${hash.digest("hex")}`;
836
+ }
837
+ function hashWorkspaceDirectory(root, directory, relativeDirectory, hash, verifiedFd) {
838
+ const directoryFd = verifiedFd
839
+ ?? openSync(directory, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
840
+ const ownsFd = verifiedFd === undefined;
841
+ try {
842
+ assertContainedRealpathSync(root, directory);
843
+ const openedDirectory = fstatSync(directoryFd, { bigint: true });
844
+ if (!openedDirectory.isDirectory() || openedDirectory.isSymbolicLink()) {
845
+ throw new Error(`Workspace directory "${relativeDirectory}" is unsafe.`);
846
+ }
847
+ const entries = readDirectoryEntries(directory, directoryFd);
848
+ entries.sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0);
849
+ for (const entry of entries) {
850
+ const path = join(directory, entry.name);
851
+ const relativePath = relativeDirectory === "" ? entry.name : `${relativeDirectory}/${entry.name}`;
852
+ if (relativePath.split("/", 1)[0]?.toLowerCase() === "restricted") {
853
+ throw new Error(`Restricted packet content cannot enter the workspace: "${relativePath}".`);
854
+ }
855
+ const current = lstatSync(path, { bigint: true });
856
+ const childFd = openChildSync(path, entry.isDirectory());
857
+ try {
858
+ const metadata = fstatSync(childFd, { bigint: true });
859
+ assertContainedRealpathSync(root, path);
860
+ if (current.isSymbolicLink() || !sameIdentity(metadata, current)) {
861
+ throw new Error(`Workspace entry "${relativePath}" changed during fingerprinting.`);
862
+ }
863
+ if (metadata.isDirectory()) {
864
+ hash.update(`directory\0${relativePath}\0${metadata.mode & 4095n}\0${workspaceTimestamp(metadata)}\0`);
865
+ hashWorkspaceDirectory(root, path, relativePath, hash, childFd);
866
+ }
867
+ else if (metadata.isFile()) {
868
+ if (metadata.nlink > 1n)
869
+ throw new Error(`Workspace contains a hard-linked file at "${relativePath}".`);
870
+ const size = Number(metadata.size);
871
+ if (!Number.isSafeInteger(size) || size < 0)
872
+ throw new Error(`Workspace file "${relativePath}" is too large.`);
873
+ hash.update(`file\0${relativePath}\0${metadata.mode & 4095n}\0${workspaceTimestamp(metadata)}\0${size}\0`);
874
+ const bytes = Buffer.alloc(Math.min(size, 64 * 1024));
875
+ for (let offset = 0; offset < size;) {
876
+ const read = readSync(childFd, bytes, 0, Math.min(bytes.length, size - offset), offset);
877
+ if (read === 0)
878
+ throw new Error(`Workspace file "${relativePath}" changed during fingerprinting.`);
879
+ hash.update(bytes.subarray(0, read));
880
+ offset += read;
881
+ }
882
+ }
883
+ else {
884
+ throw new Error(`Workspace contains an unsupported entry at "${relativePath}".`);
885
+ }
886
+ const completed = fstatSync(childFd, { bigint: true });
887
+ const finalPath = lstatSync(path, { bigint: true });
888
+ if (!sameIdentity(metadata, completed) || !sameIdentity(metadata, finalPath)
889
+ || finalPath.isSymbolicLink() || (metadata.isFile() && !sameFileSnapshot(metadata, completed))) {
890
+ throw new Error(`Workspace entry "${relativePath}" changed during fingerprinting.`);
891
+ }
892
+ }
893
+ finally {
894
+ closeSync(childFd);
895
+ }
896
+ }
897
+ }
898
+ finally {
899
+ if (ownsFd)
900
+ closeSync(directoryFd);
901
+ }
902
+ }
903
+ async function runSetup(setup, setupSteps, workspacePath) {
904
+ const steps = setup === undefined ? [] : typeof setup === "function" ? [setup] : [...setup];
905
+ const allSteps = [...steps, ...(setupSteps ?? [])];
906
+ if (allSteps.length === 0)
907
+ return;
908
+ if (process.platform === "win32")
909
+ throw new Error("Workspace setup process tracking is unavailable on this platform.");
910
+ const children = new Map();
911
+ let spawnError;
912
+ let contextOpen = true;
913
+ const context = {
914
+ spawn(command, args, options) {
915
+ if (!contextOpen)
916
+ throw new Error("Workspace setup context is closed.");
917
+ const child = spawnOwnedProcess(command, args, options);
918
+ child.once("error", (error) => {
919
+ spawnError = error instanceof Error ? error : new Error("Workspace setup child failed.");
920
+ });
921
+ if (child.pid !== undefined)
922
+ children.set(child.pid, child);
923
+ return child;
924
+ },
925
+ };
926
+ let setupFailed = false;
927
+ let setupFailure;
928
+ try {
929
+ for (const step of allSteps) {
930
+ if (typeof step !== "function")
931
+ throw new Error("Workspace setup steps must be functions.");
932
+ await step(workspacePath, context);
933
+ }
934
+ }
935
+ catch (error) {
936
+ setupFailed = true;
937
+ setupFailure = error;
938
+ throw error;
939
+ }
940
+ finally {
941
+ contextOpen = false;
942
+ try {
943
+ await new Promise((resolve) => setImmediate(resolve));
944
+ await reapSetupChildren(children);
945
+ if (spawnError !== undefined)
946
+ throw new Error(`Workspace setup child failed: ${spawnError.message}`);
947
+ }
948
+ catch (cleanupError) {
949
+ if (setupFailed) {
950
+ throw new AggregateError([setupFailure, cleanupError], `Workspace setup failed: ${errorMessage(setupFailure)}; cleanup failed: ${errorMessage(cleanupError)}`);
951
+ }
952
+ throw cleanupError;
953
+ }
954
+ }
955
+ }
956
+ function spawnOwnedProcess(command, args, options) {
957
+ const ownedOptions = { ...options, detached: true };
958
+ return args === undefined
959
+ ? spawnProcess(command, ownedOptions)
960
+ : spawnProcess(command, args, ownedOptions);
961
+ }
962
+ async function reapSetupChildren(children) {
963
+ const waits = [];
964
+ for (const [pid, child] of children) {
965
+ try {
966
+ // A detached child is the leader of its own process group. Killing the
967
+ // group also covers descendants that outlive their direct parent.
968
+ process.kill(-pid, "SIGKILL");
969
+ }
970
+ catch {
971
+ // The setup child may have exited between the process snapshot and kill.
972
+ }
973
+ waits.push(waitForChildExit(child).then(() => waitForProcessGroupExit(pid)));
974
+ }
975
+ await Promise.all(waits);
976
+ }
977
+ function waitForChildExit(child) {
978
+ if (child.exitCode !== null || child.signalCode !== null)
979
+ return Promise.resolve();
980
+ return new Promise((resolve) => child.once("close", () => resolve()));
981
+ }
982
+ async function waitForProcessGroupExit(pid) {
983
+ for (let attempt = 0; attempt < 100; attempt += 1) {
984
+ try {
985
+ process.kill(-pid, 0);
986
+ }
987
+ catch (error) {
988
+ if (error.code === "ESRCH")
989
+ return;
990
+ throw error;
991
+ }
992
+ await new Promise((resolve) => setTimeout(resolve, 10));
993
+ }
994
+ throw new Error("Workspace setup process group did not terminate.");
995
+ }
996
+ function assertModelVisibleEntries(entries) {
997
+ for (const entry of entries) {
998
+ const first = entry.path.split("/", 1)[0]?.toLowerCase();
999
+ if (first === "restricted") {
1000
+ throw new Error(`Restricted packet content cannot enter the workspace: "${entry.path}".`);
1001
+ }
1002
+ }
1003
+ }
1004
+ function assertSafeWorkspacePath(root, relativePath) {
1005
+ if (relativePath === ".")
1006
+ return;
1007
+ if (!isSafeArtifactRelativePath(relativePath))
1008
+ throw new Error(`Workspace path "${relativePath}" is unsafe.`);
1009
+ const destination = resolve(root, relativePath);
1010
+ if (!isContained(root, destination))
1011
+ throw new Error(`Workspace path "${relativePath}" escapes its root.`);
1012
+ }
1013
+ async function isOwnedWorkspaceRoot(path, expected) {
1014
+ try {
1015
+ const metadata = await lstat(path);
1016
+ return metadata.isDirectory() && !metadata.isSymbolicLink()
1017
+ && metadata.dev === expected.dev && metadata.ino === expected.ino
1018
+ && (await realpath(path)) === path;
1019
+ }
1020
+ catch {
1021
+ return false;
1022
+ }
1023
+ }
1024
+ async function isSafeWorkspaceRoot(path, expected) {
1025
+ try {
1026
+ const metadata = await lstat(path);
1027
+ return metadata.isDirectory() && !metadata.isSymbolicLink()
1028
+ && (metadata.mode & 0o7777) === DIRECTORY_MODE
1029
+ && metadata.dev === expected.dev && metadata.ino === expected.ino
1030
+ && (await realpath(path)) === path;
1031
+ }
1032
+ catch {
1033
+ return false;
1034
+ }
1035
+ }
1036
+ function isSafeWorkspaceTree(directory, relativeDirectory = "", root = directory, verifiedFd) {
1037
+ let directoryFd;
1038
+ try {
1039
+ directoryFd = verifiedFd
1040
+ ?? openSync(directory, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
1041
+ }
1042
+ catch {
1043
+ return false;
1044
+ }
1045
+ const ownsFd = verifiedFd === undefined;
1046
+ try {
1047
+ assertContainedRealpathSync(root, directory);
1048
+ const directoryMetadata = fstatSync(directoryFd);
1049
+ if (!directoryMetadata.isDirectory() || directoryMetadata.isSymbolicLink())
1050
+ return false;
1051
+ for (const entry of readDirectoryEntries(directory, directoryFd)) {
1052
+ const path = join(directory, entry.name);
1053
+ const relativePath = relativeDirectory === "" ? entry.name : `${relativeDirectory}/${entry.name}`;
1054
+ if (relativePath.split("/", 1)[0]?.toLowerCase() === "restricted")
1055
+ return false;
1056
+ const current = lstatSync(path);
1057
+ const childFd = openChildSync(path, entry.isDirectory());
1058
+ try {
1059
+ const metadata = fstatSync(childFd);
1060
+ assertContainedRealpathSync(root, path);
1061
+ if (current.isSymbolicLink() || !sameIdentity(metadata, current))
1062
+ return false;
1063
+ if (metadata.isDirectory()) {
1064
+ if ((metadata.mode & 0o7777) !== DIRECTORY_MODE
1065
+ || !isSafeWorkspaceTree(path, relativePath, root, childFd))
1066
+ return false;
1067
+ }
1068
+ else if (!metadata.isFile() || metadata.nlink > 1
1069
+ || (metadata.mode & 0o7777) !== normalizedFileMode(metadata.mode)) {
1070
+ return false;
1071
+ }
1072
+ }
1073
+ finally {
1074
+ closeSync(childFd);
1075
+ }
1076
+ }
1077
+ return true;
1078
+ }
1079
+ catch {
1080
+ return false;
1081
+ }
1082
+ finally {
1083
+ if (ownsFd)
1084
+ closeSync(directoryFd);
1085
+ }
1086
+ }
1087
+ function removeWorkspaceIfOwned(path, expected) {
1088
+ try {
1089
+ const metadata = lstatSync(path);
1090
+ if (metadata.isSymbolicLink())
1091
+ return false;
1092
+ if (expected === undefined || !metadata.isDirectory()
1093
+ || metadata.dev !== expected.dev || metadata.ino !== expected.ino)
1094
+ return false;
1095
+ const parent = dirname(path);
1096
+ const quarantine = join(parent, `.${basename(path)}-cleanup-${randomUUID()}`);
1097
+ const parentHandle = openSync(parent, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
1098
+ try {
1099
+ if (!sameIdentity(fstatSync(parentHandle), lstatSync(parent)))
1100
+ return false;
1101
+ const current = lstatSync(path);
1102
+ if (current.isSymbolicLink() || !current.isDirectory()
1103
+ || current.dev !== expected.dev || current.ino !== expected.ino)
1104
+ return false;
1105
+ renameSync(path, quarantine);
1106
+ const moved = lstatSync(quarantine);
1107
+ if (moved.isDirectory() && !moved.isSymbolicLink()
1108
+ && moved.dev === expected.dev && moved.ino === expected.ino) {
1109
+ if (removeQuarantinedWorkspace(quarantine, expected))
1110
+ return true;
1111
+ }
1112
+ if (sameIdentity(moved, { dev: expected.dev, ino: expected.ino })) {
1113
+ try {
1114
+ lstatSync(path);
1115
+ }
1116
+ catch (error) {
1117
+ if (error.code === "ENOENT")
1118
+ renameSync(quarantine, path);
1119
+ }
1120
+ }
1121
+ return false;
1122
+ }
1123
+ finally {
1124
+ closeSync(parentHandle);
1125
+ }
1126
+ }
1127
+ catch (error) {
1128
+ if (error.code === "ENOENT")
1129
+ return expected === undefined;
1130
+ throw error;
1131
+ }
1132
+ }
1133
+ function removeQuarantinedWorkspace(path, expected) {
1134
+ let rootFd;
1135
+ try {
1136
+ rootFd = openSync(path, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
1137
+ }
1138
+ catch (error) {
1139
+ if (error.code !== "EACCES")
1140
+ return false;
1141
+ try {
1142
+ normalizeUnreadableDirectory(path, expected);
1143
+ rootFd = openSync(path, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
1144
+ }
1145
+ catch {
1146
+ return false;
1147
+ }
1148
+ }
1149
+ const originalCwd = process.cwd();
1150
+ try {
1151
+ if (!sameIdentity(fstatSync(rootFd), expected))
1152
+ return false;
1153
+ fchmodSync(rootFd, DIRECTORY_MODE);
1154
+ process.chdir(path);
1155
+ const cwdFd = openSync(".", constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
1156
+ try {
1157
+ if (!sameIdentity(fstatSync(cwdFd), expected))
1158
+ return false;
1159
+ removeDirectoryContentsFromCwd();
1160
+ }
1161
+ finally {
1162
+ closeSync(cwdFd);
1163
+ }
1164
+ process.chdir(originalCwd);
1165
+ const current = lstatSync(path);
1166
+ if (!current.isDirectory() || current.isSymbolicLink() || !sameIdentity(current, expected))
1167
+ return false;
1168
+ rmdirSync(path);
1169
+ return true;
1170
+ }
1171
+ catch {
1172
+ return false;
1173
+ }
1174
+ finally {
1175
+ process.chdir(originalCwd);
1176
+ closeSync(rootFd);
1177
+ }
1178
+ }
1179
+ function removeDirectoryContentsFromCwd() {
1180
+ const handle = opendirSync(".");
1181
+ try {
1182
+ for (let entry = handle.readSync(); entry !== null; entry = handle.readSync()) {
1183
+ removeDirectoryEntryFromCwd(entry.name, entry.isDirectory());
1184
+ }
1185
+ }
1186
+ finally {
1187
+ handle.closeSync();
1188
+ }
1189
+ }
1190
+ function removeDirectoryEntryFromCwd(name, directory) {
1191
+ const current = lstatSync(name);
1192
+ if (current.isSymbolicLink()) {
1193
+ moveAndUnlinkFromCwd(name, current);
1194
+ return;
1195
+ }
1196
+ if (!current.isDirectory() && !current.isFile()) {
1197
+ moveAndUnlinkFromCwd(name, current);
1198
+ return;
1199
+ }
1200
+ const childFd = openChildSync(name, directory, current.isDirectory() ? DIRECTORY_MODE : undefined);
1201
+ try {
1202
+ const opened = fstatSync(childFd);
1203
+ if (!sameIdentity(current, opened))
1204
+ throw new Error(`Workspace entry "${name}" changed during cleanup.`);
1205
+ if (opened.isDirectory()) {
1206
+ fchmodSync(childFd, DIRECTORY_MODE);
1207
+ moveAndRemoveDirectoryFromCwd(name, opened);
1208
+ }
1209
+ else
1210
+ moveAndUnlinkFromCwd(name, opened);
1211
+ }
1212
+ finally {
1213
+ closeSync(childFd);
1214
+ }
1215
+ }
1216
+ function moveAndUnlinkFromCwd(name, expected) {
1217
+ const alias = `.ebo-clean-${randomUUID()}`;
1218
+ try {
1219
+ lstatSync(alias);
1220
+ throw new Error(`Workspace cleanup alias already exists: "${alias}".`);
1221
+ }
1222
+ catch (error) {
1223
+ if (error.code !== "ENOENT")
1224
+ throw error;
1225
+ }
1226
+ renameSync(name, alias);
1227
+ const moved = lstatSync(alias);
1228
+ if (!sameIdentity(moved, expected))
1229
+ throw new Error(`Workspace entry "${name}" changed during cleanup.`);
1230
+ const final = lstatSync(alias);
1231
+ if (!sameIdentity(final, expected))
1232
+ throw new Error(`Workspace entry "${name}" changed during cleanup.`);
1233
+ unlinkSync(alias);
1234
+ }
1235
+ function moveAndRemoveDirectoryFromCwd(name, expected) {
1236
+ const alias = `.ebo-clean-${randomUUID()}`;
1237
+ try {
1238
+ lstatSync(alias);
1239
+ throw new Error(`Workspace cleanup alias already exists: "${alias}".`);
1240
+ }
1241
+ catch (error) {
1242
+ if (error.code !== "ENOENT")
1243
+ throw error;
1244
+ }
1245
+ renameSync(name, alias);
1246
+ let entered = false;
1247
+ try {
1248
+ if (!sameIdentity(lstatSync(alias), expected))
1249
+ throw new Error(`Workspace entry "${name}" changed during cleanup.`);
1250
+ process.chdir(alias);
1251
+ entered = true;
1252
+ const cwdFd = openSync(".", constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
1253
+ try {
1254
+ if (!sameIdentity(fstatSync(cwdFd), expected))
1255
+ throw new Error(`Workspace entry "${name}" changed during cleanup.`);
1256
+ removeDirectoryContentsFromCwd();
1257
+ }
1258
+ finally {
1259
+ closeSync(cwdFd);
1260
+ }
1261
+ }
1262
+ finally {
1263
+ if (entered)
1264
+ process.chdir("..");
1265
+ }
1266
+ const final = lstatSync(join(".", alias));
1267
+ if (!final.isDirectory() || final.isSymbolicLink() || !sameIdentity(final, expected)) {
1268
+ throw new Error(`Workspace entry "${name}" changed during cleanup.`);
1269
+ }
1270
+ rmdirSync(alias);
1271
+ }
1272
+ function assertAttemptId(attemptId) {
1273
+ if (!ATTEMPT_ID_PATTERN.test(attemptId))
1274
+ throw new Error("Workspace attempt ID is unsafe.");
1275
+ }
1276
+ function normalizedFileMode(mode) {
1277
+ return FILE_MODE | (mode & 0o100 ? 0o100 : 0);
1278
+ }
1279
+ function assertWorkspaceRoot(path, expected) {
1280
+ const metadata = lstatSync(path);
1281
+ if (!metadata.isDirectory() || metadata.isSymbolicLink()
1282
+ || metadata.dev !== expected.dev || metadata.ino !== expected.ino) {
1283
+ throw new Error("Workspace root changed during materialization.");
1284
+ }
1285
+ }
1286
+ function sameIdentity(left, right) {
1287
+ return left.dev === right.dev && left.ino === right.ino;
1288
+ }
1289
+ function sameFileSnapshot(left, right) {
1290
+ return left.size === right.size && left.mtimeNs === right.mtimeNs && left.ctimeNs === right.ctimeNs;
1291
+ }
1292
+ function workspaceTimestamp(metadata) {
1293
+ return process.platform === "win32" ? metadata.mtimeMs : metadata.mtimeNs;
1294
+ }
1295
+ function sameDigest(left, right) {
1296
+ return left.algorithm === right.algorithm && left.value === right.value;
1297
+ }
1298
+ function assertFrozenSnapshot(options, packetDigest, aggregateDigest) {
1299
+ const current = statusTaskPacket(options.bundleRoot, options.packetLocator, options.freezeLocator);
1300
+ if (current.status !== "frozen" || current.packetDigest === null || !sameDigest(current.packetDigest, packetDigest)
1301
+ || (aggregateDigest !== null && (current.aggregateDigest === null || !sameDigest(current.aggregateDigest, aggregateDigest)))) {
1302
+ throw new Error(`Frozen task packet "${options.packetLocator}" changed before workspace creation.`);
1303
+ }
1304
+ }
1305
+ function isDisjoint(left, right) {
1306
+ return !isContained(left, right) && !isContained(right, left);
1307
+ }
1308
+ function isContained(root, path) {
1309
+ const fromRoot = relative(root, path);
1310
+ return fromRoot === "" || (!isAbsolute(fromRoot) && fromRoot !== ".." && !fromRoot.startsWith(`..${sep}`));
1311
+ }
1312
+ function errorMessage(error) {
1313
+ return error instanceof Error ? error.message : "Workspace materialization failed.";
1314
+ }