@nowcrew/daemon 0.6.15 → 0.6.17

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 (46) hide show
  1. package/dist/agent-ability/controller.js +6 -2
  2. package/dist/agent-ability/resolver.js +6 -0
  3. package/dist/agent-ability/runtime-context.js +7 -1
  4. package/dist/agent-ability/runtime.js +2 -3
  5. package/dist/atomic-private-write.js +54 -1
  6. package/dist/automatic-install-target.js +40 -11
  7. package/dist/console.js +9 -0
  8. package/dist/control-plane-url.js +2 -2
  9. package/dist/daemon-migration-controller.js +198 -0
  10. package/dist/daemon-migration-wiring.js +22 -0
  11. package/dist/daemon-update-eligibility.js +1 -1
  12. package/dist/directory-projection-identity.js +32 -0
  13. package/dist/directory-projection.js +922 -0
  14. package/dist/execution-protocol.js +78 -11
  15. package/dist/execution-runner.js +50 -2
  16. package/dist/i18n.js +1 -0
  17. package/dist/local-execution-prompt.js +57 -0
  18. package/dist/local-executor.js +99 -40
  19. package/dist/machine-info.js +45 -9
  20. package/dist/normalize.js +5 -0
  21. package/dist/profile-layout.js +41 -0
  22. package/dist/project-skills/controller.js +74 -14
  23. package/dist/project-skills/execution-adapter.js +11 -0
  24. package/dist/project-skills/initialized-reconciler.js +20 -0
  25. package/dist/project-skills/projection-set-switch.js +419 -0
  26. package/dist/project-skills/projection-state-domain.js +153 -0
  27. package/dist/project-skills/projection-state-store.js +841 -0
  28. package/dist/project-skills/projection-state-transaction.js +318 -0
  29. package/dist/project-skills/projection-state.js +3 -0
  30. package/dist/project-skills/reconciler.js +299 -68
  31. package/dist/project-skills/runtime-warning.js +6 -0
  32. package/dist/project-skills/scanner.js +30 -1
  33. package/dist/project-skills/types.js +9 -0
  34. package/dist/project-workspaces/resolver.js +179 -0
  35. package/dist/project-workspaces/types.js +1 -0
  36. package/dist/prompt.js +64 -5
  37. package/dist/runner.js +1 -0
  38. package/dist/runtimes/claude.js +235 -4
  39. package/dist/runtimes/codex-app-server-runner.js +92 -23
  40. package/dist/runtimes/codex-contract.js +123 -0
  41. package/dist/runtimes/codex.js +2 -0
  42. package/dist/serve.js +31 -17
  43. package/dist/session.js +3 -0
  44. package/dist/supervised-runtime.js +12 -4
  45. package/dist/workspace.js +14 -5
  46. package/package.json +1 -1
@@ -0,0 +1,922 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { chmod, cp, lstat, mkdir, open, readFile, readlink, realpath, readdir, rename, rm, symlink, unlink, writeFile, } from "node:fs/promises";
3
+ import { posix, win32 } from "node:path";
4
+ import { durableAtomicPrivateWrite, durableDirectorySync, durablePrivateUnlink, } from "./atomic-private-write.js";
5
+ import { readExactDirectoryIdentity, sameExactDirectoryIdentity, validExactDirectoryIdentity, } from "./directory-projection-identity.js";
6
+ export class DirectoryProjectionError extends Error {
7
+ code;
8
+ constructor(code) {
9
+ super(code);
10
+ this.code = code;
11
+ this.name = "DirectoryProjectionError";
12
+ }
13
+ }
14
+ const MARKER_NAME = ".nowcrew-directory-projection.json";
15
+ const SIDECAR_SUFFIX = ".nowcrew-owner.json";
16
+ const COPY_SWAP_JOURNAL_SUFFIX = ".nowcrew-projection-journal.json";
17
+ const MAX_OWNERSHIP_METADATA_BYTES = 16 * 1024;
18
+ const DEFAULT_COPY_LIMITS = Object.freeze({
19
+ maxEntries: 10_000,
20
+ maxBytes: 256 * 1024 * 1024,
21
+ maxDepth: 32,
22
+ maxFileBytes: 64 * 1024 * 1024,
23
+ });
24
+ // Only Windows permission/policy denials authorize the readonly copy fallback.
25
+ const JUNCTION_COPY_FALLBACK_CODES = new Set(["EACCES", "EPERM"]);
26
+ const hasTraversal = (value, platform) => (platform === "win32" ? value.split(/[\\/]/u) : value.split("/"))
27
+ .some((component) => component === "..");
28
+ const isFullyQualifiedWindowsPath = (value) => {
29
+ if (/^\\\\[?.]\\/u.test(value))
30
+ return false;
31
+ if (/^[A-Za-z]:[\\/]/u.test(value))
32
+ return true;
33
+ return /^\\\\[^\\/]+[\\/][^\\/]+(?:[\\/]|$)/u.test(value);
34
+ };
35
+ const normalizeWindowsLinkTarget = (value) => {
36
+ if (/^\\\\\?\\UNC\\/iu.test(value))
37
+ return `\\\\${value.slice(8)}`;
38
+ if (/^\\\\\?\\[A-Za-z]:\\/u.test(value))
39
+ return value.slice(4);
40
+ return value;
41
+ };
42
+ const normalizeAbsolute = (value, platform, path) => {
43
+ if (value.length === 0 || value.includes("\0") || hasTraversal(value, platform)) {
44
+ throw new DirectoryProjectionError("directory_projection_path_invalid");
45
+ }
46
+ if (platform === "win32" ? !isFullyQualifiedWindowsPath(value) : !path.isAbsolute(value)) {
47
+ throw new DirectoryProjectionError("directory_projection_path_invalid");
48
+ }
49
+ return path.normalize(value);
50
+ };
51
+ const isWithin = (parent, candidate, path) => {
52
+ const relative = path.relative(parent, candidate);
53
+ return relative !== ""
54
+ && relative !== ".."
55
+ && !relative.startsWith(`..${path.sep}`)
56
+ && !path.isAbsolute(relative);
57
+ };
58
+ const samePath = (left, right, path) => path.relative(left, right) === "";
59
+ const errorCode = (error) => error.code;
60
+ const resolveLimits = (input) => {
61
+ const bounded = (name) => {
62
+ const value = input?.[name] ?? DEFAULT_COPY_LIMITS[name];
63
+ if (!Number.isSafeInteger(value) || value <= 0) {
64
+ throw new DirectoryProjectionError("directory_projection_path_invalid");
65
+ }
66
+ return Math.min(value, DEFAULT_COPY_LIMITS[name]);
67
+ };
68
+ return Object.freeze({
69
+ maxEntries: bounded("maxEntries"),
70
+ maxBytes: bounded("maxBytes"),
71
+ maxDepth: bounded("maxDepth"),
72
+ maxFileBytes: bounded("maxFileBytes"),
73
+ });
74
+ };
75
+ const assertWithinLimits = (entries, bytes, depth, fileBytes, limits) => {
76
+ if (entries > limits.maxEntries
77
+ || bytes > limits.maxBytes
78
+ || depth > limits.maxDepth
79
+ || fileBytes > limits.maxFileBytes) {
80
+ throw new DirectoryProjectionError("directory_projection_limit_exceeded");
81
+ }
82
+ };
83
+ const tryLstat = async (projectionFs, target) => {
84
+ try {
85
+ return await projectionFs.lstat(target);
86
+ }
87
+ catch (error) {
88
+ if (errorCode(error) === "ENOENT")
89
+ return null;
90
+ throw error;
91
+ }
92
+ };
93
+ const markerFor = (source, target, finalTarget, nonce) => Object.freeze({
94
+ managedBy: "nowcrew-directory-projection",
95
+ version: 2,
96
+ source,
97
+ target,
98
+ finalTarget,
99
+ nonce,
100
+ });
101
+ const readMarker = async (projectionFs, root, path) => {
102
+ const markerPath = path.join(root, MARKER_NAME);
103
+ const info = await tryLstat(projectionFs, markerPath);
104
+ if (info === null || !info.isFile() || info.isSymbolicLink()
105
+ || Number(info.size) > MAX_OWNERSHIP_METADATA_BYTES)
106
+ return null;
107
+ let raw;
108
+ try {
109
+ raw = await projectionFs.readFile(markerPath, "utf8");
110
+ }
111
+ catch (error) {
112
+ if (errorCode(error) === "ENOENT")
113
+ return null;
114
+ throw error;
115
+ }
116
+ try {
117
+ const candidate = JSON.parse(raw);
118
+ if (candidate.managedBy !== "nowcrew-directory-projection"
119
+ || candidate.version !== 2
120
+ || typeof candidate.source !== "string"
121
+ || typeof candidate.target !== "string"
122
+ || typeof candidate.finalTarget !== "string"
123
+ || typeof candidate.nonce !== "string")
124
+ return null;
125
+ return candidate;
126
+ }
127
+ catch {
128
+ return null;
129
+ }
130
+ };
131
+ const sameComponent = (left, right, path) => (path === win32 ? left.toLowerCase() : left) === (path === win32 ? right.toLowerCase() : right);
132
+ const normalizedMarker = (marker, path) => {
133
+ if (marker === null || !/^[A-Za-z0-9-]{1,128}$/u.test(marker.nonce))
134
+ return null;
135
+ const platform = path === win32 ? "win32" : "linux";
136
+ try {
137
+ return Object.freeze({
138
+ ...marker,
139
+ source: normalizeAbsolute(marker.source, platform, path),
140
+ target: normalizeAbsolute(marker.target, platform, path),
141
+ finalTarget: normalizeAbsolute(marker.finalTarget, platform, path),
142
+ });
143
+ }
144
+ catch {
145
+ return null;
146
+ }
147
+ };
148
+ const hasPermittedFinalTarget = (operationTarget, finalTarget, path) => {
149
+ if (samePath(operationTarget, finalTarget, path))
150
+ return true;
151
+ if (!sameComponent(path.basename(operationTarget), path.basename(finalTarget), path))
152
+ return false;
153
+ const operationRoot = path.dirname(operationTarget);
154
+ const finalRoot = path.dirname(finalTarget);
155
+ if (!samePath(path.dirname(operationRoot), path.dirname(finalRoot), path))
156
+ return false;
157
+ const expectedPrefix = `.${path.basename(finalRoot)}-next-`;
158
+ const operationRootName = path.basename(operationRoot);
159
+ const comparableRootName = path === win32 ? operationRootName.toLowerCase() : operationRootName;
160
+ const comparablePrefix = path === win32 ? expectedPrefix.toLowerCase() : expectedPrefix;
161
+ return comparableRootName.startsWith(comparablePrefix)
162
+ && /^[A-Za-z0-9-]{1,128}$/u.test(operationRootName.slice(expectedPrefix.length));
163
+ };
164
+ const markerMatchesOperationTarget = (marker, target, path, nonce) => {
165
+ const normalized = normalizedMarker(marker, path);
166
+ return normalized !== null
167
+ && samePath(normalized.target, target, path)
168
+ && hasPermittedFinalTarget(normalized.target, normalized.finalTarget, path)
169
+ && (nonce === undefined || normalized.nonce === nonce);
170
+ };
171
+ const markerMatchesFinalTarget = (marker, target, path, nonce) => {
172
+ const normalized = normalizedMarker(marker, path);
173
+ return normalized !== null
174
+ && samePath(normalized.finalTarget, target, path)
175
+ && hasPermittedFinalTarget(normalized.target, normalized.finalTarget, path)
176
+ && (nonce === undefined || normalized.nonce === nonce);
177
+ };
178
+ const readStagingSidecar = async (projectionFs, sidecarPath) => {
179
+ const info = await tryLstat(projectionFs, sidecarPath);
180
+ if (info === null || !info.isFile() || info.isSymbolicLink()
181
+ || Number(info.size) > MAX_OWNERSHIP_METADATA_BYTES)
182
+ return null;
183
+ let raw;
184
+ try {
185
+ raw = await projectionFs.readFile(sidecarPath, "utf8");
186
+ }
187
+ catch (error) {
188
+ if (errorCode(error) === "ENOENT")
189
+ return null;
190
+ throw error;
191
+ }
192
+ try {
193
+ const candidate = JSON.parse(raw);
194
+ if (candidate.managedBy !== "nowcrew-directory-projection-staging"
195
+ || candidate.version !== 1
196
+ || typeof candidate.staging !== "string"
197
+ || typeof candidate.target !== "string"
198
+ || typeof candidate.nonce !== "string")
199
+ return null;
200
+ return candidate;
201
+ }
202
+ catch {
203
+ return null;
204
+ }
205
+ };
206
+ const stagingSidecarMatches = (sidecar, staging, target, nonce, path) => {
207
+ if (sidecar === null || sidecar.nonce !== nonce || !/^[A-Za-z0-9-]{1,128}$/u.test(sidecar.nonce))
208
+ return false;
209
+ try {
210
+ const platform = path === win32 ? "win32" : "linux";
211
+ return samePath(normalizeAbsolute(sidecar.staging, platform, path), staging, path)
212
+ && samePath(normalizeAbsolute(sidecar.target, platform, path), target, path);
213
+ }
214
+ catch {
215
+ return false;
216
+ }
217
+ };
218
+ const isAbsoluteLink = (value) => posix.isAbsolute(value) || win32.isAbsolute(value);
219
+ const validateCopySource = async (projectionFs, source, path, limits) => {
220
+ const rootInfo = await tryLstat(projectionFs, source);
221
+ if (rootInfo === null || !rootInfo.isDirectory() || rootInfo.isSymbolicLink()) {
222
+ throw new DirectoryProjectionError("directory_projection_source_invalid");
223
+ }
224
+ const pending = [
225
+ { directory: source, depth: 0 },
226
+ ];
227
+ let entryCount = 0;
228
+ let byteCount = 0;
229
+ while (pending.length > 0) {
230
+ const current = pending.pop();
231
+ const entries = await projectionFs.readdir(current.directory, { withFileTypes: true });
232
+ for (const entry of entries) {
233
+ if (current.depth === 0 && entry.name === MARKER_NAME) {
234
+ throw new DirectoryProjectionError("directory_projection_source_invalid");
235
+ }
236
+ const candidate = path.join(current.directory, entry.name);
237
+ const candidateInfo = await tryLstat(projectionFs, candidate);
238
+ if (candidateInfo === null) {
239
+ throw new DirectoryProjectionError("directory_projection_source_invalid");
240
+ }
241
+ const depth = current.depth + 1;
242
+ entryCount += 1;
243
+ if (candidateInfo.isSymbolicLink()) {
244
+ const link = await projectionFs.readlink(candidate);
245
+ if (isAbsoluteLink(link)) {
246
+ throw new DirectoryProjectionError("directory_projection_source_invalid");
247
+ }
248
+ const resolved = path.resolve(path.dirname(candidate), link);
249
+ if (!samePath(source, resolved, path) && !isWithin(source, resolved, path)) {
250
+ throw new DirectoryProjectionError("directory_projection_source_invalid");
251
+ }
252
+ }
253
+ else if (candidateInfo.isDirectory()) {
254
+ pending.push({ directory: candidate, depth });
255
+ }
256
+ else if (!candidateInfo.isFile()) {
257
+ throw new DirectoryProjectionError("directory_projection_source_invalid");
258
+ }
259
+ else {
260
+ const fileSize = Number(candidateInfo.size);
261
+ if (!Number.isSafeInteger(fileSize) || fileSize < 0) {
262
+ throw new DirectoryProjectionError("directory_projection_limit_exceeded");
263
+ }
264
+ byteCount += fileSize;
265
+ }
266
+ const fileSize = candidateInfo.isFile() ? Number(candidateInfo.size) : 0;
267
+ assertWithinLimits(entryCount, byteCount, depth, fileSize, limits);
268
+ }
269
+ }
270
+ };
271
+ const validateStagedCopy = async (projectionFs, staging, path, limits) => {
272
+ let physicalRoot;
273
+ try {
274
+ physicalRoot = await projectionFs.realpath(staging);
275
+ }
276
+ catch {
277
+ throw new DirectoryProjectionError("directory_projection_source_invalid");
278
+ }
279
+ const pending = [
280
+ { directory: staging, depth: 0 },
281
+ ];
282
+ let entryCount = 0;
283
+ let byteCount = 0;
284
+ while (pending.length > 0) {
285
+ const current = pending.pop();
286
+ for (const entry of await projectionFs.readdir(current.directory, { withFileTypes: true })) {
287
+ const candidate = path.join(current.directory, entry.name);
288
+ const info = await projectionFs.lstat(candidate);
289
+ const adapterMarker = current.depth === 0 && entry.name === MARKER_NAME;
290
+ const depth = current.depth + 1;
291
+ if (!adapterMarker)
292
+ entryCount += 1;
293
+ if (info.isSymbolicLink()) {
294
+ const link = await projectionFs.readlink(candidate);
295
+ if (isAbsoluteLink(link)) {
296
+ throw new DirectoryProjectionError("directory_projection_source_invalid");
297
+ }
298
+ const lexicalTarget = path.resolve(path.dirname(candidate), link);
299
+ if (!samePath(staging, lexicalTarget, path) && !isWithin(staging, lexicalTarget, path)) {
300
+ throw new DirectoryProjectionError("directory_projection_source_invalid");
301
+ }
302
+ let physicalTarget;
303
+ try {
304
+ physicalTarget = await projectionFs.realpath(candidate);
305
+ }
306
+ catch {
307
+ throw new DirectoryProjectionError("directory_projection_source_invalid");
308
+ }
309
+ if (!samePath(physicalRoot, physicalTarget, path) && !isWithin(physicalRoot, physicalTarget, path)) {
310
+ throw new DirectoryProjectionError("directory_projection_source_invalid");
311
+ }
312
+ }
313
+ else if (info.isDirectory()) {
314
+ pending.push({ directory: candidate, depth });
315
+ }
316
+ else if (!info.isFile()) {
317
+ throw new DirectoryProjectionError("directory_projection_source_invalid");
318
+ }
319
+ else if (!adapterMarker) {
320
+ const fileSize = Number(info.size);
321
+ if (!Number.isSafeInteger(fileSize) || fileSize < 0) {
322
+ throw new DirectoryProjectionError("directory_projection_limit_exceeded");
323
+ }
324
+ byteCount += fileSize;
325
+ }
326
+ if (!adapterMarker) {
327
+ assertWithinLimits(entryCount, byteCount, depth, info.isFile() ? Number(info.size) : 0, limits);
328
+ }
329
+ }
330
+ }
331
+ };
332
+ const applyBestEffortReadonly = async (projectionFs, root, path) => {
333
+ const pending = [
334
+ { candidate: root, visited: false },
335
+ ];
336
+ while (pending.length > 0) {
337
+ const current = pending.pop();
338
+ const info = await projectionFs.lstat(current.candidate);
339
+ if (info.isSymbolicLink())
340
+ continue;
341
+ if (info.isDirectory() && !current.visited) {
342
+ pending.push({ candidate: current.candidate, visited: true });
343
+ for (const entry of await projectionFs.readdir(current.candidate, { withFileTypes: true })) {
344
+ pending.push({ candidate: path.join(current.candidate, entry.name), visited: false });
345
+ }
346
+ }
347
+ else if (info.isDirectory()) {
348
+ await projectionFs.chmod(current.candidate, 0o555);
349
+ }
350
+ else if (info.isFile()) {
351
+ await projectionFs.chmod(current.candidate, info.mode & 0o111 ? 0o555 : 0o444);
352
+ }
353
+ }
354
+ };
355
+ const makeWritable = async (projectionFs, root, path) => {
356
+ const pending = [root];
357
+ while (pending.length > 0) {
358
+ const current = pending.pop();
359
+ const info = await projectionFs.lstat(current);
360
+ if (info.isSymbolicLink())
361
+ continue;
362
+ if (!info.isDirectory()) {
363
+ await projectionFs.chmod(current, 0o600);
364
+ continue;
365
+ }
366
+ await projectionFs.chmod(current, 0o700);
367
+ for (const entry of await projectionFs.readdir(current, { withFileTypes: true })) {
368
+ pending.push(path.join(current, entry.name));
369
+ }
370
+ }
371
+ };
372
+ const assertOwnedArtifact = async (projectionFs, root, target, path, identity, nonce) => {
373
+ const info = await tryLstat(projectionFs, root);
374
+ if (info === null || !info.isDirectory() || info.isSymbolicLink()
375
+ || !(identity === "operation"
376
+ ? markerMatchesOperationTarget(await readMarker(projectionFs, root, path), target, path, nonce)
377
+ : markerMatchesFinalTarget(await readMarker(projectionFs, root, path), target, path))) {
378
+ throw new DirectoryProjectionError("directory_projection_artifact_unmanaged");
379
+ }
380
+ };
381
+ const removeOwnedStaging = async (projectionFs, staging, sidecarPath, target, nonce, path) => {
382
+ if (!stagingSidecarMatches(await readStagingSidecar(projectionFs, sidecarPath), staging, target, nonce, path)) {
383
+ throw new DirectoryProjectionError("directory_projection_artifact_unmanaged");
384
+ }
385
+ const staged = await tryLstat(projectionFs, staging);
386
+ if (staged !== null) {
387
+ if (!staged.isDirectory() || staged.isSymbolicLink()) {
388
+ throw new DirectoryProjectionError("directory_projection_artifact_unmanaged");
389
+ }
390
+ await makeWritable(projectionFs, staging, path);
391
+ await projectionFs.rm(staging, { recursive: true, force: true });
392
+ }
393
+ await projectionFs.unlink(sidecarPath);
394
+ };
395
+ const removeOwnedSidecar = async (projectionFs, sidecarPath, staging, target, nonce, path) => {
396
+ if (!stagingSidecarMatches(await readStagingSidecar(projectionFs, sidecarPath), staging, target, nonce, path))
397
+ return;
398
+ await projectionFs.unlink(sidecarPath);
399
+ };
400
+ const copyArtifactPaths = (target, nonce, path) => {
401
+ const parent = path.dirname(target);
402
+ const name = path.basename(target);
403
+ return Object.freeze({
404
+ parent,
405
+ staging: path.join(parent, `.${name}.nowcrew-projection-staging-${nonce}`),
406
+ rollback: path.join(parent, `.${name}.nowcrew-projection-rollback-${nonce}`),
407
+ discard: path.join(parent, `.${name}.nowcrew-projection-discard-${nonce}`),
408
+ stagingSidecar: path.join(parent, `.${name}.nowcrew-projection-staging-${nonce}${SIDECAR_SUFFIX}`),
409
+ journal: path.join(parent, `.${name}${COPY_SWAP_JOURNAL_SUFFIX}`),
410
+ });
411
+ };
412
+ const copyDirectoryIdentity = async (projectionFs, target) => {
413
+ try {
414
+ return await readExactDirectoryIdentity(target, projectionFs.lstat);
415
+ }
416
+ catch {
417
+ throw new DirectoryProjectionError("directory_projection_artifact_unmanaged");
418
+ }
419
+ };
420
+ const assertCopyIdentity = async (projectionFs, target, expected) => {
421
+ const actual = await copyDirectoryIdentity(projectionFs, target);
422
+ if (actual === null || !sameExactDirectoryIdentity(actual, expected)) {
423
+ throw new DirectoryProjectionError("directory_projection_artifact_unmanaged");
424
+ }
425
+ };
426
+ const readCopySwapJournal = async (projectionFs, target, path) => {
427
+ const placeholder = copyArtifactPaths(target, "placeholder", path);
428
+ const info = await tryLstat(projectionFs, placeholder.journal);
429
+ if (info === null)
430
+ return null;
431
+ if (!info.isFile() || info.isSymbolicLink() || Number(info.size) > MAX_OWNERSHIP_METADATA_BYTES) {
432
+ throw new DirectoryProjectionError("directory_projection_artifact_unmanaged");
433
+ }
434
+ let candidate;
435
+ try {
436
+ candidate = JSON.parse(await projectionFs.readFile(placeholder.journal, "utf8"));
437
+ }
438
+ catch (error) {
439
+ if (error instanceof SyntaxError) {
440
+ throw new DirectoryProjectionError("directory_projection_artifact_unmanaged");
441
+ }
442
+ throw error;
443
+ }
444
+ if (candidate.managedBy !== "nowcrew-directory-projection-swap"
445
+ || candidate.version !== 1
446
+ || (candidate.phase !== "prepared"
447
+ && candidate.phase !== "previous_moved"
448
+ && candidate.phase !== "committed")
449
+ || typeof candidate.nonce !== "string"
450
+ || !/^[A-Za-z0-9-]{1,128}$/u.test(candidate.nonce)
451
+ || typeof candidate.source !== "string"
452
+ || typeof candidate.target !== "string"
453
+ || typeof candidate.finalTarget !== "string"
454
+ || typeof candidate.staging !== "string"
455
+ || typeof candidate.rollback !== "string"
456
+ || typeof candidate.discard !== "string"
457
+ || typeof candidate.stagingSidecar !== "string"
458
+ || typeof candidate.hadPrevious !== "boolean"
459
+ || !validExactDirectoryIdentity(candidate.activatedIdentity)
460
+ || (candidate.previousIdentity !== null && !validExactDirectoryIdentity(candidate.previousIdentity))
461
+ || (candidate.previousNonce !== null && typeof candidate.previousNonce !== "string")) {
462
+ throw new DirectoryProjectionError("directory_projection_artifact_unmanaged");
463
+ }
464
+ const platform = path === win32 ? "win32" : "linux";
465
+ let normalizedSource;
466
+ let normalizedTarget;
467
+ let normalizedFinalTarget;
468
+ let normalizedStaging;
469
+ let normalizedRollback;
470
+ let normalizedDiscard;
471
+ let normalizedSidecar;
472
+ try {
473
+ normalizedSource = normalizeAbsolute(candidate.source, platform, path);
474
+ normalizedTarget = normalizeAbsolute(candidate.target, platform, path);
475
+ normalizedFinalTarget = normalizeAbsolute(candidate.finalTarget, platform, path);
476
+ normalizedStaging = normalizeAbsolute(candidate.staging, platform, path);
477
+ normalizedRollback = normalizeAbsolute(candidate.rollback, platform, path);
478
+ normalizedDiscard = normalizeAbsolute(candidate.discard, platform, path);
479
+ normalizedSidecar = normalizeAbsolute(candidate.stagingSidecar, platform, path);
480
+ }
481
+ catch {
482
+ throw new DirectoryProjectionError("directory_projection_artifact_unmanaged");
483
+ }
484
+ const expected = copyArtifactPaths(target, candidate.nonce, path);
485
+ if (!samePath(normalizedTarget, target, path)
486
+ || !samePath(normalizedStaging, expected.staging, path)
487
+ || !samePath(normalizedRollback, expected.rollback, path)
488
+ || !samePath(normalizedDiscard, expected.discard, path)
489
+ || !samePath(normalizedSidecar, expected.stagingSidecar, path)
490
+ || !hasPermittedFinalTarget(normalizedTarget, normalizedFinalTarget, path)
491
+ || samePath(normalizedSource, normalizedTarget, path)
492
+ || isWithin(normalizedSource, normalizedTarget, path)
493
+ || isWithin(normalizedTarget, normalizedSource, path)
494
+ || samePath(normalizedSource, normalizedFinalTarget, path)
495
+ || isWithin(normalizedSource, normalizedFinalTarget, path)
496
+ || isWithin(normalizedFinalTarget, normalizedSource, path)
497
+ || candidate.hadPrevious !== (candidate.previousIdentity !== null)
498
+ || candidate.hadPrevious !== (candidate.previousNonce !== null)
499
+ || (candidate.previousNonce !== null && !/^[A-Za-z0-9-]{1,128}$/u.test(candidate.previousNonce))) {
500
+ throw new DirectoryProjectionError("directory_projection_artifact_unmanaged");
501
+ }
502
+ return Object.freeze({
503
+ ...candidate,
504
+ source: normalizedSource,
505
+ target: normalizedTarget,
506
+ finalTarget: normalizedFinalTarget,
507
+ staging: normalizedStaging,
508
+ rollback: normalizedRollback,
509
+ discard: normalizedDiscard,
510
+ stagingSidecar: normalizedSidecar,
511
+ });
512
+ };
513
+ const writeCopySwapJournal = async (projectionFs, journal, path) => {
514
+ const journalPath = copyArtifactPaths(journal.target, journal.nonce, path).journal;
515
+ await durableAtomicPrivateWrite(journalPath, `${JSON.stringify(journal)}\n`, {
516
+ fs: projectionFs,
517
+ parentDirectory: path.dirname(journalPath),
518
+ });
519
+ };
520
+ const removeCopySwapJournal = async (projectionFs, journal, path) => {
521
+ const journalPath = copyArtifactPaths(journal.target, journal.nonce, path).journal;
522
+ await durablePrivateUnlink(journalPath, {
523
+ fs: projectionFs,
524
+ parentDirectory: path.dirname(journalPath),
525
+ });
526
+ };
527
+ const assertActivatedCopy = async (projectionFs, root, journal, path) => {
528
+ await assertCopyIdentity(projectionFs, root, journal.activatedIdentity);
529
+ await assertOwnedArtifact(projectionFs, root, journal.target, path, "operation", journal.nonce);
530
+ };
531
+ const assertPreviousCopy = async (projectionFs, root, journal, path, requireMarker) => {
532
+ if (journal.previousIdentity === null || journal.previousNonce === null) {
533
+ throw new DirectoryProjectionError("directory_projection_artifact_unmanaged");
534
+ }
535
+ await assertCopyIdentity(projectionFs, root, journal.previousIdentity);
536
+ if (requireMarker && !markerMatchesFinalTarget(await readMarker(projectionFs, root, path), journal.target, path, journal.previousNonce)) {
537
+ throw new DirectoryProjectionError("directory_projection_artifact_unmanaged");
538
+ }
539
+ };
540
+ const cleanupCommittedCopySwap = async (projectionFs, journal, path, syncDirectory) => {
541
+ const parent = path.dirname(journal.target);
542
+ await assertActivatedCopy(projectionFs, journal.target, journal, path);
543
+ const rollback = await copyDirectoryIdentity(projectionFs, journal.rollback);
544
+ if (rollback !== null) {
545
+ if (!journal.hadPrevious || await copyDirectoryIdentity(projectionFs, journal.discard) !== null) {
546
+ throw new DirectoryProjectionError("directory_projection_artifact_unmanaged");
547
+ }
548
+ await assertPreviousCopy(projectionFs, journal.rollback, journal, path, true);
549
+ await projectionFs.rename(journal.rollback, journal.discard);
550
+ await syncDirectory(parent);
551
+ }
552
+ const discard = await copyDirectoryIdentity(projectionFs, journal.discard);
553
+ if (discard !== null) {
554
+ if (!journal.hadPrevious)
555
+ throw new DirectoryProjectionError("directory_projection_artifact_unmanaged");
556
+ await assertPreviousCopy(projectionFs, journal.discard, journal, path, false);
557
+ await makeWritable(projectionFs, journal.discard, path);
558
+ await projectionFs.rm(journal.discard, { recursive: true, force: true });
559
+ await syncDirectory(parent);
560
+ }
561
+ if (await copyDirectoryIdentity(projectionFs, journal.staging) !== null) {
562
+ throw new DirectoryProjectionError("directory_projection_artifact_unmanaged");
563
+ }
564
+ await removeOwnedSidecar(projectionFs, journal.stagingSidecar, journal.staging, journal.target, journal.nonce, path);
565
+ await syncDirectory(parent);
566
+ await removeCopySwapJournal(projectionFs, journal, path);
567
+ };
568
+ const rollbackUncommittedCopySwap = async (projectionFs, journal, path, syncDirectory) => {
569
+ const parent = path.dirname(journal.target);
570
+ const current = await copyDirectoryIdentity(projectionFs, journal.target);
571
+ const currentIsPrevious = current !== null
572
+ && journal.previousIdentity !== null
573
+ && sameExactDirectoryIdentity(current, journal.previousIdentity);
574
+ if (current !== null && !currentIsPrevious) {
575
+ await assertActivatedCopy(projectionFs, journal.target, journal, path);
576
+ if (await copyDirectoryIdentity(projectionFs, journal.discard) !== null) {
577
+ throw new DirectoryProjectionError("directory_projection_artifact_unmanaged");
578
+ }
579
+ await projectionFs.rename(journal.target, journal.discard);
580
+ await syncDirectory(parent);
581
+ }
582
+ const afterActivation = await copyDirectoryIdentity(projectionFs, journal.target);
583
+ if (journal.hadPrevious) {
584
+ if (afterActivation === null) {
585
+ await assertPreviousCopy(projectionFs, journal.rollback, journal, path, true);
586
+ await projectionFs.rename(journal.rollback, journal.target);
587
+ await syncDirectory(parent);
588
+ }
589
+ else {
590
+ await assertPreviousCopy(projectionFs, journal.target, journal, path, true);
591
+ }
592
+ }
593
+ else if (afterActivation !== null) {
594
+ throw new DirectoryProjectionError("directory_projection_artifact_unmanaged");
595
+ }
596
+ if (await copyDirectoryIdentity(projectionFs, journal.rollback) !== null) {
597
+ throw new DirectoryProjectionError("directory_projection_artifact_unmanaged");
598
+ }
599
+ if (await copyDirectoryIdentity(projectionFs, journal.discard) !== null) {
600
+ await assertCopyIdentity(projectionFs, journal.discard, journal.activatedIdentity);
601
+ await makeWritable(projectionFs, journal.discard, path);
602
+ await projectionFs.rm(journal.discard, { recursive: true, force: true });
603
+ await syncDirectory(parent);
604
+ }
605
+ const staging = await copyDirectoryIdentity(projectionFs, journal.staging);
606
+ if (staging !== null) {
607
+ if (!sameExactDirectoryIdentity(staging, journal.activatedIdentity)) {
608
+ throw new DirectoryProjectionError("directory_projection_artifact_unmanaged");
609
+ }
610
+ await removeOwnedStaging(projectionFs, journal.staging, journal.stagingSidecar, journal.target, journal.nonce, path);
611
+ await syncDirectory(parent);
612
+ }
613
+ else {
614
+ await removeOwnedSidecar(projectionFs, journal.stagingSidecar, journal.staging, journal.target, journal.nonce, path);
615
+ await syncDirectory(parent);
616
+ }
617
+ await removeCopySwapJournal(projectionFs, journal, path);
618
+ };
619
+ const recoverManagedCopySwap = async (projectionFs, target, path, syncDirectory) => {
620
+ const journal = await readCopySwapJournal(projectionFs, target, path);
621
+ if (journal === null)
622
+ return;
623
+ if (journal.phase === "committed") {
624
+ await cleanupCommittedCopySwap(projectionFs, journal, path, syncDirectory);
625
+ }
626
+ else {
627
+ await rollbackUncommittedCopySwap(projectionFs, journal, path, syncDirectory);
628
+ }
629
+ };
630
+ const mappedPublishError = (error) => new DirectoryProjectionError(errorCode(error) === "EXDEV"
631
+ ? "directory_projection_cross_volume"
632
+ : "directory_projection_publish_failed");
633
+ const mappedCleanupError = () => new DirectoryProjectionError("directory_projection_cleanup_pending");
634
+ const projectManagedCopy = async (projectionFs, source, target, finalTarget, path, limits, randomId, syncDirectory) => {
635
+ await validateCopySource(projectionFs, source, path, limits);
636
+ const nonce = randomId();
637
+ if (!/^[A-Za-z0-9-]{1,128}$/u.test(nonce)) {
638
+ throw new DirectoryProjectionError("directory_projection_path_invalid");
639
+ }
640
+ const artifacts = copyArtifactPaths(target, nonce, path);
641
+ const parent = artifacts.parent;
642
+ const name = path.basename(target);
643
+ if (name.length === 0)
644
+ throw new DirectoryProjectionError("directory_projection_path_invalid");
645
+ const { staging, rollback, discard, stagingSidecar } = artifacts;
646
+ if (!samePath(path.dirname(staging), parent, path)
647
+ || !samePath(path.dirname(rollback), parent, path)
648
+ || !samePath(path.dirname(discard), parent, path)
649
+ || !samePath(path.parse(staging).root, path.parse(target).root, path)
650
+ || !samePath(path.parse(rollback).root, path.parse(target).root, path)
651
+ || !samePath(path.parse(discard).root, path.parse(target).root, path)) {
652
+ throw new DirectoryProjectionError("directory_projection_cross_volume");
653
+ }
654
+ if (await tryLstat(projectionFs, staging) !== null
655
+ || await tryLstat(projectionFs, rollback) !== null
656
+ || await tryLstat(projectionFs, discard) !== null
657
+ || await tryLstat(projectionFs, stagingSidecar) !== null
658
+ || await tryLstat(projectionFs, artifacts.journal) !== null) {
659
+ throw new DirectoryProjectionError("directory_projection_artifact_unmanaged");
660
+ }
661
+ const sidecar = Object.freeze({
662
+ managedBy: "nowcrew-directory-projection-staging",
663
+ version: 1,
664
+ staging,
665
+ target,
666
+ nonce,
667
+ });
668
+ try {
669
+ await projectionFs.writeFile(stagingSidecar, JSON.stringify(sidecar), {
670
+ encoding: "utf8",
671
+ mode: 0o600,
672
+ flag: "wx",
673
+ });
674
+ }
675
+ catch (error) {
676
+ if (errorCode(error) !== "EEXIST" && stagingSidecarMatches(await readStagingSidecar(projectionFs, stagingSidecar), staging, target, nonce, path)) {
677
+ await projectionFs.unlink(stagingSidecar);
678
+ }
679
+ if (errorCode(error) === "EEXIST") {
680
+ throw new DirectoryProjectionError("directory_projection_artifact_unmanaged");
681
+ }
682
+ throw error;
683
+ }
684
+ try {
685
+ await projectionFs.mkdir(staging, { mode: 0o700 });
686
+ }
687
+ catch (error) {
688
+ await removeOwnedSidecar(projectionFs, stagingSidecar, staging, target, nonce, path);
689
+ if (errorCode(error) === "EEXIST") {
690
+ throw new DirectoryProjectionError("directory_projection_artifact_unmanaged");
691
+ }
692
+ throw error;
693
+ }
694
+ const marker = markerFor(source, target, finalTarget, nonce);
695
+ try {
696
+ await projectionFs.writeFile(path.join(staging, MARKER_NAME), JSON.stringify(marker), {
697
+ encoding: "utf8",
698
+ mode: 0o600,
699
+ flag: "wx",
700
+ });
701
+ }
702
+ catch (error) {
703
+ await removeOwnedStaging(projectionFs, staging, stagingSidecar, target, nonce, path);
704
+ throw error;
705
+ }
706
+ try {
707
+ await projectionFs.cp(source, staging, {
708
+ recursive: true,
709
+ preserveTimestamps: true,
710
+ dereference: false,
711
+ verbatimSymlinks: true,
712
+ });
713
+ await validateStagedCopy(projectionFs, staging, path, limits);
714
+ if (!markerMatchesOperationTarget(await readMarker(projectionFs, staging, path), target, path, nonce)) {
715
+ throw new DirectoryProjectionError("directory_projection_source_invalid");
716
+ }
717
+ await applyBestEffortReadonly(projectionFs, staging, path);
718
+ }
719
+ catch (error) {
720
+ await removeOwnedStaging(projectionFs, staging, stagingSidecar, target, nonce, path);
721
+ throw error;
722
+ }
723
+ let previousIdentity = null;
724
+ let previousNonce = null;
725
+ try {
726
+ const previous = await tryLstat(projectionFs, target);
727
+ if (previous !== null) {
728
+ const previousMarker = await readMarker(projectionFs, target, path);
729
+ if (!previous.isDirectory() || previous.isSymbolicLink()
730
+ || !markerMatchesFinalTarget(previousMarker, target, path)) {
731
+ throw new DirectoryProjectionError("directory_projection_target_unmanaged");
732
+ }
733
+ previousIdentity = await copyDirectoryIdentity(projectionFs, target);
734
+ if (previousIdentity === null) {
735
+ throw new DirectoryProjectionError("directory_projection_artifact_unmanaged");
736
+ }
737
+ previousNonce = previousMarker.nonce;
738
+ }
739
+ }
740
+ catch (error) {
741
+ await removeOwnedStaging(projectionFs, staging, stagingSidecar, target, nonce, path);
742
+ throw error;
743
+ }
744
+ const activatedIdentity = await copyDirectoryIdentity(projectionFs, staging);
745
+ if (activatedIdentity === null) {
746
+ await removeOwnedStaging(projectionFs, staging, stagingSidecar, target, nonce, path);
747
+ throw new DirectoryProjectionError("directory_projection_artifact_unmanaged");
748
+ }
749
+ let journal = Object.freeze({
750
+ managedBy: "nowcrew-directory-projection-swap",
751
+ version: 1,
752
+ phase: "prepared",
753
+ nonce,
754
+ source,
755
+ target,
756
+ finalTarget,
757
+ staging,
758
+ rollback,
759
+ discard,
760
+ stagingSidecar,
761
+ hadPrevious: previousIdentity !== null,
762
+ previousNonce,
763
+ previousIdentity,
764
+ activatedIdentity,
765
+ });
766
+ try {
767
+ await writeCopySwapJournal(projectionFs, journal, path);
768
+ if (journal.hadPrevious) {
769
+ await projectionFs.rename(target, rollback);
770
+ await syncDirectory(parent);
771
+ await assertPreviousCopy(projectionFs, rollback, journal, path, true);
772
+ }
773
+ journal = Object.freeze({ ...journal, phase: "previous_moved" });
774
+ await writeCopySwapJournal(projectionFs, journal, path);
775
+ await assertActivatedCopy(projectionFs, staging, journal, path);
776
+ await projectionFs.rename(staging, target);
777
+ await syncDirectory(parent);
778
+ await assertActivatedCopy(projectionFs, target, journal, path);
779
+ const committed = Object.freeze({ ...journal, phase: "committed" });
780
+ await writeCopySwapJournal(projectionFs, committed, path);
781
+ journal = committed;
782
+ }
783
+ catch (error) {
784
+ const rollbackJournal = journal.phase === "committed"
785
+ ? Object.freeze({ ...journal, phase: "previous_moved" })
786
+ : journal;
787
+ try {
788
+ await writeCopySwapJournal(projectionFs, rollbackJournal, path);
789
+ await rollbackUncommittedCopySwap(projectionFs, rollbackJournal, path, syncDirectory);
790
+ }
791
+ catch {
792
+ throw mappedCleanupError();
793
+ }
794
+ if (error instanceof DirectoryProjectionError)
795
+ throw error;
796
+ throw mappedPublishError(error);
797
+ }
798
+ try {
799
+ await cleanupCommittedCopySwap(projectionFs, journal, path, syncDirectory);
800
+ }
801
+ catch {
802
+ throw mappedCleanupError();
803
+ }
804
+ return Object.freeze({ mode: "copy", target, copyProtection: "best-effort" });
805
+ };
806
+ const resolveFileSystem = (options) => ({
807
+ chmod: options.fs?.chmod ?? chmod,
808
+ cp: options.fs?.cp ?? cp,
809
+ lstat: options.fs?.lstat ?? lstat,
810
+ mkdir: options.fs?.mkdir ?? mkdir,
811
+ open: options.fs?.open ?? open,
812
+ readFile: options.fs?.readFile ?? readFile,
813
+ readlink: options.fs?.readlink ?? readlink,
814
+ realpath: options.fs?.realpath ?? realpath,
815
+ readdir: options.fs?.readdir ?? readdir,
816
+ rename: options.fs?.rename ?? rename,
817
+ rm: options.fs?.rm ?? rm,
818
+ symlink: options.fs?.symlink ?? symlink,
819
+ unlink: options.fs?.unlink ?? unlink,
820
+ writeFile: options.fs?.writeFile ?? writeFile,
821
+ });
822
+ export async function isManagedDirectoryProjectionCopy(target, platform, options = {}) {
823
+ if (platform !== "win32")
824
+ return false;
825
+ const path = platform === "win32" ? win32 : posix;
826
+ let normalizedTarget;
827
+ try {
828
+ normalizedTarget = normalizeAbsolute(target, platform, path);
829
+ }
830
+ catch {
831
+ return false;
832
+ }
833
+ const projectionFs = resolveFileSystem(options);
834
+ const info = await tryLstat(projectionFs, normalizedTarget);
835
+ return info !== null
836
+ && info.isDirectory()
837
+ && !info.isSymbolicLink()
838
+ && markerMatchesFinalTarget(await readMarker(projectionFs, normalizedTarget, path), normalizedTarget, path);
839
+ }
840
+ /**
841
+ * Projects one absolute directory without exposing host paths in errors.
842
+ *
843
+ * Containment is component-based in the target platform's path flavor: source and target may live in
844
+ * unrelated trees, but neither target identity may contain the source or be contained by it. Every
845
+ * adapter-owned staging, rollback, discard, and sidecar path is a direct sibling of the target on its
846
+ * volume. Raw `..` traversal, drive-relative/root-relative Windows paths, and device namespaces are
847
+ * rejected before IO.
848
+ *
849
+ * Windows copy ownership requires a version-2 marker whose normalized `finalTarget` exactly equals the
850
+ * inspected target. A distinct operation target is accepted only for the complete sibling
851
+ * `.skills-next-<id>/<name>` to `skills/<name>` lineage used by Project Skills reconciliation. Legacy,
852
+ * incomplete, POSIX, or mismatched markers never authorize refresh or deletion.
853
+ *
854
+ * Copy fallback never follows source links, rejects every absolute link form, and permits a relative link
855
+ * only when its completed staging target is both lexically and physically contained by that staging tree.
856
+ * Both traversals enforce fixed entry/byte/depth/file-size ceilings. Chmod only preserves readonly intent:
857
+ * on Windows copy protection is explicitly `best-effort`, not an enforced ACL security boundary, and the
858
+ * capability remains gated from Windows advertisement pending a native ACL rollout smoke test.
859
+ *
860
+ * Links use one symlink call, and a new copy is published by one rename. Refreshing a managed copy uses
861
+ * two same-parent renames, so the target name can be briefly absent, but readers never see a partially
862
+ * copied or mixed tree. A swap journal is fsynced before projection mutations; the new copy commits only
863
+ * after its activated identity and the committed journal rename are durable. Pre-commit failures restore
864
+ * the validated previous copy. Post-commit cleanup failures return `directory_projection_cleanup_pending`
865
+ * with the journal retained so the next call resumes exact nonce-owned cleanup. `EXDEV` is reported as
866
+ * `directory_projection_cross_volume` instead of claiming an atomic cross-volume switch.
867
+ */
868
+ export async function projectDirectory(source, target, platform, options = {}) {
869
+ const path = platform === "win32" ? win32 : posix;
870
+ const normalizedSource = normalizeAbsolute(source, platform, path);
871
+ const normalizedTarget = normalizeAbsolute(target, platform, path);
872
+ const normalizedFinalTarget = options.finalTarget === undefined
873
+ ? normalizedTarget
874
+ : normalizeAbsolute(options.finalTarget, platform, path);
875
+ const overlapsSource = (candidate) => samePath(normalizedSource, candidate, path)
876
+ || isWithin(normalizedSource, candidate, path)
877
+ || isWithin(candidate, normalizedSource, path);
878
+ if (overlapsSource(normalizedTarget)
879
+ || overlapsSource(normalizedFinalTarget)
880
+ || !hasPermittedFinalTarget(normalizedTarget, normalizedFinalTarget, path)) {
881
+ throw new DirectoryProjectionError("directory_projection_path_overlap");
882
+ }
883
+ const projectionFs = resolveFileSystem(options);
884
+ const syncDirectory = options.syncDirectory ?? ((directory) => durableDirectorySync(directory, { fs: projectionFs }));
885
+ const limits = resolveLimits(options.limits);
886
+ const mode = platform === "win32" ? "junction" : "symlink";
887
+ if (platform === "win32") {
888
+ try {
889
+ await recoverManagedCopySwap(projectionFs, normalizedTarget, path, syncDirectory);
890
+ }
891
+ catch (error) {
892
+ if (error instanceof DirectoryProjectionError)
893
+ throw error;
894
+ throw mappedCleanupError();
895
+ }
896
+ }
897
+ const existing = await tryLstat(projectionFs, normalizedTarget);
898
+ if (existing !== null) {
899
+ if (existing.isSymbolicLink()) {
900
+ const rawLink = await projectionFs.readlink(normalizedTarget);
901
+ const link = path.resolve(path.dirname(normalizedTarget), platform === "win32" ? normalizeWindowsLinkTarget(rawLink) : rawLink);
902
+ if (!samePath(link, normalizedSource, path)) {
903
+ throw new DirectoryProjectionError("directory_projection_target_unmanaged");
904
+ }
905
+ return Object.freeze({ mode, target: normalizedTarget });
906
+ }
907
+ if (platform === "win32" && existing.isDirectory()
908
+ && markerMatchesFinalTarget(await readMarker(projectionFs, normalizedTarget, path), normalizedTarget, path)) {
909
+ return projectManagedCopy(projectionFs, normalizedSource, normalizedTarget, normalizedFinalTarget, path, limits, options.randomId ?? randomUUID, syncDirectory);
910
+ }
911
+ throw new DirectoryProjectionError("directory_projection_target_unmanaged");
912
+ }
913
+ try {
914
+ await projectionFs.symlink(normalizedSource, normalizedTarget, platform === "win32" ? "junction" : "dir");
915
+ }
916
+ catch (error) {
917
+ if (platform !== "win32" || !JUNCTION_COPY_FALLBACK_CODES.has(errorCode(error) ?? ""))
918
+ throw error;
919
+ return projectManagedCopy(projectionFs, normalizedSource, normalizedTarget, normalizedFinalTarget, path, limits, options.randomId ?? randomUUID, syncDirectory);
920
+ }
921
+ return Object.freeze({ mode, target: normalizedTarget });
922
+ }