@deepseek-ai/dsh-experimental-webworker-runtime 0.2.1-alpha.2 → 0.2.1-alpha.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.js CHANGED
@@ -142,6 +142,122 @@ function parseInboundFrame(data) {
142
142
  };
143
143
  }
144
144
  //#endregion
145
+ //#region src/node/notImplementedFail.ts
146
+ /** Node's standard code for a feature the running platform cannot provide. */
147
+ const FEATURE_UNAVAILABLE_ON_PLATFORM = "ERR_FEATURE_UNAVAILABLE_ON_PLATFORM";
148
+ /**
149
+ * Build the refusal error and write it to the console first, for stubs that
150
+ * cannot be a plain throwing function (constructors, methods on structural
151
+ * fakes).
152
+ * @param module - module specifier being stubbed.
153
+ * @param symbol - unavailable member, named as the importer sees it.
154
+ * @returns the error to throw, with `code` set to Node's `ERR_FEATURE_UNAVAILABLE_ON_PLATFORM`.
155
+ */
156
+ function notAvailableError(module, symbol) {
157
+ const message = `web-preview: ${module}.${symbol} is not available in the worker host`;
158
+ console.error(message);
159
+ return Object.assign(new Error(message), { code: FEATURE_UNAVAILABLE_ON_PLATFORM });
160
+ }
161
+ //#endregion
162
+ //#region src/node/internal_modules/module-map.ts
163
+ /**
164
+ * `internal/modules/esm/module_map` for the Worker, following Node v24.18.0. The Worker evaluates no ES module
165
+ * jobs, so a {@link LoadCache} stays empty and refuses to store one.
166
+ * @module @deepseek-ai/dsh-experimental-webworker-runtime/src/node/internal_modules/module-map
167
+ */
168
+ /** Job slot Node uses when an import names no `type` attribute. */
169
+ const IMPLICIT_TYPE_ATTRIBUTE = "javascript";
170
+ /**
171
+ * Node's `validateString` refusal.
172
+ * @param value - Argument to check.
173
+ * @param name - Argument name in the message.
174
+ */
175
+ function validateString(value, name) {
176
+ if (typeof value === "string") return;
177
+ throw Object.assign(/* @__PURE__ */ new TypeError(`The "${name}" argument must be of type string. Received ${typeof value}`), { code: "ERR_INVALID_ARG_TYPE" });
178
+ }
179
+ /** Module jobs keyed by URL, then by import `type` attribute; always empty in the Worker. */
180
+ var LoadCache = class extends Map {
181
+ /**
182
+ * Read the job for one URL and type.
183
+ * @param url - Module URL.
184
+ * @param type - Import `type` attribute.
185
+ * @returns The cached job, if any.
186
+ */
187
+ get(url, type = IMPLICIT_TYPE_ATTRIBUTE) {
188
+ validateString(url, "url");
189
+ validateString(type, "type");
190
+ return super.get(url)?.[type];
191
+ }
192
+ /**
193
+ * Refuse to store a job: the Worker creates no ES module jobs.
194
+ * @param url - Module URL.
195
+ * @param type - Import `type` attribute.
196
+ * @param _job - Job Node would store.
197
+ * @returns Never; always throws.
198
+ */
199
+ set(url, type = IMPLICIT_TYPE_ATTRIBUTE, _job) {
200
+ validateString(url, "url");
201
+ validateString(type, "type");
202
+ throw notAvailableError("internal/modules/esm/module_map", "LoadCache.set");
203
+ }
204
+ /**
205
+ * Check for the job of one URL and type.
206
+ * @param url - Module URL.
207
+ * @param type - Import `type` attribute.
208
+ * @returns Whether a job is cached.
209
+ */
210
+ has(url, type = IMPLICIT_TYPE_ATTRIBUTE) {
211
+ validateString(url, "url");
212
+ validateString(type, "type");
213
+ return super.get(url)?.[type] !== void 0;
214
+ }
215
+ };
216
+ //#endregion
217
+ //#region src/node/internal_modules/esm-loader.ts
218
+ /**
219
+ * `internal/modules/esm/loader` cascaded loader for the Worker. {@link createCascadedLoader} builds the object with
220
+ * the Node v24.18.0 (v2) loader members this repository calls: `resolveSync` and `import` over one Worker module
221
+ * loader, and an empty `loadCache`. ES module jobs are unavailable on this platform.
222
+ * @module @deepseek-ai/dsh-experimental-webworker-runtime/src/node/internal_modules/esm-loader
223
+ */
224
+ /**
225
+ * Translate a Worker resolution failure into the code Node's ESM resolver reports.
226
+ * @param error - Failure thrown by the Worker module loader.
227
+ * @returns The same failure with an ESM resolution code.
228
+ */
229
+ function esmResolutionError(error) {
230
+ if (error instanceof Error && error.code === "MODULE_NOT_FOUND") return Object.assign(error, { code: "ERR_MODULE_NOT_FOUND" });
231
+ return error;
232
+ }
233
+ /**
234
+ * Build the cascaded loader for one Worker module loader.
235
+ * @param source - The Worker module loader's resolution and loading operations.
236
+ * @returns A cascaded loader delegating to `source`.
237
+ */
238
+ function createCascadedLoader(source) {
239
+ return {
240
+ loadCache: new LoadCache(),
241
+ resolveSync(parentURL, request) {
242
+ try {
243
+ const { url, format } = source.resolveSync(request.specifier, parentURL);
244
+ return {
245
+ url,
246
+ format
247
+ };
248
+ } catch (error) {
249
+ throw esmResolutionError(error);
250
+ }
251
+ },
252
+ import(specifier, parentURL) {
253
+ return source.import(specifier, parentURL);
254
+ },
255
+ getOrCreateModuleJob() {
256
+ throw notAvailableError("internal/modules/esm/loader", "getOrCreateModuleJob");
257
+ }
258
+ };
259
+ }
260
+ //#endregion
145
261
  //#region src/module-system/posix-path.ts
146
262
  var posix_path_exports = /* @__PURE__ */ __exportAll({
147
263
  SEP: () => "/",
@@ -306,8 +422,8 @@ function fileUrlToPath(url) {
306
422
  //#region src/image-layout.ts
307
423
  /**
308
424
  * Image layout contract shared by the packer and the worker host: the virtual
309
- * root, where the composed config and the manifest sit inside the image, and
310
- * the working directories every image carries empty. One definition, two
425
+ * root, where the manifest sits inside the image, where lazy archives sit beside
426
+ * it, and the working directories every image carries empty. One definition, two
311
427
  * consumers — the packer writes this layout, the worker host mounts it.
312
428
  */
313
429
  /** Default virtual root; the runtime mounts the image here unless told otherwise. */
@@ -318,12 +434,21 @@ const DEFAULT_ROOT = "/dsh";
318
434
  * there, so the extension is part of what a deployment serves.
319
435
  */
320
436
  const IMAGE_FILE_NAME = "vfs-image.tar.gz";
321
- /** Image path the composed profile is written to; the runtime's Loader reads it. */
322
- const IMAGE_CONFIG_PATH = "config/cordis.yml";
323
437
  /** Image path of the manifest the runtime reads before it wraps a single module. */
324
438
  const IMAGE_MANIFEST_PATH = "config/vfs-manifest.json";
439
+ /**
440
+ * Directory, relative to the base image URL, holding the lazy chunk archives:
441
+ * `lazy/<sha256 of the archive>.tar.gz`, one page chunk each.
442
+ */
443
+ const IMAGE_LAZY_DIRECTORY = "lazy";
325
444
  /** Home directory under the root; the process shim's `DSH_HOME`/`HOME` default. */
326
445
  const IMAGE_HOME_DIRECTORY = "home";
446
+ /**
447
+ * Config fields the Worker requires on Loader rows, by row id. The packer writes them into the image's home patch
448
+ * (`home/cordis.patch.yml`) over each row's composed config: the Worker carries no Zstandard codec, so JSONL Session
449
+ * logs stay plaintext.
450
+ */
451
+ const PLATFORM_ENTRY_CONFIG = { "session-persistence-jsonl": { compression: "none" } };
327
452
  /** Working directories the host tree expects to exist, empty. */
328
453
  const IMAGE_EMPTY_DIRECTORIES = [
329
454
  "home/",
@@ -361,10 +486,11 @@ const WRAPPER_PARAMS = [
361
486
  //#endregion
362
487
  //#region src/module-system/module-loader.ts
363
488
  /**
364
- * CommonJS module loader over the worker VFS. It fills the `loader.internal`
365
- * seam Cordis uses for every entry import, and backs the `node:module`
366
- * `createRequire` proxy that `typert-loader`, `client-modules`, and the plugin
367
- * package inventory resolve package metadata through.
489
+ * CommonJS module loader over the worker VFS. It backs the Node internal ESM
490
+ * loader that `node-addon-require-builtin` answers, which Cordis's Loader uses
491
+ * for every entry import, and the `node:module` `createRequire` proxy that
492
+ * `typert-loader`, `client-modules`, and the plugin package inventory resolve
493
+ * package metadata through.
368
494
  *
369
495
  * Resolution is a narrowed Node `require` algorithm: `exports` walk with a
370
496
  * fixed condition order, extension probing, and one cache keyed by resolved
@@ -400,9 +526,11 @@ var WorkerModuleLoader = class {
400
526
  modules = /* @__PURE__ */ new Map();
401
527
  manifests = /* @__PURE__ */ new Map();
402
528
  stack = [];
529
+ cascaded;
403
530
  /**
404
- * The Cordis module seam. `parentURL` positions relative specifiers;
405
- * import attributes are ignored, as the client implementation does.
531
+ * Resolution and loading over this mount, in Node's v1 argument order. The cascaded ESM loader
532
+ * ({@link cascadedLoader}) and the packer call it; `parentURL` positions relative specifiers and import attributes
533
+ * are ignored.
406
534
  */
407
535
  internal;
408
536
  constructor(options) {
@@ -434,9 +562,22 @@ var WorkerModuleLoader = class {
434
562
  resolveSync: resolveInternal
435
563
  };
436
564
  }
437
- fail(detail) {
565
+ /**
566
+ * The `internal/modules/esm/loader` cascaded loader over this mount, created on first use.
567
+ * @returns This loader's one cascaded loader.
568
+ */
569
+ cascadedLoader() {
570
+ return this.cascaded ??= createCascadedLoader(this.internal);
571
+ }
572
+ /**
573
+ * Throw this loader's diagnostic.
574
+ * @param detail - What failed.
575
+ * @param code - Node error code for a resolution failure Node also reports by code.
576
+ */
577
+ fail(detail, code) {
438
578
  const chain = this.stack.length === 0 ? "" : ` (importer chain: ${this.stack.join(" -> ")})`;
439
- throw new Error(`webworker modules: ${detail}${chain}`);
579
+ const error = /* @__PURE__ */ new Error(`webworker modules: ${detail}${chain}`);
580
+ throw code === void 0 ? error : Object.assign(error, { code });
440
581
  }
441
582
  /** @returns Directory a base path or URL resolves specifiers from. */
442
583
  baseDirectoryOf(base) {
@@ -517,7 +658,7 @@ var WorkerModuleLoader = class {
517
658
  }
518
659
  return this.probe(join(path, "index"), specifier);
519
660
  }
520
- return this.fail(`cannot resolve "${specifier}": no file at ${candidates.join(", ")}`);
661
+ return this.fail(`cannot resolve "${specifier}": no file at ${candidates.join(", ")}`, "MODULE_NOT_FOUND");
521
662
  }
522
663
  /** @returns The Worker-provided implementation of a static specifier. */
523
664
  staticModule(specifier) {
@@ -556,12 +697,12 @@ var WorkerModuleLoader = class {
556
697
  const packageName = specifier.startsWith("@") ? segments.slice(0, 2).join("/") : segments[0] ?? specifier;
557
698
  const rest = specifier.slice(packageName.length).replace(/^\//, "");
558
699
  const packageDirectory = join(this.root, "node_modules", packageName);
559
- if (!this.vfs.existsSync(join(packageDirectory, "package.json"))) return this.fail(`cannot resolve "${specifier}": ${packageDirectory}/package.json is not in the image`);
700
+ if (!this.vfs.existsSync(join(packageDirectory, "package.json"))) return this.fail(`cannot resolve "${specifier}": ${packageDirectory}/package.json is not in the image`, "MODULE_NOT_FOUND");
560
701
  const manifest = this.manifestOf(packageDirectory);
561
702
  const subpath = rest === "" ? "." : `./${rest}`;
562
703
  if (manifest.exports !== void 0) {
563
704
  const target = this.selectExport(manifest.exports, subpath, packageName);
564
- if (target === void 0) return this.fail(`"${packageName}" does not export "${subpath}" under conditions [${[...this.conditions].join(", ")}]`);
705
+ if (target === void 0) return this.fail(`"${packageName}" does not export "${subpath}" under conditions [${[...this.conditions].join(", ")}]`, "ERR_PACKAGE_PATH_NOT_EXPORTED");
565
706
  return {
566
707
  kind: "file",
567
708
  path: this.probe(join(packageDirectory, target), specifier)
@@ -751,6 +892,7 @@ const MODULE_PROXIES = {
751
892
  "got": "./node/external_packages/got.ts",
752
893
  "@vscode/ripgrep": "./node/external_packages/ripgrep.ts",
753
894
  "@earendil-works/pi-ai": "./node/external_packages/pi-ai.ts",
895
+ "node-addon-require-builtin": "./node/external_packages/node-addon-require-builtin.ts",
754
896
  "ws": "./node/external_packages/ws.ts"
755
897
  };
756
898
  /** pi-ai subpaths (`/providers/all`, `/api/*.lazy`) share the one structural stub. */
@@ -773,6 +915,7 @@ const REPLACED_EXTERNAL_PACKAGES = [
773
915
  "node-pty",
774
916
  "execa",
775
917
  "got",
918
+ "node-addon-require-builtin",
776
919
  "sharp",
777
920
  "ws"
778
921
  ];
@@ -1991,19 +2134,21 @@ function signalProcess(pid, signal) {
1991
2134
  /**
1992
2135
  * The `process` global the worker needs before any VFS module runs. Cordis
1993
2136
  * reads `process.env` and `process.versions.node` while the Loader is
1994
- * constructed, and `cordis.yml` keeps its `!!js process.*` expressions, so the
1995
- * configuration bytes stay identical to the Node deployment. Third-party Node
2137
+ * constructed, and bundle patch rows keep their `!!js process.*` expressions, so
2138
+ * the configuration bytes stay identical to the Node deployment. Third-party Node
1996
2139
  * packages use the presence of `process.title` to avoid browser-only globals.
1997
2140
  * @module @deepseek-ai/dsh-experimental-webworker-runtime/src/node/globals/process
1998
2141
  */
1999
2142
  /** Virtual executable identity; the worker has no Node binary behind it. */
2000
2143
  const EXEC_PATH = "/dsh/bin/node";
2144
+ /** Node release whose internal-module members `node-addon-require-builtin` follows in the Worker. */
2145
+ const NODE_VERSION = "24.18.0";
2001
2146
  /**
2002
2147
  * Publish `globalThis.process`.
2003
2148
  *
2004
- * `versions.node` is `0.0.0` on purpose: it makes Cordis's
2005
- * `ModuleLoader.fromInternal()` return undefined instead of reaching for Node
2006
- * internals, which is what lets the worker install its own module seam.
2149
+ * `versions.node` names the Node release the Worker's `node-addon-require-builtin` internals follow, so Cordis's
2150
+ * `ModuleLoader.fromInternal()` reaches them the way it reaches Node's; `execArgv` stays empty, without
2151
+ * `--expose-internals`.
2007
2152
  * @param options - Root, environment, and argument vector.
2008
2153
  * @returns The published object, for the module proxy table.
2009
2154
  */
@@ -2022,8 +2167,8 @@ function installProcessGlobal(options) {
2022
2167
  platform: "linux",
2023
2168
  arch: "x64",
2024
2169
  pid: 1,
2025
- version: "v0.0.0",
2026
- versions: { node: "0.0.0" },
2170
+ version: `v${NODE_VERSION}`,
2171
+ versions: { node: NODE_VERSION },
2027
2172
  cwd: () => options.cwd,
2028
2173
  getBuiltinModule: (id) => {
2029
2174
  let resolution;
@@ -2417,6 +2562,8 @@ function resize(bytes, length) {
2417
2562
  */
2418
2563
  var MemoryVfs = class {
2419
2564
  files = /* @__PURE__ */ new Map();
2565
+ /** Files a lazy archive supplies on their first asynchronous read. */
2566
+ deferred = /* @__PURE__ */ new Map();
2420
2567
  directories = new Set(["/"]);
2421
2568
  /** Directory permission bits; absence means {@link DEFAULT_DIRECTORY_MODE}. */
2422
2569
  directoryModes = /* @__PURE__ */ new Map();
@@ -2465,7 +2612,10 @@ var MemoryVfs = class {
2465
2612
  }
2466
2613
  /** Promise face mirroring `node:fs/promises` for the methods the roster uses. */
2467
2614
  promises = {
2468
- readFile: async (path, options) => this.readFileSync(path, options),
2615
+ readFile: async (path, options) => {
2616
+ await this.whenReadable(path);
2617
+ return this.readFileSync(path, options);
2618
+ },
2469
2619
  writeFile: async (path, data, options) => {
2470
2620
  this.writeFileSync(path, data, options);
2471
2621
  },
@@ -2497,11 +2647,13 @@ var MemoryVfs = class {
2497
2647
  this.chmodSync(path, mode);
2498
2648
  },
2499
2649
  opendir: async (path) => this.opendir(path),
2500
- open: async (path, flags, mode) => this.open(path, flags, mode),
2650
+ open: async (path, flags, mode) => {
2651
+ await this.whenReadable(path);
2652
+ return this.open(path, flags, mode);
2653
+ },
2501
2654
  /** Resolves for any existing path: the VFS grants read and write to everything it holds. */
2502
2655
  access: async (path) => {
2503
- const target = normalize(resolve(path));
2504
- if (!this.files.has(target) && !this.directories.has(target)) fail("ENOENT", "access", target);
2656
+ if (!this.existsSync(path)) fail("ENOENT", "access", normalize(resolve(path)));
2505
2657
  }
2506
2658
  };
2507
2659
  /** @returns Absolute path with no trailing separator. */
@@ -2510,6 +2662,101 @@ var MemoryVfs = class {
2510
2662
  return absolute.length > 1 && absolute.endsWith("/") ? absolute.slice(0, -1) : absolute;
2511
2663
  }
2512
2664
  /**
2665
+ * Register files whose bytes arrive with one archive on the first
2666
+ * asynchronous read of any of them. Their parent directories are created now.
2667
+ * @param archive - Archive name, mount root, registered files, and fetch.
2668
+ * @throws When a registered path already holds a file, directory, or another registration.
2669
+ */
2670
+ deferArchive(archive) {
2671
+ const root = this.key(archive.root);
2672
+ const files = /* @__PURE__ */ new Map();
2673
+ for (const file of archive.files) {
2674
+ const target = this.key(join(root, file.path));
2675
+ if (this.files.has(target) || this.directories.has(target) || this.deferred.has(target) || files.has(target)) throw new Error(`webworker vfs: ${target} is already registered, so lazy archive ${archive.name} cannot supply it`);
2676
+ files.set(target, file.size);
2677
+ }
2678
+ const state = {
2679
+ name: archive.name,
2680
+ root,
2681
+ load: () => archive.load(),
2682
+ files,
2683
+ fetching: void 0
2684
+ };
2685
+ const mtimeMs = Date.now();
2686
+ for (const [target, size] of files) {
2687
+ this.seedDirectory(dirname(target));
2688
+ this.deferred.set(target, {
2689
+ size,
2690
+ mode: DEFAULT_FILE_MODE,
2691
+ mtimeMs,
2692
+ archive: state
2693
+ });
2694
+ this.touchDirectory(dirname(target));
2695
+ }
2696
+ }
2697
+ /**
2698
+ * Fetch the lazy archive that supplies a registered path. Concurrent readers
2699
+ * of one archive share one fetch; a failed fetch leaves its files registered,
2700
+ * so the next read fetches again.
2701
+ * @param path - Path about to be read asynchronously.
2702
+ * @returns A promise that settles once the archive arrived, or undefined when the path needs no fetch.
2703
+ */
2704
+ whenReadable(path) {
2705
+ const file = this.deferred.get(this.key(path));
2706
+ if (file === void 0) return void 0;
2707
+ file.archive.fetching ??= this.fetchArchive(file.archive);
2708
+ return file.archive.fetching;
2709
+ }
2710
+ /** Fetch, validate against the registration, and mount one archive's files. */
2711
+ async fetchArchive(state) {
2712
+ try {
2713
+ let archive;
2714
+ try {
2715
+ archive = await state.load();
2716
+ } catch (error) {
2717
+ throw new Error(`webworker vfs: lazy archive ${state.name} could not be fetched`, { cause: error });
2718
+ }
2719
+ const entries = /* @__PURE__ */ new Map();
2720
+ for (const entry of parseTar(archive)) {
2721
+ if (entry.directory) continue;
2722
+ const relativeName = entry.name.startsWith("./") ? entry.name.slice(2) : entry.name;
2723
+ const target = this.key(join(state.root, relativeName));
2724
+ const size = state.files.get(target);
2725
+ if (size === void 0) throw new Error(`webworker vfs: lazy archive ${state.name} carries unregistered entry "${relativeName}"`);
2726
+ if (entry.bytes.length !== size) throw new Error(`webworker vfs: lazy archive ${state.name} holds ${String(entry.bytes.length)} bytes for ${target}, registered as ${String(size)}`);
2727
+ entries.set(target, entry.bytes);
2728
+ }
2729
+ for (const target of state.files.keys()) if (!entries.has(target)) throw new Error(`webworker vfs: lazy archive ${state.name} lacks registered file ${target}`);
2730
+ for (const [target, bytes] of entries) {
2731
+ const file = this.deferred.get(target);
2732
+ if (file?.archive !== state) continue;
2733
+ this.deferred.delete(target);
2734
+ const identity = this.identities.get(target);
2735
+ this.identities.delete(target);
2736
+ this.setFile(target, {
2737
+ bytes,
2738
+ mtimeMs: file.mtimeMs,
2739
+ mode: file.mode,
2740
+ paths: void 0,
2741
+ ...identity === void 0 ? {} : { identity }
2742
+ });
2743
+ }
2744
+ } catch (error) {
2745
+ state.fetching = void 0;
2746
+ throw error;
2747
+ }
2748
+ }
2749
+ /** Refuse a synchronous operation on a registered file whose bytes have not arrived. */
2750
+ requireArrived(syscall, target) {
2751
+ const file = this.deferred.get(target);
2752
+ if (file === void 0) return;
2753
+ const error = /* @__PURE__ */ new Error(`ERR_DSH_VFS_DEFERRED: ${syscall} cannot wait for '${target}', which lazy archive ${file.archive.name} supplies on its first asynchronous read`);
2754
+ error.code = "ERR_DSH_VFS_DEFERRED";
2755
+ error.path = target;
2756
+ error.syscall = syscall;
2757
+ throw error;
2758
+ }
2759
+ /**
2513
2760
  * Read a file.
2514
2761
  * @param path - File path.
2515
2762
  * @param options - `'utf8'` or `{encoding}` for text; omitted for bytes.
@@ -2517,6 +2764,7 @@ var MemoryVfs = class {
2517
2764
  */
2518
2765
  readFileSync(path, options) {
2519
2766
  const target = this.key(path);
2767
+ this.requireArrived("open", target);
2520
2768
  const node = this.files.get(target);
2521
2769
  if (node === void 0) {
2522
2770
  if (this.directories.has(target)) fail("EISDIR", "read", target);
@@ -2531,7 +2779,7 @@ var MemoryVfs = class {
2531
2779
  */
2532
2780
  existsSync(path) {
2533
2781
  const target = this.key(path);
2534
- return this.files.has(target) || this.directories.has(target);
2782
+ return this.files.has(target) || this.directories.has(target) || this.deferred.has(target);
2535
2783
  }
2536
2784
  /**
2537
2785
  * Stat a path.
@@ -2542,11 +2790,17 @@ var MemoryVfs = class {
2542
2790
  statSync(path, options) {
2543
2791
  const target = this.key(path);
2544
2792
  const node = this.files.get(target);
2793
+ const deferred = this.deferred.get(target);
2545
2794
  const [size, mtimeMs, directory, mode] = node !== void 0 ? [
2546
2795
  node.bytes.length,
2547
2796
  node.mtimeMs,
2548
2797
  false,
2549
2798
  node.mode
2799
+ ] : deferred !== void 0 ? [
2800
+ deferred.size,
2801
+ deferred.mtimeMs,
2802
+ false,
2803
+ deferred.mode
2550
2804
  ] : this.directories.has(target) ? [
2551
2805
  0,
2552
2806
  this.directoryMtimes.get(target) ?? 0,
@@ -2716,12 +2970,16 @@ var MemoryVfs = class {
2716
2970
  readdirSync(path, options) {
2717
2971
  const target = this.key(path);
2718
2972
  if (!this.directories.has(target)) {
2719
- if (this.files.has(target)) fail("ENOTDIR", "scandir", target);
2973
+ if (this.files.has(target) || this.deferred.has(target)) fail("ENOTDIR", "scandir", target);
2720
2974
  fail("ENOENT", "scandir", target);
2721
2975
  }
2722
2976
  const prefix = target === "/" ? "/" : `${target}/`;
2723
2977
  const names = /* @__PURE__ */ new Set();
2724
- for (const candidate of [...this.files.keys(), ...this.directories]) {
2978
+ for (const candidate of [
2979
+ ...this.files.keys(),
2980
+ ...this.deferred.keys(),
2981
+ ...this.directories
2982
+ ]) {
2725
2983
  if (!candidate.startsWith(prefix) || candidate === target) continue;
2726
2984
  const rest = candidate.slice(prefix.length);
2727
2985
  if (rest === "") continue;
@@ -2761,7 +3019,7 @@ var MemoryVfs = class {
2761
3019
  */
2762
3020
  mkdirSync(path, options) {
2763
3021
  const target = this.key(path);
2764
- if (this.files.has(target)) fail("EEXIST", "mkdir", target);
3022
+ if (this.files.has(target) || this.deferred.has(target)) fail("EEXIST", "mkdir", target);
2765
3023
  if (this.directories.has(target)) {
2766
3024
  if (options?.recursive === true) return void 0;
2767
3025
  fail("EEXIST", "mkdir", target);
@@ -2791,6 +3049,7 @@ var MemoryVfs = class {
2791
3049
  */
2792
3050
  writeFileSync(path, data, options) {
2793
3051
  const target = this.key(path);
3052
+ this.requireArrived("open", target);
2794
3053
  if (this.directories.has(target)) fail("EISDIR", "open", target);
2795
3054
  if (!this.directories.has(dirname(target))) fail("ENOENT", "open", target);
2796
3055
  const flag = options?.flag ?? "w";
@@ -2921,6 +3180,7 @@ var MemoryVfs = class {
2921
3180
  */
2922
3181
  openFileSync(path, flags = "r", mode) {
2923
3182
  const target = this.key(path);
3183
+ this.requireArrived("open", target);
2924
3184
  const access = openMode(flags);
2925
3185
  const existing = this.files.get(target);
2926
3186
  if (this.directories.has(target)) fail("EISDIR", "open", target);
@@ -2982,6 +3242,7 @@ var MemoryVfs = class {
2982
3242
  */
2983
3243
  appendFileSync(path, data) {
2984
3244
  const target = this.key(path);
3245
+ this.requireArrived("open", target);
2985
3246
  const existing = this.files.get(target);
2986
3247
  const addition = typeof data === "string" ? encoder.encode(data) : data;
2987
3248
  if (existing === void 0) {
@@ -2998,6 +3259,8 @@ var MemoryVfs = class {
2998
3259
  renameSync(from, to) {
2999
3260
  const source = this.key(from);
3000
3261
  const destination = this.key(to);
3262
+ this.requireArrived("rename", source);
3263
+ this.requireArrived("rename", destination);
3001
3264
  if (source === destination) return;
3002
3265
  const node = this.files.get(source);
3003
3266
  if (node !== void 0) {
@@ -3026,13 +3289,14 @@ var MemoryVfs = class {
3026
3289
  if (!this.directories.has(source)) fail("ENOENT", "rename", source);
3027
3290
  if (this.files.has(destination)) fail("ENOTDIR", "rename", destination);
3028
3291
  if (!this.directories.has(dirname(destination))) fail("ENOENT", "rename", destination);
3292
+ const prefix = `${source}/`;
3293
+ for (const registered of this.deferred.keys()) if (registered.startsWith(prefix)) this.requireArrived("rename", registered);
3029
3294
  if (this.directories.has(destination)) {
3030
3295
  if (this.readdirSync(destination).length > 0) fail("ENOTEMPTY", "rename", destination);
3031
3296
  this.directories.delete(destination);
3032
3297
  this.directoryModes.delete(destination);
3033
3298
  this.directoryMtimes.delete(destination);
3034
3299
  }
3035
- const prefix = `${source}/`;
3036
3300
  const movedFiles = [];
3037
3301
  for (const [candidate, value] of [...this.files]) {
3038
3302
  if (!candidate.startsWith(prefix)) continue;
@@ -3094,9 +3358,10 @@ var MemoryVfs = class {
3094
3358
  linkSync(existing, next) {
3095
3359
  const source = this.key(existing);
3096
3360
  const target = this.key(next);
3361
+ this.requireArrived("link", source);
3097
3362
  const node = this.files.get(source);
3098
3363
  if (node === void 0) fail("ENOENT", "link", source);
3099
- if (this.files.has(target) || this.directories.has(target)) fail("EEXIST", "link", target);
3364
+ if (this.existsSync(target)) fail("EEXIST", "link", target);
3100
3365
  if (!this.directories.has(dirname(target))) fail("ENOENT", "link", target);
3101
3366
  this.setFile(target, node);
3102
3367
  this.touchDirectory(dirname(target));
@@ -3115,6 +3380,7 @@ var MemoryVfs = class {
3115
3380
  */
3116
3381
  truncateSync(path, length = 0) {
3117
3382
  const target = this.key(path);
3383
+ this.requireArrived("truncate", target);
3118
3384
  const node = this.files.get(target);
3119
3385
  if (node === void 0) fail("ENOENT", "truncate", target);
3120
3386
  this.truncateFile(node, length);
@@ -3126,6 +3392,7 @@ var MemoryVfs = class {
3126
3392
  */
3127
3393
  chmodSync(path, mode) {
3128
3394
  const target = this.key(path);
3395
+ this.requireArrived("chmod", target);
3129
3396
  const node = this.files.get(target);
3130
3397
  if (node !== void 0) {
3131
3398
  this.chmodFile(node, mode);
@@ -3149,7 +3416,7 @@ var MemoryVfs = class {
3149
3416
  */
3150
3417
  unlinkSync(path) {
3151
3418
  const target = this.key(path);
3152
- if (this.deleteFile(target) === void 0) fail("ENOENT", "unlink", target);
3419
+ if (this.deleteFile(target) === void 0 && !this.deferred.delete(target)) fail("ENOENT", "unlink", target);
3153
3420
  this.forgetIdentity(target);
3154
3421
  this.touchDirectory(dirname(target));
3155
3422
  this.publish({
@@ -3164,7 +3431,7 @@ var MemoryVfs = class {
3164
3431
  */
3165
3432
  rmSync(path, options) {
3166
3433
  const target = this.key(path);
3167
- if (this.deleteFile(target) !== void 0) {
3434
+ if (this.deleteFile(target) !== void 0 || this.deferred.delete(target)) {
3168
3435
  this.forgetIdentity(target);
3169
3436
  this.touchDirectory(dirname(target));
3170
3437
  this.publish({
@@ -3177,6 +3444,7 @@ var MemoryVfs = class {
3177
3444
  if (options?.recursive !== true) fail("ERR_FS_EISDIR", "rm", target);
3178
3445
  const prefix = `${target}/`;
3179
3446
  for (const candidate of [...this.files.keys()]) if (candidate.startsWith(prefix)) this.deleteFile(candidate);
3447
+ for (const candidate of [...this.deferred.keys()]) if (candidate.startsWith(prefix)) this.deferred.delete(candidate);
3180
3448
  for (const candidate of [...this.directories]) {
3181
3449
  if (!candidate.startsWith(prefix)) continue;
3182
3450
  this.directories.delete(candidate);
@@ -3216,6 +3484,7 @@ var MemoryVfs = class {
3216
3484
  seed(path, data, options = {}) {
3217
3485
  const target = this.key(path);
3218
3486
  this.seedDirectory(dirname(target));
3487
+ this.deferred.delete(target);
3219
3488
  this.setFile(target, {
3220
3489
  bytes: typeof data === "string" ? encoder.encode(data) : data,
3221
3490
  mtimeMs: options.mtimeMs ?? this.touch(target),
@@ -3234,7 +3503,7 @@ var MemoryVfs = class {
3234
3503
  if (!this.directories.has(target)) {
3235
3504
  const parent = dirname(target);
3236
3505
  if (parent !== target) this.seedDirectory(parent);
3237
- if (this.files.has(target)) fail("EEXIST", "mkdir", target);
3506
+ if (this.files.has(target) || this.deferred.has(target)) fail("EEXIST", "mkdir", target);
3238
3507
  this.directories.add(target);
3239
3508
  this.directoryMtimes.set(target, options.mtimeMs ?? Date.now());
3240
3509
  this.touchDirectory(parent);
@@ -3259,7 +3528,7 @@ var MemoryVfs = class {
3259
3528
  /**
3260
3529
  * Mount a tar image produced by the build-time collector.
3261
3530
  *
3262
- * Entry names are relative to `root` (`node_modules/...`, `config/cordis.yml`);
3531
+ * Entry names are relative to `root` (`node_modules/...`, `config/vfs-manifest.json`);
3263
3532
  * an absolute entry name is a collector defect and fails loud. File contents
3264
3533
  * stay views into `image` — nothing is copied at mount time.
3265
3534
  * @param image - The ustar archive, as `inflateImage` produces it from the fetched image.
@@ -3327,6 +3596,72 @@ function requireActiveVfs() {
3327
3596
  if (active === void 0) throw new Error("webworker vfs: no filesystem is mounted; the worker entry must call setActiveVfs before any node:fs access");
3328
3597
  return active;
3329
3598
  }
3599
+ //#endregion
3600
+ //#region src/image-manifest.ts
3601
+ /**
3602
+ * The base image's manifest: what the packer records about one image and what
3603
+ * the worker host requires before it wraps a module or boots the tree. Both
3604
+ * sides use these types; the worker validates the fetched file with
3605
+ * {@link parseImageManifest}.
3606
+ */
3607
+ function recordOf$1(value) {
3608
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
3609
+ }
3610
+ /** A relative path made of non-empty segments without traversal. */
3611
+ function isRelativePath(value) {
3612
+ return typeof value === "string" && value !== "" && !value.startsWith("/") && value.split("/").every((segment) => segment !== "" && segment !== "." && segment !== "..");
3613
+ }
3614
+ function isCount(value) {
3615
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
3616
+ }
3617
+ /**
3618
+ * Validate a fetched base-image manifest.
3619
+ * @param value - Parsed manifest JSON.
3620
+ * @param path - Manifest path inside the image, named in refusals.
3621
+ * @returns The detached manifest.
3622
+ * @throws When the manifest is malformed, or its bodies were lowered against another wrapper contract.
3623
+ */
3624
+ function parseImageManifest(value, path) {
3625
+ const record = recordOf$1(value);
3626
+ if (record === void 0) throw new Error(`webworker host: ${path} does not hold an object`);
3627
+ if (record.lowered !== "dsh-worker-transform/1") throw new Error(`webworker host: image was lowered by ${String(record.lowered)}, this build runs ${LOWERING_VERSION}; rebuild the image`);
3628
+ const { root, profile, installation, javascriptEntries, visitedEntries, rewrittenEntries, lazy } = record;
3629
+ if (typeof root !== "string" || typeof profile !== "string" || profile === "" || installation !== void 0 && (typeof installation !== "string" || installation === "") || !isCount(javascriptEntries) || !isCount(visitedEntries) || !isCount(rewrittenEntries) || !Array.isArray(lazy)) throw new Error(`webworker host: ${path} lacks a valid root, profile, installation, entry count, or lazy archive list`);
3630
+ const archives = [];
3631
+ const archiveNames = /* @__PURE__ */ new Set();
3632
+ const filePaths = /* @__PURE__ */ new Set();
3633
+ for (const entry of lazy) {
3634
+ const archive = recordOf$1(entry);
3635
+ const name = archive?.archive;
3636
+ const files = archive?.files;
3637
+ if (!isRelativePath(name) || archiveNames.has(name) || !Array.isArray(files) || files.length === 0) throw new Error(`webworker host: ${path} lists an invalid or repeated lazy archive`);
3638
+ archiveNames.add(name);
3639
+ const parsed = [];
3640
+ for (const candidate of files) {
3641
+ const file = recordOf$1(candidate);
3642
+ if (file === void 0 || !isRelativePath(file.path) || !isCount(file.size) || filePaths.has(file.path)) throw new Error(`webworker host: ${path} lists an invalid or repeated file in lazy archive ${name}`);
3643
+ filePaths.add(file.path);
3644
+ parsed.push({
3645
+ path: file.path,
3646
+ size: file.size
3647
+ });
3648
+ }
3649
+ archives.push({
3650
+ archive: name,
3651
+ files: parsed
3652
+ });
3653
+ }
3654
+ return {
3655
+ root,
3656
+ profile,
3657
+ ...installation === void 0 ? {} : { installation },
3658
+ lowered: LOWERING_VERSION,
3659
+ javascriptEntries,
3660
+ visitedEntries,
3661
+ rewrittenEntries,
3662
+ lazy: archives
3663
+ };
3664
+ }
3330
3665
  function requireGlobalPort(channel) {
3331
3666
  if (channel !== void 0) return channel;
3332
3667
  const post = globalThis.postMessage;
@@ -3349,7 +3684,6 @@ async function readImage(image) {
3349
3684
  */
3350
3685
  function createWorkerHost(options) {
3351
3686
  const root = options.root ?? "/dsh";
3352
- const configPath = options.configPath ?? join(root, "config/cordis.yml");
3353
3687
  const port = options.port ?? 3080;
3354
3688
  const tunnel = new TunnelServer({
3355
3689
  port: requireGlobalPort(options.channel),
@@ -3363,7 +3697,7 @@ function createWorkerHost(options) {
3363
3697
  const start = async () => {
3364
3698
  try {
3365
3699
  const home = join(root, IMAGE_HOME_DIRECTORY);
3366
- installProcessGlobal({
3700
+ const processShim = installProcessGlobal({
3367
3701
  cwd: root,
3368
3702
  env: {
3369
3703
  DSH_HOME: home,
@@ -3377,7 +3711,8 @@ function createWorkerHost(options) {
3377
3711
  for (const directory of IMAGE_EMPTY_DIRECTORIES) mounted.seedDirectory(join(root, directory.replace(/\/$/, "")));
3378
3712
  setActiveVfs(mounted);
3379
3713
  vfs = mounted;
3380
- requireLoweredImage(mounted, options.manifestPath ?? join(root, "config/vfs-manifest.json"));
3714
+ const manifest = readImageManifest(mounted, options.manifestPath ?? join(root, "config/vfs-manifest.json"));
3715
+ deferLazyArchives(mounted, manifest, root, options.image);
3381
3716
  const staticModules = { ...options.staticModules };
3382
3717
  for (const key of ["node:process", "process"]) staticModules[key] ??= () => globalThis.process;
3383
3718
  const loader = new WorkerModuleLoader({
@@ -3389,16 +3724,18 @@ function createWorkerHost(options) {
3389
3724
  });
3390
3725
  setActiveModuleLoader(loader);
3391
3726
  modules = loader;
3392
- const require = loader.requireFrom(dirname(configPath));
3727
+ const require = loader.requireFrom(root);
3393
3728
  const appBoot = require("@deepseek-ai/dsh-app-boot");
3729
+ const { DSH_LAUNCH_ENVIRONMENT_KEY } = require("@deepseek-ai/dsh-launch-environment");
3394
3730
  const cmdline = require("@deepseek-ai/dsh-cmdline");
3395
3731
  const started = Promise.withResolvers();
3396
- const { patches, profile } = bootPatches(loader, mounted, configPath, root);
3732
+ const { patches, profile, environment } = prepareProfile(appBoot, mounted, manifest, root, processShim.env);
3397
3733
  const profileConfig = join(profile.dir, "cordis.yml");
3398
- const ctx = await appBoot.boot("dsh-webworker", profileConfig, patches, (hostCtx) => {
3734
+ const ctx = await appBoot.boot(BIN_NAME, profileConfig, patches, async (hostCtx) => {
3399
3735
  hostCtx.provide("profileContext", profile);
3400
- hostCtx.loader.internal = loader.internal;
3736
+ hostCtx.provide(DSH_LAUNCH_ENVIRONMENT_KEY, environment);
3401
3737
  installLogSink(hostCtx, require);
3738
+ await hostCtx.plugin(appBoot.PluginPackages, {});
3402
3739
  cmdline.provideCmdline(hostCtx, {
3403
3740
  args: [...options.cmdlineArgs ?? [
3404
3741
  "--host",
@@ -3428,11 +3765,12 @@ function createWorkerHost(options) {
3428
3765
  if (typertGateway === void 0) throw new Error("webworker host: the tree activated without a typertGateway service");
3429
3766
  const handler = connection.createSharedFetchHandler("/api");
3430
3767
  const usage = loader.usage();
3431
- console.info(`webworker host: tree active (modules=${String(usage.modules)}, data overlays=${String(overlays.length)}, direct lane=connection.createSharedFetchHandler, als causality=${options.alsCausality === void 0 ? "inert" : "snapshot/restore"}, image lowering=${LOWERING_VERSION})`);
3768
+ console.info(`webworker host: tree active (modules=${String(usage.modules)}, data overlays=${String(overlays.length)}, lazy archives=${String(manifest.lazy.length)}, direct lane=connection.createSharedFetchHandler, als causality=${options.alsCausality === void 0 ? "inert" : "snapshot/restore"}, image lowering=${LOWERING_VERSION})`);
3769
+ const peer = connection.browserPeer;
3432
3770
  tunnel.serve({
3433
- directFetch: (request) => handler.fetch(request),
3771
+ directFetch: (request) => handler.fetch(request, peer),
3434
3772
  bootPayload: () => readBootPayload(ctx),
3435
- openStream: (endpoint, payload, uplink, signal) => typertGateway.wireStream.open(endpoint, payload, uplink, void 0, signal),
3773
+ openStream: (endpoint, payload, uplink, signal) => typertGateway.wireStream.open(endpoint, payload, uplink, peer, signal),
3436
3774
  streamFailure: typertGateway.wireStream.failure
3437
3775
  });
3438
3776
  started.resolve();
@@ -3487,76 +3825,84 @@ function installLogSink(ctx, require) {
3487
3825
  };
3488
3826
  ctx.logger.exporter(exporter);
3489
3827
  }
3828
+ /** Diagnostic prefix the launcher passes to app-boot; the Worker boots the same profile code. */
3829
+ const BIN_NAME = "dsh";
3490
3830
  /**
3491
- * Require the mounted image to carry bodies this build can wrap.
3831
+ * Read and validate the mounted image's manifest.
3492
3832
  *
3493
3833
  * The manifest the packer writes is the single source of truth: the worker holds
3494
3834
  * no transform, so an image that was never lowered — or was lowered against
3495
3835
  * different wrapper semantics — cannot be recovered at load and must be rebuilt.
3496
3836
  * @param vfs - Mounted filesystem.
3497
3837
  * @param path - Manifest path inside the image.
3498
- * @throws When the manifest is missing, unreadable, or names another contract.
3838
+ * @returns The validated manifest.
3839
+ * @throws When the manifest is missing, malformed, or names another contract.
3499
3840
  */
3500
- function requireLoweredImage(vfs, path) {
3841
+ function readImageManifest(vfs, path) {
3501
3842
  if (!vfs.existsSync(path)) throw new Error(`webworker host: ${path} is missing, so the image records no lowering; rebuild the image`);
3502
- const parsed = JSON.parse(vfs.readFileSync(path, "utf8"));
3503
- if (typeof parsed !== "object" || parsed === null) throw new Error(`webworker host: ${path} does not hold an object`);
3504
- const lowered = parsed.lowered;
3505
- if (lowered !== "dsh-worker-transform/1") throw new Error(`webworker host: image was lowered by ${String(lowered)}, this build runs ${LOWERING_VERSION}; rebuild the image`);
3506
- }
3507
- /**
3508
- * Prepare an editable VFS profile from the packed rows and deployment overlays.
3509
- * ConfigEditor writes overrides beside the insertion rows; reconciliation starts
3510
- * at the empty profile root and retains the Worker deployment overlays.
3511
- * @param loader - Module loader, for the image's YAML reader.
3512
- * @param vfs - Filesystem holding the composed configuration.
3513
- * @param configPath - Composed configuration path.
3514
- * @param root - Virtual root.
3515
- * @returns Profile locations and effective boot patches.
3516
- */
3517
- function bootPatches(loader, vfs, configPath, root) {
3518
- const text = vfs.readFileSync(configPath, "utf8");
3519
- const include = loader.load(loader.resolve("@deepseek-ai/cordis-plugin-include", root));
3520
- const yaml = loader.load(loader.resolve("js-yaml", root));
3521
- let rows;
3522
- if (configPath.endsWith(".json")) rows = JSON.parse(text);
3523
- else rows = yaml.load(text, { schema: include.entryListSchema });
3524
- const find = (entries, id) => {
3525
- if (!Array.isArray(entries)) return void 0;
3526
- for (const entry of entries) {
3527
- if (entry.id === id) return entry;
3528
- const nested = find(entry.config, id);
3529
- if (nested !== void 0) return nested;
3530
- }
3531
- };
3532
- const configOf = (row) => typeof row.config === "object" && row.config !== null && !Array.isArray(row.config) ? row.config : {};
3533
- const patches = [];
3534
- const jsonl = find(rows, "session-persistence-jsonl");
3535
- if (jsonl !== void 0) patches.push({
3536
- id: "session-persistence-jsonl",
3537
- config: {
3538
- ...configOf(jsonl),
3539
- compression: "none"
3540
- }
3541
- });
3542
- const dir = join(root, IMAGE_HOME_DIRECTORY, "profiles/preview");
3843
+ return parseImageManifest(JSON.parse(vfs.readFileSync(path, "utf8")), path);
3844
+ }
3845
+ /**
3846
+ * Register the manifest's lazy archives; each is fetched beside the image on
3847
+ * the first asynchronous read of a file it carries.
3848
+ * @param vfs - Mounted filesystem.
3849
+ * @param manifest - Validated image manifest.
3850
+ * @param root - Virtual root the archives' entries mount under.
3851
+ * @param image - The image bytes or URL; archive names resolve against a URL.
3852
+ * @throws When the image registers lazy archives but arrived as bytes, leaving them unlocatable.
3853
+ */
3854
+ function deferLazyArchives(vfs, manifest, root, image) {
3855
+ if (manifest.lazy.length === 0) return;
3856
+ if (typeof image !== "string") throw new Error("webworker host: the image registers lazy archives, which only an image fetched from a URL can locate");
3857
+ const base = new URL(image, globalThis.location?.href);
3858
+ for (const archive of manifest.lazy) {
3859
+ const url = new URL(archive.archive, base).href;
3860
+ vfs.deferArchive({
3861
+ name: archive.archive,
3862
+ root,
3863
+ files: archive.files,
3864
+ load: () => readImage(url)
3865
+ });
3866
+ }
3867
+ }
3868
+ /**
3869
+ * Prepare the profile the served launcher would boot: the image's app-boot
3870
+ * initializes the manifest's shipped profile template under the Worker home
3871
+ * and composes its bundle layers, user layer, and home layer. The profile
3872
+ * context carries the launcher's facts with no command-line overlay; the
3873
+ * image's home patch, written by the packer, carries the Worker's required
3874
+ * row config (`PLATFORM_ENTRY_CONFIG` in `image-layout.ts`).
3875
+ * @param appBoot - The image's app-boot.
3876
+ * @param vfs - Filesystem holding the profile.
3877
+ * @param manifest - Validated image manifest naming the profile and installation.
3878
+ * @param root - Virtual root, the process working directory.
3879
+ * @param env - The process shim's environment, after the launch layers applied.
3880
+ * @returns Effective boot patches, the profile context, and the launch environment snapshot.
3881
+ * @throws When the image names no installation manifest to anchor bundle resolution.
3882
+ */
3883
+ function prepareProfile(appBoot, vfs, manifest, root, env) {
3884
+ if (manifest.installation === void 0) throw new Error("webworker host: the image names no installation manifest, so no profile bundle can resolve; rebuild the image");
3885
+ const home = join(root, IMAGE_HOME_DIRECTORY);
3886
+ const installAnchor = join(root, "node_modules", manifest.installation, "package.json");
3887
+ const environment = appBoot.loadLayeredEnv(BIN_NAME, root);
3888
+ const loaded = appBoot.loadProfile(BIN_NAME, manifest.profile, installAnchor, home);
3889
+ appBoot.reportSkippedBundles(BIN_NAME, loaded);
3890
+ vfs.writeFileSync(join(loaded.dir, "cordis.yml"), "[]\n");
3543
3891
  const profile = {
3544
- name: "preview",
3545
- dir,
3546
- patchPath: join(dir, "cordis.patch.yml"),
3547
- installAnchor: join(dir, "package.json"),
3892
+ name: loaded.name,
3893
+ dir: loaded.dir,
3894
+ patchPath: loaded.patchPath,
3895
+ installAnchor,
3548
3896
  cwd: root,
3549
- home: join(root, IMAGE_HOME_DIRECTORY),
3550
- startedBundles: [],
3551
- overlays: patches,
3552
- telemetryDisabledEnv: void 0
3897
+ home,
3898
+ startedBundles: loaded.layers.map((layer) => layer.packageName),
3899
+ overlays: [],
3900
+ telemetryDisabledEnv: env.DSH_TELEMETRY_DISABLED
3553
3901
  };
3554
- vfs.seed(join(dir, "package.json"), "{\"private\":true,\"dsh\":{\"profile\":{\"bundles\":[]}}}\n");
3555
- vfs.seed(join(dir, "cordis.yml"), "[]\n");
3556
- if (!vfs.existsSync(profile.patchPath)) vfs.seed(profile.patchPath, yaml.dump([{ insert: rows }], { schema: include.entryListSchema }));
3557
3902
  return {
3558
- patches: loader.load(loader.resolve("@deepseek-ai/dsh-app-boot", root)).readProfilePatches("dsh-webworker", profile),
3559
- profile
3903
+ patches: appBoot.readProfilePatches(BIN_NAME, profile, loaded),
3904
+ profile,
3905
+ environment
3560
3906
  };
3561
3907
  }
3562
3908
  /**
@@ -3618,4 +3964,4 @@ function parsePreviewFixtureManifest(value) {
3618
3964
  };
3619
3965
  }
3620
3966
  //#endregion
3621
- export { API_PREFIX, DEFAULT_CONDITIONS, DEFAULT_ROOT, IMAGE_CONFIG_PATH, IMAGE_EMPTY_DIRECTORIES, IMAGE_FILE_NAME, IMAGE_HOME_DIRECTORY, IMAGE_MANIFEST_PATH, IMAGE_OVERLAY_DIRECTORIES, LOWERING_VERSION, MODULE_PROXIES, MODULE_PROXY_PREFIXES, MemoryVfs, PREVIEW_FIXTURE_MANIFEST_FILE, PREVIEW_FIXTURE_MANIFEST_VERSION, REPLACED_EXTERNAL_PACKAGES, SYNTHETIC_HOST, TunnelServer, WRAPPER_PARAMS, WorkerModuleLoader, createAlsRuntime, createSyntheticExchange, createWorkerHost, inflateImage, inflateImageStream, installProcessGlobal, loadVfsImage, loadVfsOverlay, lowerModuleSource, packTar, parseInboundFrame, parsePreviewFixtureManifest, parseTar, posix_path_exports as posixPath, requireActiveModuleLoader, requireActiveVfs, setActiveModuleLoader, setActiveVfs };
3967
+ export { API_PREFIX, DEFAULT_CONDITIONS, DEFAULT_ROOT, IMAGE_EMPTY_DIRECTORIES, IMAGE_FILE_NAME, IMAGE_HOME_DIRECTORY, IMAGE_LAZY_DIRECTORY, IMAGE_MANIFEST_PATH, IMAGE_OVERLAY_DIRECTORIES, LOWERING_VERSION, MODULE_PROXIES, MODULE_PROXY_PREFIXES, MemoryVfs, PLATFORM_ENTRY_CONFIG, PREVIEW_FIXTURE_MANIFEST_FILE, PREVIEW_FIXTURE_MANIFEST_VERSION, REPLACED_EXTERNAL_PACKAGES, SYNTHETIC_HOST, TunnelServer, WRAPPER_PARAMS, WorkerModuleLoader, createAlsRuntime, createSyntheticExchange, createWorkerHost, inflateImage, inflateImageStream, installProcessGlobal, loadVfsImage, loadVfsOverlay, lowerModuleSource, packTar, parseImageManifest, parseInboundFrame, parsePreviewFixtureManifest, parseTar, posix_path_exports as posixPath, requireActiveModuleLoader, requireActiveVfs, setActiveModuleLoader, setActiveVfs };