@forgeax/engine-devkit 0.1.3 → 0.1.6

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 (53) hide show
  1. package/README.md +30 -4
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/__tests__/cli-output.unit.test.d.ts +2 -0
  4. package/dist/__tests__/cli-output.unit.test.d.ts.map +1 -0
  5. package/dist/__tests__/gltf-source-key-diagnostics.integration.test.d.ts +2 -0
  6. package/dist/__tests__/gltf-source-key-diagnostics.integration.test.d.ts.map +1 -0
  7. package/dist/__tests__/preview-port.integration.test.d.ts +2 -0
  8. package/dist/__tests__/preview-port.integration.test.d.ts.map +1 -0
  9. package/dist/__tests__/rhi-debug-build-graph.integration.test.d.ts +2 -0
  10. package/dist/__tests__/rhi-debug-build-graph.integration.test.d.ts.map +1 -0
  11. package/dist/__tests__/sdk-update.unit.test.d.ts +2 -0
  12. package/dist/__tests__/sdk-update.unit.test.d.ts.map +1 -0
  13. package/dist/assets.d.ts.map +1 -1
  14. package/dist/bootstrap-commands.d.ts.map +1 -1
  15. package/dist/cli-output.d.ts +4 -0
  16. package/dist/cli-output.d.ts.map +1 -0
  17. package/dist/cli.mjs +728 -641
  18. package/dist/cli.mjs.map +1 -1
  19. package/dist/commands.d.ts.map +1 -1
  20. package/dist/host/__tests__/bootstrap-root-owner.test-d.d.ts +2 -0
  21. package/dist/host/__tests__/bootstrap-root-owner.test-d.d.ts.map +1 -0
  22. package/dist/host/project-bootstrap.d.ts +4 -2
  23. package/dist/host/project-bootstrap.d.ts.map +1 -1
  24. package/dist/host/resource-bootstrap.d.ts +5 -3
  25. package/dist/host/resource-bootstrap.d.ts.map +1 -1
  26. package/dist/host.d.ts +2 -1
  27. package/dist/host.d.ts.map +1 -1
  28. package/dist/index.d.ts +4 -0
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.mjs +662 -655
  31. package/dist/index.mjs.map +1 -1
  32. package/dist/init.d.ts +2 -1
  33. package/dist/init.d.ts.map +1 -1
  34. package/dist/project.d.ts +0 -1
  35. package/dist/project.d.ts.map +1 -1
  36. package/dist/rhi-debug/cli-context.d.ts.map +1 -1
  37. package/dist/sdk-bootstrap.d.ts +34 -0
  38. package/dist/sdk-bootstrap.d.ts.map +1 -0
  39. package/dist/sdk-cli.mjs +412 -66
  40. package/dist/sdk-cli.mjs.map +1 -1
  41. package/dist/sdk-install.d.ts.map +1 -1
  42. package/dist/sdk-update.d.ts +23 -0
  43. package/dist/sdk-update.d.ts.map +1 -0
  44. package/dist/tools/benchmark/__tests__/benchmark-vocabulary-owner.test-d.d.ts +2 -0
  45. package/dist/tools/benchmark/__tests__/benchmark-vocabulary-owner.test-d.d.ts.map +1 -0
  46. package/dist/tools/benchmark/harness.d.ts.map +1 -1
  47. package/dist/tools/browser-host.d.ts.map +1 -1
  48. package/dist/tools/native-preview.d.ts.map +1 -1
  49. package/dist/types.d.ts +7 -0
  50. package/dist/types.d.ts.map +1 -1
  51. package/package.json +29 -30
  52. package/dist/__tests__/tool-migration.test.d.ts +0 -2
  53. package/dist/__tests__/tool-migration.test.d.ts.map +0 -1
package/dist/index.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  import { deflateRawSync } from 'zlib';
2
2
  import { createHash } from 'crypto';
3
- import { readFile, writeFile, copyFile, readdir, mkdir, mkdtemp, cp, rename, rm, stat, symlink, realpath, unlink, lstat, readlink, rmdir, access } from 'fs/promises';
3
+ import { readFile, writeFile, mkdir, copyFile, readdir, mkdtemp, cp, rename, rm, stat, symlink, realpath, unlink, lstat, readlink, rmdir, access } from 'fs/promises';
4
4
  import { resolve, relative, sep, isAbsolute, extname, dirname, basename, join } from 'path';
5
- import { existsSync, readFileSync } from 'fs';
5
+ import { existsSync } from 'fs';
6
6
  import { createRequire } from 'module';
7
7
  import { fileURLToPath } from 'url';
8
8
  import { audioImporter } from '@forgeax/engine-audio-webaudio/audio-importer';
@@ -10,8 +10,11 @@ import { fbxImporter } from '@forgeax/engine-fbx';
10
10
  import { fontImporter } from '@forgeax/engine-font/font-importer';
11
11
  import { gltfImporter } from '@forgeax/engine-gltf';
12
12
  import { imageImporter } from '@forgeax/engine-image/image-importer';
13
- import { validatePreviewArtifactManifest, createMaterialPreviewContribution, createMeshPreviewContribution, createVfxPreviewContribution, createTexturePreviewContribution, describeResourcePreviewFailure, validateCanonicalKitReceipt } from '@forgeax/engine-preview';
14
- import { err, ok, createStandaloneRuntimeAssetBinding } from '@forgeax/engine-types';
13
+ import { BUILTIN_MESH_ASSETS } from '@forgeax/engine-pack/builtin';
14
+ import { scanInventory } from '@forgeax/engine-pack/scanner';
15
+ import { validatePreviewArtifactManifest, createMaterialPreviewContribution, createMeshPreviewContribution, createVfxPreviewContribution, createTexturePreviewContribution, validateCanonicalKitReceipt, describeResourcePreviewFailure } from '@forgeax/engine-preview';
16
+ import { createMaterialPackCooker } from '@forgeax/engine-shader-compiler';
17
+ import { createStandaloneRuntimeAssetBinding, err, ok } from '@forgeax/engine-types';
15
18
  import { createParticleCodeNativeCookerFromRoots } from '@forgeax/engine-vfx-compiler';
16
19
  import { pluginPack, reloadAssetHost } from '@forgeax/engine-vite-plugin-pack';
17
20
  import { forgeaxShader } from '@forgeax/engine-vite-plugin-shader';
@@ -20,10 +23,12 @@ import { runCliGltf } from '@forgeax/engine-gltf/cli-gltf';
20
23
  import { scanEntries } from '@forgeax/engine-pack/cli-asset';
21
24
  import { AssetGuid } from '@forgeax/engine-pack/guid';
22
25
  import { execFile } from 'child_process';
26
+ import { tmpdir } from 'os';
23
27
  import { promisify } from 'util';
24
28
  import { createRhiDebugError, decodeTape, openReplay, buildFrameModel } from '@forgeax/engine-rhi-debug';
25
- import { tmpdir } from 'os';
26
- import { build, createServer, preview } from 'vite';
29
+ import { rhi, createShaderModule } from '@forgeax/engine-rhi-null';
30
+ import { build, preview, createServer as createServer$2 } from 'vite';
31
+ import { createServer as createServer$1 } from 'net';
27
32
  import { startVitest } from 'vitest/node';
28
33
  import materialPreviewPlugin from '@forgeax/engine-preview/material';
29
34
  import meshPreviewPlugin from '@forgeax/engine-preview/mesh';
@@ -33,9 +38,8 @@ import { validateArtifactManifest, defineTool, createToolRuntime, validateRealmB
33
38
  export { createAuthenticatedLoopbackTransport, createCapabilityToken, createMigrationRecipe, createRealmCapabilityMatrix, createServiceCapability, probeMigrationTarget } from '@forgeax/engine-tool-runtime';
34
39
  import { Context, isToolPlugin } from '@forgeax/engine-plugin';
35
40
  import { installCatalogLoader, projectPluginEntries, bootstrapCatalogLoader } from '@forgeax/engine-plugin/loader';
36
- import { createServer as createServer$1 } from 'http';
41
+ import { createServer } from 'http';
37
42
  import { createToolPreviewRecipe, createToolPreviewHost } from '@forgeax/engine-app';
38
- import { createServer as createServer$2 } from 'net';
39
43
  import { chromium } from 'playwright';
40
44
 
41
45
  var __defProp = Object.defineProperty;
@@ -300,16 +304,17 @@ var init_dist = __esm({
300
304
  "src/dist.ts"() {
301
305
  }
302
306
  });
307
+
308
+ // src/host.ts
309
+ var host_exports = {};
310
+ __export(host_exports, {
311
+ createViteConfig: () => createViteConfig,
312
+ devKitDdcRoots: () => devKitDdcRoots,
313
+ ignoreDevKitCatalogPath: () => ignoreDevKitCatalogPath
314
+ });
303
315
  function ignoreDevKitCatalogPath(path) {
304
316
  const normalized = path.replace(/\\/g, "/");
305
- if (normalized.split("/").includes("shaders")) return true;
306
- if (!normalized.endsWith(".meta.json")) return false;
307
- try {
308
- const meta = JSON.parse(readFileSync(path, "utf8"));
309
- return meta.importer === "game-default-target-profile";
310
- } catch {
311
- return false;
312
- }
317
+ return normalized.split("/").includes("shaders");
313
318
  }
314
319
  function devKitDdcRoots(projectRoot) {
315
320
  return {
@@ -323,6 +328,42 @@ function isResourcePreviewIgnoredPath(path) {
323
328
  function isProjectSourceIgnoredPath(path) {
324
329
  return ignoreDevKitCatalogPath(path) || path.endsWith(".wgsl.meta.json");
325
330
  }
331
+ async function prepareBuiltinPack(projectRoots, generated, ignorePath) {
332
+ const inventory = await scanInventory(projectRoots, { ignorePath });
333
+ if (!inventory.ok) return void 0;
334
+ const declared = /* @__PURE__ */ new Set();
335
+ for (const declaration of inventory.value.declarations.values()) {
336
+ if (declaration.format === "pack.json") {
337
+ for (const asset of declaration.value.assets) declared.add(asset.guid.toLowerCase());
338
+ continue;
339
+ }
340
+ for (const asset of declaration.value.subAssets) declared.add(asset.guid.toLowerCase());
341
+ }
342
+ const missing = BUILTIN_MESH_ASSETS.filter((asset) => !declared.has(asset.guid.toLowerCase()));
343
+ if (missing.length === 0) return void 0;
344
+ const packPath = resolve(generated, "engine-builtins.pack.json");
345
+ await writeFile(
346
+ packPath,
347
+ `${JSON.stringify(
348
+ {
349
+ schemaVersion: "2.0.0",
350
+ kind: "internal-text-package",
351
+ assets: missing.map((asset) => ({
352
+ guid: asset.guid,
353
+ kind: "mesh",
354
+ payload: { geometry: asset.geometry },
355
+ refs: [],
356
+ artifacts: {}
357
+ }))
358
+ },
359
+ null,
360
+ 2
361
+ )}
362
+ `,
363
+ "utf8"
364
+ );
365
+ return packPath;
366
+ }
326
367
  function findEngineWorkspaceRoot() {
327
368
  let cursor = dirname(fileURLToPath(import.meta.url));
328
369
  for (; ; ) {
@@ -338,6 +379,7 @@ async function engineWorkspacePackages() {
338
379
  const workspaceRoot = findEngineWorkspaceRoot();
339
380
  if (workspaceRoot === void 0) return /* @__PURE__ */ new Map();
340
381
  const packageRoot = resolve(workspaceRoot, "packages");
382
+ if (!existsSync(packageRoot)) return /* @__PURE__ */ new Map();
341
383
  const packages = /* @__PURE__ */ new Map();
342
384
  for (const entry of await readdir(packageRoot, { withFileTypes: true })) {
343
385
  if (!entry.isDirectory()) continue;
@@ -432,6 +474,18 @@ async function createEngineWorkspaceResolver() {
432
474
  }
433
475
  };
434
476
  }
477
+ async function consumerEngineAliases(projectRoot) {
478
+ const root = resolve(projectRoot, "node_modules", ".pnpm", "node_modules", "@forgeax");
479
+ try {
480
+ const entries2 = await readdir(root, { withFileTypes: true });
481
+ return entries2.filter((entry) => entry.isDirectory() && entry.name.startsWith("engine-")).map((entry) => ({
482
+ find: `@forgeax/${entry.name}`,
483
+ replacement: resolve(root, entry.name)
484
+ })).sort((a, b) => a.find.localeCompare(b.find));
485
+ } catch {
486
+ return [];
487
+ }
488
+ }
435
489
  async function resolveCanonicalKit() {
436
490
  const packageJson = hostRequire.resolve("@forgeax/engine-preview/package.json");
437
491
  const root = resolve(packageJson, "..", "assets/canonical-kit");
@@ -506,6 +560,7 @@ function hostSource(facts, bootstrapRoot, canonicalEnvironmentGuid) {
506
560
  const plugins = bootstrapRoot === "resource-bootstrap" ? [] : [
507
561
  `webAudioPlugin()`,
508
562
  `audioPlugin()`,
563
+ `skinningPlugin()`,
509
564
  ...facts.physics === void 0 ? [] : [`physicsPlugin('${facts.physics === "2d" ? "rapier-2d" : "rapier-3d"}')`]
510
565
  ];
511
566
  const bootstrapPlugin = facts.bootstrapEntry === void 0 ? "" : `const bootstrapModule = await import(${JSON.stringify(
@@ -523,20 +578,24 @@ await app.pluginContext.plugin({
523
578
  },
524
579
  });`;
525
580
  return `import { forgeaxBundlerAdapter } from 'virtual:forgeax/bundler';
526
- import { createApp, createToolPreviewHost, createToolPreviewRecipe, fitToolPreviewCameraToAabb, gameHostPlugin, replayToolPreviewCapture } from '@forgeax/engine-app';
527
- import { createCatalogSource } from '@forgeax/engine-assets-runtime';
528
- import { installCatalogLoader, projectPluginEntries } from '@forgeax/engine-plugin/loader';
529
- import { audioPlugin } from '@forgeax/engine-audio';
530
- import { webAudioPlugin } from '@forgeax/engine-audio-webaudio';
531
- import { physicsPlugin } from '@forgeax/engine-physics';
532
- import { createPrimitiveMesh } from '@forgeax/engine-geometry';
533
- import { createDevImportTransport } from '@forgeax/engine-runtime';
534
- import { mat4 } from '@forgeax/engine-math';
535
- import { CAMERA_PROJECTION_ORTHOGRAPHIC, Camera, DirectionalLight, Materials, MeshFilter, MeshRenderer, Skylight, SkyboxBackground, TONEMAP_NONE, TONEMAP_REINHARD_EXTENDED } from '@forgeax/engine-render';
536
- import { projectSceneAsset, Transform, worldInstantiateScene } from '@forgeax/engine-scene';
537
- import { createStandaloneRuntimeAssetBinding } from '@forgeax/engine-types';
538
- import { ParticleEffectPlayer, vfxGpuEffectContribution } from '@forgeax/engine-vfx';
539
- import { createVfxRuntimeHost } from '@forgeax/engine-vfx-render';
581
+ import { createApp, createToolPreviewHost, createToolPreviewRecipe, fitToolPreviewCameraToAabb, gameHostPlugin, replayToolPreviewCapture } from '@forgeax/engine/app';
582
+ import { createCatalogSource } from '@forgeax/engine/assets-runtime';
583
+ import {
584
+ createRuntimeAssetImportTransport,
585
+ runtimeBinding,
586
+ } from 'virtual:forgeax/pack-runtime';
587
+ import { installCatalogLoader, projectPluginEntries } from '@forgeax/engine/plugin/loader';
588
+ import { audioPlugin } from '@forgeax/engine/audio';
589
+ import { webAudioPlugin } from '@forgeax/engine/audio-webaudio';
590
+ import { physicsPlugin } from '@forgeax/engine/physics';
591
+ import { skinningPlugin } from '@forgeax/engine/skinning';
592
+ import { createPrimitiveMesh } from '@forgeax/engine/geometry';
593
+ import { mat4 } from '@forgeax/engine/math';
594
+ import { CAMERA_PROJECTION_ORTHOGRAPHIC, Camera, DirectionalLight, Materials, MeshFilter, MeshRenderer, Skylight, SkyboxBackground, TONEMAP_NONE, TONEMAP_REINHARD_EXTENDED } from '@forgeax/engine/render';
595
+ import { Transform } from '@forgeax/engine/scene';
596
+ import { type SceneAsset } from '@forgeax/engine/types';
597
+ import { ParticleEffectPlayer, vfxGpuEffectContribution } from '@forgeax/engine/vfx';
598
+ import { createVfxRuntimeHost } from '@forgeax/engine/vfx-render';
540
599
 
541
600
  const pluginCatalog = new Map([
542
601
  ${catalogModules(facts).map(
@@ -635,16 +694,39 @@ const resizeCanvas = () => {
635
694
  const resizeObserver = new ResizeObserver(resizeCanvas);
636
695
  resizeObserver.observe(canvas);
637
696
  resizeCanvas();
638
- const binding = import.meta.env.DEV
639
- ? createStandaloneRuntimeAssetBinding(${JSON.stringify(facts.id)})
640
- : undefined;
641
- const assetCatalog = binding === undefined
642
- ? undefined
643
- : createCatalogSource({ url: binding.catalogUrl, expectedScope: binding });
697
+ const runtimeScopeBinding = runtimeBinding;
698
+ if (runtimeScopeBinding === undefined) {
699
+ throw new Error('forgeax: Vite Pack runtime binding is required in the generated host');
700
+ }
701
+ const assetCatalog = createCatalogSource({
702
+ url: import.meta.env.DEV
703
+ ? runtimeScopeBinding.catalogUrl
704
+ : new URL('pack-index.json', document.baseURI).href,
705
+ ...(import.meta.env.DEV ? { expectedScope: runtimeScopeBinding } : {}),
706
+ });
644
707
  const bundler = {
645
708
  ...forgeaxBundlerAdapter(),
646
- ...(binding === undefined ? {} : { importTransport: createDevImportTransport(binding) }),
709
+ ...(import.meta.env.DEV
710
+ ? { importTransport: createRuntimeAssetImportTransport(runtimeScopeBinding) }
711
+ : {}),
647
712
  };
713
+ const assetPreparation = new WeakMap();
714
+
715
+ function prepareAssetRegistry(assets) {
716
+ const existing = assetPreparation.get(assets);
717
+ if (existing !== undefined) return existing;
718
+ const pending = (async () => {
719
+ assets.configureRuntimeBinding(runtimeScopeBinding);
720
+ assets.setCatalogSource(assetCatalog);
721
+ if (vfxRuntimeHost !== undefined) {
722
+ assets.installDecoder(vfxGpuEffectContribution.kind, vfxGpuEffectContribution.decoder);
723
+ }
724
+ const catalog = await assets.enumerateCatalog();
725
+ if (!catalog.ok) throw catalog.error;
726
+ })();
727
+ assetPreparation.set(assets, pending);
728
+ return pending;
729
+ }
648
730
 
649
731
  function previewStable(value) {
650
732
  if (value instanceof ArrayBuffer) return 'ArrayBuffer:' + JSON.stringify(Array.from(new Uint8Array(value)));
@@ -705,13 +787,14 @@ async function previewOwnerFacts(assets, resource, payload) {
705
787
  }
706
788
 
707
789
  async function prepareProject(app) {
790
+ await prepareAssetRegistry(app.assets);
708
791
  previewWorld = app.world;
709
792
  const assets = app.assets;
710
793
  let canonicalEnvironment;
711
794
  if (resource !== undefined) {
712
795
  if (resource.kind !== 'texture') {
713
796
  if (canonicalEnvironmentGuid === null) throw new Error('canonical preview environment is unavailable');
714
- const environment = await assets.load(canonicalEnvironmentGuid, 'equirect');
797
+ const environment = await assets.loadByGuid(assets.parseGuid(canonicalEnvironmentGuid));
715
798
  if (!environment.ok) throw environment.error;
716
799
  if (environment.value.kind !== 'equirect') {
717
800
  throw new Error(\`canonical preview environment expected equirect, received \${environment.value.kind}\`);
@@ -726,7 +809,7 @@ if (resource !== undefined) {
726
809
  const attached = await vfxRuntimeHost.attachWorld({ world: app.world, assets });
727
810
  if (!attached.ok) throw attached.error;
728
811
  }
729
- const loaded = await assets.load(resource.guid, resource.kind === 'vfx' ? 'particle-effect' : resource.kind);
812
+ const loaded = await assets.loadByGuid(assets.parseGuid(resource.guid));
730
813
  if (!loaded.ok) throw loaded.error;
731
814
  const payload = loaded.value;
732
815
  if (resource.kind === 'vfx' && payload.kind !== 'particle-effect') {
@@ -739,7 +822,7 @@ if (resource !== undefined) {
739
822
  const meshMaterialHandles = resource.kind === 'mesh' && payload.kind === 'mesh'
740
823
  ? await Promise.all(payload.materialSlots.map(async (slot) => {
741
824
  if (slot.defaultMaterial === undefined) return 0;
742
- const material = await assets.load(slot.defaultMaterial, 'material');
825
+ const material = await assets.loadByGuid(assets.parseGuid(slot.defaultMaterial));
743
826
  if (!material.ok) throw material.error;
744
827
  if (material.value.kind !== 'material') {
745
828
  throw new Error('mesh material slot resolved to a non-material asset');
@@ -869,15 +952,13 @@ let defaultScene;
869
952
  let defaultSceneRoot;
870
953
  const defaultSceneGuid = ${JSON.stringify(facts.defaultScene)};
871
954
  if (defaultSceneGuid !== undefined) {
872
- const loaded = await assets.load(defaultSceneGuid, 'scene');
955
+ const loaded = await assets.loadByGuid<SceneAsset>(assets.parseGuid(defaultSceneGuid));
873
956
  if (!loaded.ok) throw loaded.error;
874
- const projected = await projectSceneAsset(app.world, loaded.value, (guid, kind) => assets.load(guid, kind));
875
- if (!projected.ok) throw projected.error;
876
- defaultScene = projected.value;
877
- const handle = app.world.allocSharedRef('SceneAsset', projected.value);
878
- const instantiated = worldInstantiateScene(app.world, handle);
957
+ defaultScene = loaded.value;
958
+ const handle = app.world.allocSharedRef('SceneAsset', loaded.value);
959
+ const instantiated = assets.instantiate<SceneAsset>(handle, app.world);
879
960
  if (!instantiated.ok) throw instantiated.error;
880
- defaultSceneRoot = instantiated.value.root;
961
+ defaultSceneRoot = instantiated.value;
881
962
  }
882
963
  const uiRoot = document.querySelector('#game-ui');
883
964
  await app.pluginContext.plugin(gameHostPlugin({
@@ -916,8 +997,6 @@ if (query.has('forgeax-tool-replay')) {
916
997
  ...(resource === undefined ? {} : { resource }),
917
998
  canvas,
918
999
  app: {
919
- ...(assetCatalog === undefined ? {} : { assetCatalog }),
920
- ...(vfxRuntimeHost === undefined ? {} : { assetDecoders: [vfxGpuEffectContribution] }),
921
1000
  plugins: [${plugins.join(", ")}],
922
1001
  ...(vfxRuntimeHost === undefined ? {} : { features: [vfxRuntimeHost.feature] }),
923
1002
  },
@@ -1022,12 +1101,16 @@ if (query.has('forgeax-tool-replay')) {
1022
1101
  window.addEventListener('pagehide', () => void host.value.dispose(), { once: true });
1023
1102
  }
1024
1103
  } else {
1104
+ // WebDriver/headless browsers cannot satisfy the trusted-gesture requirement
1105
+ // for pointer lock. Keep the game running and let its keyboard fallback own
1106
+ // camera input; real browser users retain the normal click-to-lock path.
1107
+ const pointerLockAllowed =
1108
+ typeof navigator !== 'undefined' && navigator.webdriver === true ? () => false : undefined;
1025
1109
  const result = await createApp(
1026
1110
  canvas,
1027
1111
  {
1028
- ...(assetCatalog === undefined ? {} : { assetCatalog }),
1029
- ...(vfxRuntimeHost === undefined ? {} : { assetDecoders: [vfxGpuEffectContribution] }),
1030
1112
  plugins: [${plugins.join(", ")}],
1113
+ ...(pointerLockAllowed === undefined ? {} : { pointerLockAllowed }),
1031
1114
  },
1032
1115
  bundler,
1033
1116
  );
@@ -1086,10 +1169,32 @@ function htmlSource(title) {
1086
1169
  <div id="app-shell"><canvas id="app"></canvas><div id="game-ui"></div></div><div id="forgeax-fatal" role="alert"></div>
1087
1170
  <script>
1088
1171
  (() => {
1172
+ const formatStartupFailure = (reason) => {
1173
+ if (reason instanceof Error) return reason.message;
1174
+ if (reason !== null && typeof reason === 'object') {
1175
+ const record = reason;
1176
+ const lines = [];
1177
+ for (const key of ['code', 'expected', 'hint']) {
1178
+ if (typeof record[key] === 'string' && record[key].length > 0) {
1179
+ lines.push(key + ': ' + record[key]);
1180
+ }
1181
+ }
1182
+ const detail = record.detail;
1183
+ if (detail !== null && typeof detail === 'object') {
1184
+ for (const key of ['reason', 'guid']) {
1185
+ if (typeof detail[key] === 'string' && detail[key].length > 0) {
1186
+ lines.push('detail.' + key + ': ' + detail[key]);
1187
+ }
1188
+ }
1189
+ }
1190
+ if (lines.length > 0) return lines.join('\\n');
1191
+ }
1192
+ return String(reason ?? 'Unknown startup failure');
1193
+ };
1089
1194
  const show = (reason) => {
1090
1195
  const notice = document.querySelector('#forgeax-fatal');
1091
1196
  if (!(notice instanceof HTMLElement)) return;
1092
- const message = reason instanceof Error ? reason.message : String(reason ?? 'Unknown startup failure');
1197
+ const message = formatStartupFailure(reason);
1093
1198
  notice.textContent = 'ForgeaX game failed to start.\\n' + message;
1094
1199
  notice.style.display = 'grid';
1095
1200
  };
@@ -1104,29 +1209,32 @@ function htmlSource(title) {
1104
1209
  }
1105
1210
  async function createViteConfig(facts, command, base = "/", options = {}) {
1106
1211
  const bootstrapRoot = options.bootstrapRoot ?? "project-bootstrap";
1107
- const canonicalKit = bootstrapRoot === "resource-bootstrap" ? await resolveCanonicalKit() : void 0;
1212
+ const canonicalKit = await resolveCanonicalKit();
1108
1213
  const generated = resolve(facts.root, ".forgeax", "generated");
1109
1214
  await mkdir(generated, { recursive: true });
1215
+ const projectRoots = facts.assetRoots.map((root) => resolve(facts.root, root));
1216
+ const ignorePath = bootstrapRoot === "resource-bootstrap" ? isResourcePreviewIgnoredPath : isProjectSourceIgnoredPath;
1217
+ const builtinPack = await prepareBuiltinPack(projectRoots, generated, ignorePath);
1110
1218
  await Promise.all([
1111
1219
  writeFile(resolve(generated, "index.html"), htmlSource(facts.name)),
1112
1220
  writeFile(resolve(generated, "main.ts"), hostSource(facts, bootstrapRoot, canonicalKit?.guid))
1113
1221
  ]);
1114
1222
  const roots = [
1115
- ...facts.assetRoots.map((root) => resolve(facts.root, root)),
1223
+ ...projectRoots,
1224
+ ...builtinPack === void 0 ? [] : [builtinPack],
1116
1225
  ...canonicalKit === void 0 ? [] : [canonicalKit.root]
1117
1226
  ];
1118
1227
  const importers = [...projectImporters(facts)];
1119
1228
  const runtimeBinding = createStandaloneRuntimeAssetBinding(facts.id);
1120
1229
  const engineWorkspaceResolver = await createEngineWorkspaceResolver();
1230
+ const consumerAliases = await consumerEngineAliases(facts.root);
1121
1231
  const plugins = [
1122
1232
  ...engineWorkspaceResolver === void 0 ? [] : [engineWorkspaceResolver],
1123
1233
  forgeaxShader(),
1124
1234
  pluginPack({
1125
1235
  roots,
1126
- base,
1127
1236
  runtimeBinding,
1128
1237
  ddc: devKitDdcRoots(facts.root),
1129
- scriptablePack: {},
1130
1238
  refresh: command === "serve" ? reloadAssetHost() : void 0,
1131
1239
  importers: [
1132
1240
  audioImporter,
@@ -1136,8 +1244,8 @@ async function createViteConfig(facts, command, base = "/", options = {}) {
1136
1244
  fontImporter,
1137
1245
  ...importers
1138
1246
  ],
1139
- cookers: [createParticleCodeNativeCookerFromRoots(roots)],
1140
- ...bootstrapRoot === "resource-bootstrap" ? { ignorePath: isResourcePreviewIgnoredPath } : { ignorePath: isProjectSourceIgnoredPath }
1247
+ cookers: [createMaterialPackCooker(roots), createParticleCodeNativeCookerFromRoots(roots)],
1248
+ ignorePath
1141
1249
  })
1142
1250
  ];
1143
1251
  return {
@@ -1146,8 +1254,11 @@ async function createViteConfig(facts, command, base = "/", options = {}) {
1146
1254
  configFile: false,
1147
1255
  publicDir: facts.assetPublicDir === void 0 ? false : resolve(facts.root, facts.assetPublicDir),
1148
1256
  plugins,
1149
- resolve: { dedupe: ["@forgeax/engine-app", "@forgeax/engine-ecs", "@forgeax/engine-runtime"] },
1150
- server: { fs: { allow: [facts.root, ...roots] } },
1257
+ resolve: {
1258
+ alias: consumerAliases,
1259
+ dedupe: ["@forgeax/engine"]
1260
+ },
1261
+ server: { ...options.server, fs: { allow: [facts.root, ...roots] } },
1151
1262
  build: {
1152
1263
  target: "esnext",
1153
1264
  outDir: options.outDir ?? resolve(facts.root, "dist"),
@@ -1171,7 +1282,7 @@ async function readJson(path) {
1171
1282
  function firstUnsupportedStandaloneRealm(entries2, inheritedRealm = "engine") {
1172
1283
  for (const entry of entries2) {
1173
1284
  const realm = entry.realm ?? inheritedRealm ?? "engine";
1174
- if (realm === "host") return { id: entry.id, realm };
1285
+ if (realm !== "engine") return { id: entry.id, realm };
1175
1286
  if (entry.group === true) {
1176
1287
  const unsupported = firstUnsupportedStandaloneRealm(
1177
1288
  entry.config,
@@ -1289,13 +1400,10 @@ async function readProjectFacts(rootInput = process.cwd()) {
1289
1400
  }
1290
1401
  };
1291
1402
  }
1292
- function isCommandError(value) {
1293
- if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
1294
- const candidate = value;
1295
- return typeof candidate.code === "string" && typeof candidate.expected === "string" && typeof candidate.hint === "string" && candidate.detail !== null && typeof candidate.detail === "object" && !Array.isArray(candidate.detail);
1296
- }
1297
1403
  function commandError(cause, fallbackCode) {
1298
- if (isCommandError(cause)) return cause;
1404
+ if (cause !== null && typeof cause === "object" && "code" in cause && "expected" in cause && "hint" in cause && "detail" in cause) {
1405
+ return cause;
1406
+ }
1299
1407
  return {
1300
1408
  code: fallbackCode,
1301
1409
  expected: "the ForgeaX command to complete",
@@ -1307,16 +1415,17 @@ var init_project = __esm({
1307
1415
  "src/project.ts"() {
1308
1416
  }
1309
1417
  });
1310
- function failure(error) {
1311
- return { ok: false, error };
1418
+
1419
+ // src/types.ts
1420
+ function resolveProjectPort(port) {
1421
+ return { port: port ?? 5173, strictPort: port !== 0 };
1312
1422
  }
1313
- function producerFailure(stderr, fallback, detail) {
1314
- try {
1315
- const parsed = JSON.parse(stderr.at(-1) ?? "");
1316
- if (isCommandError(parsed)) return failure(parsed);
1317
- } catch {
1423
+ var init_types = __esm({
1424
+ "src/types.ts"() {
1318
1425
  }
1319
- return failure({ ...fallback, detail: { ...detail, diagnostic: stderr.join("\n") } });
1426
+ });
1427
+ function failure(error) {
1428
+ return { ok: false, error };
1320
1429
  }
1321
1430
  async function sourcesAt(path) {
1322
1431
  const info = await stat(path);
@@ -1401,15 +1510,16 @@ async function addGltf(sourcePath, dryRun) {
1401
1510
  stderrWrite: (line) => stderr.push(line)
1402
1511
  });
1403
1512
  if (exitCode !== 0) {
1404
- return producerFailure(
1405
- stderr,
1406
- {
1513
+ try {
1514
+ return failure(JSON.parse(stderr.at(-1) ?? ""));
1515
+ } catch {
1516
+ return failure({
1407
1517
  code: "asset-add-failed",
1408
1518
  expected: "the glTF producer to create or reuse a valid sidecar",
1409
- hint: "Inspect the glTF source and its external references."
1410
- },
1411
- { source: sourcePath }
1412
- );
1519
+ hint: "Inspect the glTF source and its external references.",
1520
+ detail: { source: sourcePath, diagnostic: stderr.join("\n") }
1521
+ });
1522
+ }
1413
1523
  }
1414
1524
  return { ok: true, value: { source: sourcePath, metaPath: `${sourcePath}.meta.json` } };
1415
1525
  }
@@ -1452,15 +1562,16 @@ async function entries(options) {
1452
1562
  { stdoutWrite: (line) => stdout.push(line), stderrWrite: (line) => stderr.push(line) }
1453
1563
  );
1454
1564
  if (!result.ok) {
1455
- return producerFailure(
1456
- stderr,
1457
- {
1565
+ try {
1566
+ return failure(JSON.parse(stderr.at(-1) ?? ""));
1567
+ } catch {
1568
+ return failure({
1458
1569
  code: "asset-authority-invalid",
1459
1570
  expected: "all asset roots and sidecars to pass the pack scanner",
1460
- hint: "Repair the first invalid asset authority reported by the scanner."
1461
- },
1462
- {}
1463
- );
1571
+ hint: "Repair the first invalid asset authority reported by the scanner.",
1572
+ detail: { diagnostic: stderr.join("\n") }
1573
+ });
1574
+ }
1464
1575
  }
1465
1576
  return { ok: true, value: { facts: facts.value, entries: result.value } };
1466
1577
  }
@@ -1545,7 +1656,6 @@ var init_package = __esm({
1545
1656
  "@forgeax/engine-audio-webaudio": "workspace:*",
1546
1657
  "@forgeax/engine-fbx": "workspace:*",
1547
1658
  "@forgeax/engine-font": "workspace:*",
1548
- "@forgeax/engine-geometry": "workspace:*",
1549
1659
  "@forgeax/engine-gltf": "workspace:*",
1550
1660
  "@forgeax/engine-image": "workspace:*",
1551
1661
  "@forgeax/engine-input": "workspace:*",
@@ -1555,6 +1665,7 @@ var init_package = __esm({
1555
1665
  "@forgeax/engine-preview": "workspace:*",
1556
1666
  "@forgeax/engine-project": "workspace:*",
1557
1667
  "@forgeax/engine-rhi-debug": "workspace:*",
1668
+ "@forgeax/engine-rhi-null": "workspace:*",
1558
1669
  "@forgeax/engine-render": "workspace:*",
1559
1670
  "@forgeax/engine-render-graph": "workspace:*",
1560
1671
  "@forgeax/engine-runtime": "workspace:*",
@@ -1572,7 +1683,6 @@ var init_package = __esm({
1572
1683
  vitest: "4.1.5"
1573
1684
  },
1574
1685
  devDependencies: {
1575
- "@forgeax/engine-rhi-null": "workspace:*",
1576
1686
  "@types/node": "^20.14.0"
1577
1687
  },
1578
1688
  forgeax: {
@@ -1698,6 +1808,7 @@ function createInitPlan(facts, sdk) {
1698
1808
  value: {
1699
1809
  root: facts.root,
1700
1810
  version: sdk?.sdkVersion ?? devkitVersion,
1811
+ ...sdk === void 0 ? {} : { pnpmVersion: sdk.requirements.pnpm },
1701
1812
  archiveBacked: sdk !== void 0,
1702
1813
  dependencyChanges,
1703
1814
  scriptChanges
@@ -1718,23 +1829,8 @@ async function applyInitPlan(facts, plan, options) {
1718
1829
  for (const change of plan.scriptChanges) scripts[change.name] = change.to;
1719
1830
  manifest.scripts = scripts;
1720
1831
  if (plan.archiveBacked) {
1721
- manifest.packageManager = "pnpm@10.33.2";
1722
- const pnpmValue = manifest.pnpm;
1723
- const pnpm = pnpmValue !== null && typeof pnpmValue === "object" ? pnpmValue : {};
1724
- manifest.pnpm = {
1725
- ...pnpm,
1726
- onlyBuiltDependencies: [
1727
- "@forgeax/engine-codec",
1728
- "@forgeax/engine-fbx",
1729
- "@forgeax/engine-wgpu-wasm",
1730
- "esbuild"
1731
- ],
1732
- supportedArchitectures: {
1733
- os: ["darwin", "linux", "win32"],
1734
- cpu: ["x64", "arm64"],
1735
- libc: ["glibc", "musl"]
1736
- }
1737
- };
1832
+ if (plan.pnpmVersion === void 0) throw new Error("sdk-pnpm-version-missing");
1833
+ manifest.packageManager = `pnpm@${plan.pnpmVersion}`;
1738
1834
  }
1739
1835
  await writeFile(resolve(facts.root, "package.json"), `${JSON.stringify(manifest, null, 2)}
1740
1836
  `);
@@ -1800,6 +1896,270 @@ var init_sdk = __esm({
1800
1896
  "src/sdk.ts"() {
1801
1897
  }
1802
1898
  });
1899
+ function agentOnboarding(sdk, projectRoot) {
1900
+ const root = projectRoot ?? sdk.root;
1901
+ return {
1902
+ read: [
1903
+ resolve(root, "AGENTS.md"),
1904
+ resolve(root, "skills", "forgeax-engine-sdk", "SKILL.md"),
1905
+ resolve(root, "skills", "forgeax-engine-sdk", "references", "feature-catalog.md")
1906
+ ],
1907
+ next: projectRoot === void 0 ? {
1908
+ cwd: root,
1909
+ argv: ["node", "./bin/forgeax.mjs", "new", "../my-game"]
1910
+ } : { cwd: root, argv: ["pnpm", "exec", "forgeax", "list", "--json"] }
1911
+ };
1912
+ }
1913
+ function sdkInitPath(sdk) {
1914
+ return resolve(sdk.root, ...SDK_INIT_PATH);
1915
+ }
1916
+ function commandFailure(code, expected, hint, detail = {}) {
1917
+ return { ok: false, error: { code, expected, hint, detail } };
1918
+ }
1919
+ function sdkProjectInstallArgs(store) {
1920
+ const common = [
1921
+ "install",
1922
+ "--frozen-lockfile",
1923
+ "--ignore-scripts",
1924
+ "--side-effects-cache=true",
1925
+ "--child-concurrency=1"
1926
+ ];
1927
+ return store === void 0 ? common : [...common, "--offline", "--config.trust-lockfile=true", "--store-dir", store];
1928
+ }
1929
+ function sdkBootstrapInstallArgs(store) {
1930
+ const common = [
1931
+ "install",
1932
+ "--frozen-lockfile",
1933
+ "--child-concurrency=1",
1934
+ "--side-effects-cache=true"
1935
+ ];
1936
+ return store === void 0 ? common : [...common, "--offline", "--config.trust-lockfile=true", "--store-dir", store];
1937
+ }
1938
+ function supportedPnpm(version) {
1939
+ const [major = 0, minor = 0] = version.split(".").map(Number);
1940
+ return major === 11 && minor >= 7;
1941
+ }
1942
+ async function currentPnpm() {
1943
+ try {
1944
+ const result = await execFileAsync("pnpm", ["--version"], { maxBuffer: 1024 * 1024 });
1945
+ const version = result.stdout.trim();
1946
+ if (version.length === 0) throw new Error("pnpm returned an empty version");
1947
+ return { ok: true, value: version };
1948
+ } catch (cause) {
1949
+ return commandFailure(
1950
+ "pnpm-unavailable",
1951
+ "pnpm 11.7.0 or newer in the pnpm 11 line to be available on PATH",
1952
+ "Install or activate pnpm 11, then rerun forgeax init from the SDK root.",
1953
+ { reason: cause instanceof Error ? cause.message : String(cause) }
1954
+ );
1955
+ }
1956
+ }
1957
+ function matchesState(sdk, state) {
1958
+ if (state === null || typeof state !== "object" || Array.isArray(state)) return false;
1959
+ const value = state;
1960
+ return value.schemaVersion === SDK_INIT_SCHEMA_VERSION && value.sdkVersion === sdk.manifest.sdkVersion && value.engineCommit === sdk.manifest.engineCommit && typeof value.pnpm === "string" && supportedPnpm(value.pnpm) && value.node === process.versions.node && value.platform === process.platform && value.arch === process.arch;
1961
+ }
1962
+ async function readSdkInitState(sdk) {
1963
+ try {
1964
+ const value = JSON.parse(await readFile(sdkInitPath(sdk), "utf8"));
1965
+ return matchesState(sdk, value) ? value : void 0;
1966
+ } catch {
1967
+ return void 0;
1968
+ }
1969
+ }
1970
+ async function requireSdkInitialization(sdk) {
1971
+ const state = await readSdkInitState(sdk);
1972
+ if (state === void 0) {
1973
+ return commandFailure(
1974
+ "sdk-not-initialized",
1975
+ "the downloaded SDK to be initialized for this Node/pnpm/platform tuple",
1976
+ "Run ./bin/forgeax init from the SDK root once, then run forgeax new outside the SDK.",
1977
+ {
1978
+ sdkRoot: sdk.root,
1979
+ sdkVersion: sdk.manifest.sdkVersion,
1980
+ pnpm: sdk.manifest.requirements.pnpm,
1981
+ node: process.versions.node,
1982
+ platform: process.platform,
1983
+ arch: process.arch
1984
+ }
1985
+ );
1986
+ }
1987
+ return { ok: true, value: state };
1988
+ }
1989
+ async function copyBootstrapInputs(sdk, root) {
1990
+ const template = sdk.templates.get(sdk.defaultTemplate);
1991
+ if (template === void 0) throw new Error("sdk-default-template-missing");
1992
+ for (const name of ["package.json", "pnpm-lock.yaml", "pnpm-workspace.yaml"]) {
1993
+ await cp(resolve(template, name), resolve(root, name));
1994
+ }
1995
+ try {
1996
+ await cp(resolve(template, ".npmrc"), resolve(root, ".npmrc"));
1997
+ } catch (cause) {
1998
+ if (cause === null || typeof cause !== "object" || !("code" in cause) || cause.code !== "ENOENT") {
1999
+ throw cause;
2000
+ }
2001
+ }
2002
+ }
2003
+ async function sdkInitCommand(sdk, options = {}) {
2004
+ const pnpmResult = await currentPnpm();
2005
+ if (!pnpmResult.ok) return pnpmResult;
2006
+ if (!supportedPnpm(pnpmResult.value)) {
2007
+ return commandFailure(
2008
+ "pnpm-version-unsupported",
2009
+ "pnpm >=11.7.0 <12",
2010
+ "Activate pnpm 11.7.0 or newer in the pnpm 11 line, then rerun forgeax init.",
2011
+ { actual: pnpmResult.value, expected: sdk.manifest.requirements.pnpm }
2012
+ );
2013
+ }
2014
+ const state = {
2015
+ schemaVersion: SDK_INIT_SCHEMA_VERSION,
2016
+ sdkVersion: sdk.manifest.sdkVersion,
2017
+ engineCommit: sdk.manifest.engineCommit,
2018
+ pnpm: pnpmResult.value,
2019
+ node: process.versions.node,
2020
+ platform: process.platform,
2021
+ arch: process.arch
2022
+ };
2023
+ const report = {
2024
+ ...state,
2025
+ root: sdk.root,
2026
+ store: sdk.store === void 0 ? "registry" : "offline",
2027
+ onboarding: agentOnboarding(sdk)
2028
+ };
2029
+ if (options.dryRun === true || options.install === false) return { ok: true, value: report };
2030
+ let staging;
2031
+ try {
2032
+ staging = await mkdtemp(resolve(tmpdir(), "forgeax-sdk-init-"));
2033
+ await copyBootstrapInputs(sdk, staging);
2034
+ await execFileAsync("pnpm", sdkBootstrapInstallArgs(sdk.store), {
2035
+ cwd: staging,
2036
+ env: { ...process.env, CI: "true" },
2037
+ maxBuffer: 16 * 1024 * 1024
2038
+ });
2039
+ await mkdir(resolve(sdk.root, ".forgeax"), { recursive: true });
2040
+ await writeFile(sdkInitPath(sdk), `${JSON.stringify(state, null, 2)}
2041
+ `);
2042
+ return { ok: true, value: report };
2043
+ } catch (cause) {
2044
+ return commandFailure(
2045
+ "sdk-init-failed",
2046
+ "the SDK dependency closure to install and build native packages successfully",
2047
+ "Inspect the pnpm output, repair Node/pnpm or platform permissions, then rerun forgeax init from the SDK root.",
2048
+ { reason: cause instanceof Error ? cause.message : String(cause), sdkRoot: sdk.root }
2049
+ );
2050
+ } finally {
2051
+ if (staging !== void 0) await rm(staging, { recursive: true, force: true });
2052
+ }
2053
+ }
2054
+ var execFileAsync, SDK_INIT_PATH, SDK_INIT_SCHEMA_VERSION;
2055
+ var init_sdk_bootstrap = __esm({
2056
+ "src/sdk-bootstrap.ts"() {
2057
+ execFileAsync = promisify(execFile);
2058
+ SDK_INIT_PATH = [".forgeax", "sdk-init.json"];
2059
+ SDK_INIT_SCHEMA_VERSION = "1.0.0";
2060
+ }
2061
+ });
2062
+
2063
+ // src/sdk-update.ts
2064
+ function parseVersion(version) {
2065
+ const match = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/.exec(
2066
+ version
2067
+ );
2068
+ if (match === null) return void 0;
2069
+ const major = match[1];
2070
+ const minor = match[2];
2071
+ const patch = match[3];
2072
+ if (major === void 0 || minor === void 0 || patch === void 0) return void 0;
2073
+ const prerelease = match[4]?.split(".");
2074
+ if (prerelease?.some(
2075
+ (identifier) => identifier.length === 0 || /^\d+$/.test(identifier) && /^0\d+/.test(identifier)
2076
+ )) {
2077
+ return void 0;
2078
+ }
2079
+ return {
2080
+ core: [major, minor, patch],
2081
+ ...prerelease === void 0 ? {} : { prerelease }
2082
+ };
2083
+ }
2084
+ function compareNumericIdentifier(current, latest) {
2085
+ if (current.length !== latest.length) return latest.length > current.length ? 1 : -1;
2086
+ if (current === latest) return 0;
2087
+ return latest > current ? 1 : -1;
2088
+ }
2089
+ function comparePrerelease(current, latest) {
2090
+ const length = Math.max(current.length, latest.length);
2091
+ for (let index = 0; index < length; index += 1) {
2092
+ const currentIdentifier = current[index];
2093
+ const latestIdentifier = latest[index];
2094
+ if (currentIdentifier === void 0) return 1;
2095
+ if (latestIdentifier === void 0) return -1;
2096
+ if (currentIdentifier === latestIdentifier) continue;
2097
+ const currentNumeric = /^\d+$/.test(currentIdentifier);
2098
+ const latestNumeric = /^\d+$/.test(latestIdentifier);
2099
+ if (currentNumeric && latestNumeric) {
2100
+ return compareNumericIdentifier(currentIdentifier, latestIdentifier);
2101
+ }
2102
+ if (currentNumeric !== latestNumeric) return latestNumeric ? -1 : 1;
2103
+ return latestIdentifier > currentIdentifier ? 1 : -1;
2104
+ }
2105
+ return 0;
2106
+ }
2107
+ function newerSdkVersion(currentVersion, latestVersion) {
2108
+ const current = parseVersion(currentVersion);
2109
+ const latest = parseVersion(latestVersion);
2110
+ if (current === void 0 || latest === void 0) return false;
2111
+ for (let index = 0; index < current.core.length; index += 1) {
2112
+ const precedence = compareNumericIdentifier(
2113
+ current.core[index] ?? "0",
2114
+ latest.core[index] ?? "0"
2115
+ );
2116
+ if (precedence !== 0) return precedence > 0;
2117
+ }
2118
+ if (current.prerelease !== void 0 && latest.prerelease === void 0) return true;
2119
+ if (current.prerelease === void 0 || latest.prerelease === void 0) return false;
2120
+ return comparePrerelease(current.prerelease, latest.prerelease) > 0;
2121
+ }
2122
+ function offline() {
2123
+ return ["1", "true"].includes((process.env.npm_config_offline ?? "").toLowerCase());
2124
+ }
2125
+ async function checkSdkUpdate(currentVersion) {
2126
+ if (process.env.FORGEAX_DISABLE_UPDATE_CHECK === "1") {
2127
+ return { status: "skipped", currentVersion, reason: "disabled" };
2128
+ }
2129
+ if (offline()) return { status: "skipped", currentVersion, reason: "offline" };
2130
+ try {
2131
+ const registry = process.env.npm_config_registry ?? "https://registry.npmjs.org/";
2132
+ const response = await fetch(
2133
+ new URL("@forgeax%2Fengine-sdk", `${registry.replace(/\/$/, "")}/`),
2134
+ {
2135
+ headers: { accept: "application/vnd.npm.install-v1+json" },
2136
+ signal: AbortSignal.timeout(2e3)
2137
+ }
2138
+ );
2139
+ if (!response.ok) throw new Error(`registry-http-${response.status}`);
2140
+ const metadata = await response.json();
2141
+ const latestVersion = metadata["dist-tags"]?.latest;
2142
+ if (typeof latestVersion !== "string") throw new Error("registry-latest-version-missing");
2143
+ return newerSdkVersion(currentVersion, latestVersion) ? {
2144
+ status: "available",
2145
+ currentVersion,
2146
+ latestVersion,
2147
+ migrationRisk: UPDATE_MIGRATION_RISK
2148
+ } : { status: "current", currentVersion, latestVersion };
2149
+ } catch (cause) {
2150
+ return {
2151
+ status: "unavailable",
2152
+ currentVersion,
2153
+ reason: cause instanceof Error ? cause.message : String(cause)
2154
+ };
2155
+ }
2156
+ }
2157
+ var UPDATE_MIGRATION_RISK;
2158
+ var init_sdk_update = __esm({
2159
+ "src/sdk-update.ts"() {
2160
+ UPDATE_MIGRATION_RISK = "Install a newer SDK in a separate directory. Existing games remain pinned and are not migrated automatically; review release notes and migrate and test each game before changing its Engine version.";
2161
+ }
2162
+ });
1803
2163
  function slash(path) {
1804
2164
  return path.split(sep).join("/");
1805
2165
  }
@@ -2165,6 +2525,20 @@ async function canonicalProspectivePath(path) {
2165
2525
  }
2166
2526
  }
2167
2527
  }
2528
+ async function configureSdkStore(root, store) {
2529
+ if (store === void 0) return;
2530
+ const path = resolve(root, ".npmrc");
2531
+ let content = "";
2532
+ try {
2533
+ content = await readFile(path, "utf8");
2534
+ } catch (cause) {
2535
+ if (!isMissingPathError(cause)) throw cause;
2536
+ }
2537
+ const lines = content.split(/\r?\n/).filter((line) => line.trim().length > 0 && !/^\s*store-dir\s*=/.test(line));
2538
+ lines.push(`store-dir=${store}`);
2539
+ await writeFile(path, `${lines.join("\n")}
2540
+ `);
2541
+ }
2168
2542
  function containsOrEquals(parent, child) {
2169
2543
  const path = relative(parent, child);
2170
2544
  return path === "" || path !== ".." && !path.startsWith(`..${sep}`) && !isAbsolute(path);
@@ -2175,25 +2549,14 @@ function nodeSupported() {
2175
2549
  }
2176
2550
  function pnpmSupported(version) {
2177
2551
  const [major = 0, minor = 0] = version.split(".").map(Number);
2178
- return major === 10 && minor >= 33;
2179
- }
2180
- function sdkInstallArgs(store) {
2181
- if (store === void 0) return ["install", "--frozen-lockfile"];
2182
- return [
2183
- "install",
2184
- "--offline",
2185
- "--frozen-lockfile",
2186
- "--side-effects-cache=false",
2187
- "--store-dir",
2188
- store
2189
- ];
2552
+ return major === 11 && minor >= 7;
2190
2553
  }
2191
2554
  async function doctorCommand(options = {}) {
2192
2555
  const facts = await readProjectFacts(options.root);
2193
2556
  if (!facts.ok) return facts;
2194
2557
  let pnpm;
2195
2558
  try {
2196
- pnpm = (await execFileAsync("pnpm", ["--version"], { cwd: facts.value.root })).stdout.trim();
2559
+ pnpm = (await execFileAsync2("pnpm", ["--version"], { cwd: facts.value.root })).stdout.trim();
2197
2560
  } catch (cause) {
2198
2561
  return {
2199
2562
  ok: false,
@@ -2221,7 +2584,7 @@ async function doctorCommand(options = {}) {
2221
2584
  ok: false,
2222
2585
  error: {
2223
2586
  code: "pnpm-version-unsupported",
2224
- expected: "pnpm >=10.33.0 <11",
2587
+ expected: "pnpm >=11.7.0 <12",
2225
2588
  hint: "Enable the packageManager-declared pnpm version with Corepack and retry.",
2226
2589
  detail: { actual: pnpm }
2227
2590
  }
@@ -2260,12 +2623,20 @@ async function doctorCommand(options = {}) {
2260
2623
  };
2261
2624
  }
2262
2625
  async function initCommand(options = {}) {
2263
- const facts = await readProjectFacts(options.root);
2264
- if (!facts.ok) return facts;
2265
2626
  try {
2266
2627
  const sdk = await findSdkContext();
2628
+ const root = await canonicalProspectivePath(options.root ?? process.cwd());
2629
+ if (sdk !== void 0 && root === await canonicalProspectivePath(sdk.root)) {
2630
+ return sdkInitCommand(sdk, options);
2631
+ }
2632
+ const facts = await readProjectFacts(root);
2633
+ if (!facts.ok) return facts;
2267
2634
  const plan = createInitPlan(facts.value, sdk?.manifest);
2268
2635
  if (!plan.ok) return plan;
2636
+ if (sdk !== void 0 && options.dryRun !== true) {
2637
+ const initialized = await requireSdkInitialization(sdk);
2638
+ if (!initialized.ok) return initialized;
2639
+ }
2269
2640
  const applied = await applyInitPlan(facts.value, plan.value, options);
2270
2641
  if (!applied.ok || options.dryRun === true) return applied;
2271
2642
  if (sdk !== void 0) {
@@ -2275,12 +2646,31 @@ async function initCommand(options = {}) {
2275
2646
  resolve(template, "pnpm-lock.yaml"),
2276
2647
  resolve(facts.value.root, "pnpm-lock.yaml")
2277
2648
  );
2649
+ await copyFile(
2650
+ resolve(template, "pnpm-workspace.yaml"),
2651
+ resolve(facts.value.root, "pnpm-workspace.yaml")
2652
+ );
2653
+ try {
2654
+ await readFile(resolve(facts.value.root, ".npmrc"), "utf8");
2655
+ } catch (cause) {
2656
+ if (!isMissingPathError(cause)) throw cause;
2657
+ try {
2658
+ await copyFile(resolve(template, ".npmrc"), resolve(facts.value.root, ".npmrc"));
2659
+ } catch (templateCause) {
2660
+ if (!isMissingPathError(templateCause)) throw templateCause;
2661
+ }
2662
+ }
2278
2663
  await copySdkSkills(sdk, facts.value.root);
2279
2664
  await installProjectSkills(facts.value.root, sdk.manifest);
2665
+ await configureSdkStore(
2666
+ facts.value.root,
2667
+ sdk.store === void 0 ? void 0 : await canonicalProspectivePath(sdk.store)
2668
+ );
2280
2669
  }
2281
2670
  if (options.install === false) return applied;
2282
- const installArgs = sdk === void 0 ? ["install", "--frozen-lockfile=false"] : sdkInstallArgs(sdk.store);
2283
- await execFileAsync("pnpm", installArgs, {
2671
+ const store = sdk === void 0 || sdk.store === void 0 ? void 0 : await canonicalProspectivePath(sdk.store);
2672
+ const installArgs = sdk === void 0 ? ["install", "--frozen-lockfile=false"] : sdkProjectInstallArgs(store);
2673
+ await execFileAsync2("pnpm", installArgs, {
2284
2674
  cwd: facts.value.root,
2285
2675
  env: { ...process.env, CI: "true" },
2286
2676
  maxBuffer: 16 * 1024 * 1024
@@ -2358,12 +2748,15 @@ async function newCommand(options = {}) {
2358
2748
  value: { root, template: templateId, sdkVersion: sdk.manifest.sdkVersion }
2359
2749
  };
2360
2750
  }
2751
+ const initialized = await requireSdkInitialization(sdk);
2752
+ if (!initialized.ok) return initialized;
2361
2753
  const parent = dirname(root);
2362
2754
  await mkdir(parent, { recursive: true });
2363
2755
  let staging = await mkdtemp(
2364
2756
  resolve(parent, `.${basename(root)}.forgeax-staging-`)
2365
2757
  );
2366
2758
  const committedNames = [];
2759
+ let committed = false;
2367
2760
  try {
2368
2761
  for (const name of await readdir(template)) {
2369
2762
  await cp(resolve(template, name), resolve(staging, name), {
@@ -2374,14 +2767,12 @@ async function newCommand(options = {}) {
2374
2767
  }
2375
2768
  await copySdkSkills(sdk, staging);
2376
2769
  await installProjectSkills(staging, sdk.manifest);
2377
- await execFileAsync("pnpm", sdkInstallArgs(sdk.store), {
2378
- cwd: staging,
2379
- env: { ...process.env, CI: "true" },
2380
- maxBuffer: 16 * 1024 * 1024
2381
- });
2770
+ const store = sdk.store === void 0 ? void 0 : await canonicalProspectivePath(sdk.store);
2771
+ await configureSdkStore(staging, store);
2382
2772
  if (!targetExists) {
2383
2773
  await rename(staging, root);
2384
2774
  staging = void 0;
2775
+ committedNames.push(...await readdir(root));
2385
2776
  } else {
2386
2777
  for (const name of await readdir(staging)) {
2387
2778
  await rename(resolve(staging, name), resolve(root, name));
@@ -2390,29 +2781,57 @@ async function newCommand(options = {}) {
2390
2781
  await rm(staging, { recursive: true, force: true });
2391
2782
  staging = void 0;
2392
2783
  }
2784
+ committed = true;
2785
+ await execFileAsync2("pnpm", sdkProjectInstallArgs(store), {
2786
+ cwd: root,
2787
+ env: { ...process.env, CI: "true" },
2788
+ maxBuffer: 16 * 1024 * 1024
2789
+ });
2790
+ const sdkUpdate = await checkSdkUpdate(sdk.manifest.sdkVersion);
2393
2791
  return {
2394
2792
  ok: true,
2395
- value: { root, template: templateId, sdkVersion: sdk.manifest.sdkVersion }
2793
+ value: {
2794
+ root,
2795
+ template: templateId,
2796
+ sdkVersion: sdk.manifest.sdkVersion,
2797
+ onboarding: agentOnboarding(sdk, root),
2798
+ sdkUpdate
2799
+ }
2396
2800
  };
2397
2801
  } catch (cause) {
2398
2802
  if (staging !== void 0) await rm(staging, { recursive: true, force: true });
2399
- await Promise.all(
2400
- committedNames.map((name) => rm(resolve(root, name), { recursive: true, force: true }))
2401
- );
2803
+ if (committed) {
2804
+ if (!targetExists) {
2805
+ await rm(root, { recursive: true, force: true });
2806
+ return { ok: false, error: commandError(cause, "project-create-failed") };
2807
+ }
2808
+ const cleanupNames = /* @__PURE__ */ new Set([...committedNames, "node_modules"]);
2809
+ await Promise.all(
2810
+ [...cleanupNames].map(
2811
+ (name) => rm(resolve(root, name), { recursive: true, force: true })
2812
+ )
2813
+ );
2814
+ } else {
2815
+ await Promise.all(
2816
+ committedNames.map((name) => rm(resolve(root, name), { recursive: true, force: true }))
2817
+ );
2818
+ }
2402
2819
  return { ok: false, error: commandError(cause, "project-create-failed") };
2403
2820
  }
2404
2821
  } catch (cause) {
2405
2822
  return { ok: false, error: commandError(cause, "project-create-failed") };
2406
2823
  }
2407
2824
  }
2408
- var execFileAsync;
2825
+ var execFileAsync2;
2409
2826
  var init_bootstrap_commands = __esm({
2410
2827
  "src/bootstrap-commands.ts"() {
2411
2828
  init_init();
2412
2829
  init_project();
2413
2830
  init_sdk();
2831
+ init_sdk_bootstrap();
2832
+ init_sdk_update();
2414
2833
  init_skill_install();
2415
- execFileAsync = promisify(execFile);
2834
+ execFileAsync2 = promisify(execFile);
2416
2835
  }
2417
2836
  });
2418
2837
 
@@ -2471,7 +2890,7 @@ function withoutEntry(entries2, id) {
2471
2890
  }
2472
2891
  async function mutateDependency(root, action, dependency) {
2473
2892
  if (dependency === void 0) return;
2474
- await execFileAsync2("pnpm", [action, dependency], { cwd: root, maxBuffer: 16 * 1024 * 1024 });
2893
+ await execFileAsync3("pnpm", [action, dependency], { cwd: root, maxBuffer: 16 * 1024 * 1024 });
2475
2894
  }
2476
2895
  async function pluginInstallCommand(options) {
2477
2896
  const root = resolve(options.root ?? process.cwd());
@@ -2573,489 +2992,10 @@ async function pluginUninstallCommand(options) {
2573
2992
  };
2574
2993
  }
2575
2994
  }
2576
- var execFileAsync2;
2995
+ var execFileAsync3;
2577
2996
  var init_plugin_authoring = __esm({
2578
2997
  "src/plugin-authoring.ts"() {
2579
- execFileAsync2 = promisify(execFile);
2580
- }
2581
- });
2582
- var RhiError;
2583
- var init_dist2 = __esm({
2584
- "../rhi/dist/index.mjs"() {
2585
- RhiError = class extends Error {
2586
- code;
2587
- expected;
2588
- hint;
2589
- detail;
2590
- constructor(args) {
2591
- super(`[RhiError ${args.code}] expected: ${args.expected}; hint: ${args.hint}`);
2592
- this.name = "RhiError";
2593
- this.code = args.code;
2594
- this.expected = args.expected;
2595
- this.hint = args.hint;
2596
- this.detail = args.detail;
2597
- }
2598
- };
2599
- }
2600
- });
2601
- function readPassLabel(desc) {
2602
- if (desc && typeof desc.label === "string" && desc.label.length > 0) {
2603
- return desc.label;
2604
- }
2605
- return "<unnamed>";
2606
- }
2607
- function readRecord(handle) {
2608
- if (handle === null || typeof handle !== "object") return void 0;
2609
- const tagged = handle;
2610
- return tagged[BOOKKEEPING_KEY];
2611
- }
2612
- function isHandleDestroyed(handle) {
2613
- if (readRecord(handle)?.destroyed === true) {
2614
- return err(
2615
- new RhiError({
2616
- code: "destroy-after-destroy",
2617
- expected: "GPU buffer/texture handle has not been destroyed yet",
2618
- hint: "object already destroyed; track lifecycle in caller or check isDestroyed before re-destroy"
2619
- })
2620
- );
2621
- }
2622
- return ok(void 0);
2623
- }
2624
- function acquireCanvasContext(_canvas) {
2625
- return ok(new RhiNullCanvasContext());
2626
- }
2627
- function createShaderModule(_device, _desc) {
2628
- return Promise.resolve(ok({}));
2629
- }
2630
- function requestAdapter(_opts, _compatibleSurface) {
2631
- return Promise.resolve(ok(new RhiNullAdapter()));
2632
- }
2633
- var RhiNullRenderPassEncoder, RhiNullComputePassEncoder, DeviceCounter, RhiNullCommandEncoder, BOOKKEEPING_KEY, Bookkeeper, nextDeviceId, RhiNullDevice, EMPTY_FEATURES, EMPTY_LIMITS, NEVER, RhiNullQueue, RhiNullAdapter, RhiNullCanvasContext, rhi;
2634
- var init_dist3 = __esm({
2635
- "../rhi-null/dist/index.mjs"() {
2636
- init_dist2();
2637
- RhiNullRenderPassEncoder = class {
2638
- /** Number of draw* calls issued on this pass (AC-06 readback). */
2639
- drawCount = 0;
2640
- bindGroupCount = 0;
2641
- /** Most recent setVertexBuffer / setBindGroup ownership validation; ok unless
2642
- * a foreign handle was passed (AC-09 readback). */
2643
- lastValidation = ok(void 0);
2644
- bookkeeper;
2645
- counter;
2646
- passName;
2647
- constructor(bookkeeper, counter, passName) {
2648
- this.bookkeeper = bookkeeper;
2649
- this.counter = counter;
2650
- this.passName = passName;
2651
- }
2652
- setPipeline(_pipeline) {
2653
- }
2654
- setVertexBuffer(_slot, buffer, _offset, _size) {
2655
- this.lastValidation = this.bookkeeper.validateOwnership(buffer);
2656
- }
2657
- setIndexBuffer(_buffer, _format, _offset, _size) {
2658
- }
2659
- setBindGroup(_index, bindGroup, _dynamicOffsetsData, _dynamicOffsetsDataStart, _dynamicOffsetsDataLength) {
2660
- this.bindGroupCount++;
2661
- this.counter?.recordBindGroup();
2662
- this.lastValidation = this.bookkeeper.validateOwnership(bindGroup);
2663
- }
2664
- draw(_vertexCount, _instanceCount, _firstVertex, _firstInstance) {
2665
- this.drawCount++;
2666
- this.counter?.recordDraw();
2667
- }
2668
- drawIndexed(_indexCount, _instanceCount, _firstIndex, _baseVertex, _firstInstance) {
2669
- this.drawCount++;
2670
- this.counter?.recordDraw();
2671
- }
2672
- end() {
2673
- this.counter?.recordPassName(this.passName);
2674
- }
2675
- setViewport(_x, _y, _w, _h, _minDepth, _maxDepth) {
2676
- }
2677
- setScissorRect(_x, _y, _w, _h) {
2678
- }
2679
- setBlendConstant(_color) {
2680
- }
2681
- setStencilReference(_reference) {
2682
- }
2683
- drawIndirect(_indirectBuffer, _indirectOffset) {
2684
- this.drawCount++;
2685
- this.counter?.recordDraw();
2686
- }
2687
- drawIndexedIndirect(_indirectBuffer, _indirectOffset) {
2688
- this.drawCount++;
2689
- this.counter?.recordDraw();
2690
- }
2691
- pushDebugGroup(_groupLabel) {
2692
- }
2693
- popDebugGroup() {
2694
- }
2695
- insertDebugMarker(_markerLabel) {
2696
- }
2697
- executeBundles(_bundles) {
2698
- return ok(void 0);
2699
- }
2700
- beginOcclusionQuery(_queryIndex) {
2701
- return ok(void 0);
2702
- }
2703
- endOcclusionQuery() {
2704
- return ok(void 0);
2705
- }
2706
- };
2707
- RhiNullComputePassEncoder = class {
2708
- /** Number of dispatchWorkgroups calls issued on this pass (readback). */
2709
- dispatchCount = 0;
2710
- /** Most recent setBindGroup ownership validation (AC-09 readback). */
2711
- lastValidation = ok(void 0);
2712
- bookkeeper;
2713
- counter;
2714
- passName;
2715
- constructor(bookkeeper, counter, passName) {
2716
- this.bookkeeper = bookkeeper;
2717
- this.counter = counter;
2718
- this.passName = passName;
2719
- }
2720
- setPipeline(_pipeline) {
2721
- }
2722
- setBindGroup(_index, bindGroup, _dynamicOffsets) {
2723
- this.lastValidation = this.bookkeeper.validateOwnership(bindGroup);
2724
- }
2725
- dispatchWorkgroups(_x, _y, _z) {
2726
- this.dispatchCount++;
2727
- this.counter?.recordDispatch();
2728
- }
2729
- dispatchWorkgroupsIndirect(indirectBuffer, _indirectOffset) {
2730
- this.lastValidation = this.bookkeeper.validateOwnership(indirectBuffer);
2731
- this.dispatchCount++;
2732
- this.counter?.recordDispatch();
2733
- }
2734
- end() {
2735
- this.counter?.recordPassName(this.passName);
2736
- }
2737
- };
2738
- DeviceCounter = class {
2739
- constructor(device) {
2740
- this.device = device;
2741
- }
2742
- device;
2743
- recordDraw() {
2744
- this.device.totalDrawCount++;
2745
- }
2746
- recordDispatch() {
2747
- this.device.totalDispatchCount++;
2748
- }
2749
- recordBindGroup() {
2750
- this.device.totalBindGroupCount++;
2751
- }
2752
- recordPassName(name) {
2753
- this.device.framePassNames.push(name);
2754
- }
2755
- };
2756
- RhiNullCommandEncoder = class {
2757
- bookkeeper;
2758
- counter;
2759
- constructor(bookkeeper, device) {
2760
- this.bookkeeper = bookkeeper;
2761
- this.counter = new DeviceCounter(device);
2762
- }
2763
- beginRenderPass(desc) {
2764
- const label = readPassLabel(desc);
2765
- return new RhiNullRenderPassEncoder(this.bookkeeper, this.counter, label);
2766
- }
2767
- beginComputePass(desc) {
2768
- const label = readPassLabel(desc);
2769
- return new RhiNullComputePassEncoder(this.bookkeeper, this.counter, label);
2770
- }
2771
- copyBufferToBuffer(_source, _sourceOffsetOrDestination, _destinationOrSize, _destinationOffset, _size) {
2772
- }
2773
- copyBufferToTexture(_source, _destination, _copySize) {
2774
- }
2775
- copyTextureToBuffer(_source, _destination, _copySize) {
2776
- }
2777
- copyTextureToTexture(_source, _destination, _copySize) {
2778
- }
2779
- clearBuffer(_buffer, _offset, _size) {
2780
- }
2781
- resolveQuerySet(_querySet, _firstQuery, _queryCount, _destination, _destinationOffset) {
2782
- return ok(void 0);
2783
- }
2784
- writeTimestamp(_querySet, _queryIndex) {
2785
- }
2786
- pushDebugGroup(_groupLabel) {
2787
- }
2788
- popDebugGroup() {
2789
- }
2790
- insertDebugMarker(_markerLabel) {
2791
- }
2792
- finish() {
2793
- return ok(this.bookkeeper.register("CommandBuffer"));
2794
- }
2795
- };
2796
- BOOKKEEPING_KEY = /* @__PURE__ */ Symbol("forgeax-rhi-null-bookkeeping");
2797
- Bookkeeper = class {
2798
- deviceId;
2799
- nextHandleId = 0;
2800
- records = /* @__PURE__ */ new Map();
2801
- constructor(deviceId) {
2802
- this.deviceId = deviceId;
2803
- }
2804
- /**
2805
- * Register a freshly-minted handle of the given kind, returning a plain
2806
- * object that carries its ledger row. The caller casts the return value to
2807
- * the concrete brand (`as unknown as Buffer` etc.).
2808
- */
2809
- register(kind) {
2810
- const id = this.nextHandleId++;
2811
- const record = {
2812
- id,
2813
- kind,
2814
- destroyed: false,
2815
- sourceDeviceId: this.deviceId
2816
- };
2817
- this.records.set(id, record);
2818
- return { [BOOKKEEPING_KEY]: record };
2819
- }
2820
- /**
2821
- * Mark a handle destroyed. Fail-fasts on a second destroy
2822
- * ('destroy-after-destroy') or on a handle issued by a different device
2823
- * ('rhi-not-available'); otherwise flips the destroyed flag and returns ok.
2824
- */
2825
- destroy(handle) {
2826
- const ownership = this.validateOwnership(handle);
2827
- if (!ownership.ok) return ownership;
2828
- const destroyedCheck = isHandleDestroyed(handle);
2829
- if (!destroyedCheck.ok) return destroyedCheck;
2830
- ownership.value.destroyed = true;
2831
- return ok(void 0);
2832
- }
2833
- /**
2834
- * Report whether a handle has already been destroyed (true once destroy()
2835
- * has flipped its flag). Used by command-stream methods that must not
2836
- * consume a stale handle.
2837
- */
2838
- isDestroyed(handle) {
2839
- return readRecord(handle)?.destroyed === true;
2840
- }
2841
- /**
2842
- * Validate that a handle was issued by THIS device. Returns the ledger row on
2843
- * success so callers can mutate it (e.g. flip destroyed); returns
2844
- * 'rhi-not-available' for a foreign handle (AC-09 — no silent pass).
2845
- */
2846
- validateOwnership(handle) {
2847
- const record = readRecord(handle);
2848
- if (record === void 0 || record.sourceDeviceId !== this.deviceId) {
2849
- return err(
2850
- new RhiError({
2851
- code: "rhi-not-available",
2852
- expected: "handle was issued by this RhiNull device",
2853
- hint: "do not pass a handle created on a different RhiNull device into this device; create resources on the device they are used with"
2854
- })
2855
- );
2856
- }
2857
- return ok(record);
2858
- }
2859
- /**
2860
- * Return all ledger rows as a readonly array. M3 unit tests (w17) read this
2861
- * to assert create/destroy pairing, BGL/PSO shape counts, and resource
2862
- * lifecycle coverage (AC-05/06/07). Returns a snapshot of the current Map
2863
- * so callers can iterate without a stale reference after further mutations.
2864
- */
2865
- allRecords() {
2866
- return [...this.records.values()];
2867
- }
2868
- /** Report the total number of records in the ledger. */
2869
- recordCount() {
2870
- return this.records.size;
2871
- }
2872
- };
2873
- nextDeviceId = 0;
2874
- RhiNullDevice = class {
2875
- internalBookkeeper;
2876
- nullQueue;
2877
- encoderFactory;
2878
- /** Per-frame total draw count across all pass encoders executed this frame
2879
- * (aggregated by the command encoder on finish, then reset). M3 unit tests
2880
- * (w17) read this to assert draw count >= 1 (AC-06). */
2881
- totalDrawCount = 0;
2882
- /** Per-frame total direct and indirect compute dispatch count. */
2883
- totalDispatchCount = 0;
2884
- /** Per-frame total bind group set count (AC-06 / AC-05 readback). */
2885
- totalBindGroupCount = 0;
2886
- /** Per-frame pass names executed this frame, in schedule order (AC-04). */
2887
- framePassNames = [];
2888
- /** The per-device handle ledger — exposed so M3 tests can assert create/destroy
2889
- * pairing and BGL/PSO shape counts (AC-05/06/07). */
2890
- get bookkeeper() {
2891
- return this.internalBookkeeper;
2892
- }
2893
- constructor(queue, encoderFactory) {
2894
- this.internalBookkeeper = new Bookkeeper(nextDeviceId++);
2895
- this.nullQueue = queue;
2896
- this.encoderFactory = encoderFactory;
2897
- }
2898
- get caps() {
2899
- return {
2900
- backendKind: "null",
2901
- compute: true,
2902
- timestampQuery: false,
2903
- timestampPeriodNanoseconds: null,
2904
- indirectDrawing: true,
2905
- textureCompressionBc: false,
2906
- textureCompressionEtc2: false,
2907
- textureCompressionAstc: false,
2908
- // 3 wgpu-native-only reserved flags stay false on non-native backends
2909
- // (D-5); the headless backend is not a native runtime.
2910
- multiDrawIndirect: false,
2911
- pushConstants: false,
2912
- textureBindingArray: false,
2913
- samplerAliasing: true,
2914
- firstInstanceIndirect: true,
2915
- storageBuffer: true,
2916
- storageTexture: true,
2917
- rgba16floatRenderable: true,
2918
- rg11b10ufloatRenderable: true,
2919
- float32Filterable: true,
2920
- maxColorAttachments: 8
2921
- };
2922
- }
2923
- get features() {
2924
- return EMPTY_FEATURES;
2925
- }
2926
- get limits() {
2927
- return EMPTY_LIMITS;
2928
- }
2929
- get queue() {
2930
- return this.nullQueue;
2931
- }
2932
- // forgeax-async-whitelist: dom-native — spec `GPUDevice.lost` Promise
2933
- // passthrough. The headless backend never loses a device (no GPU), so the
2934
- // Promise stays unsettled for the lifetime of the device, mirroring a live
2935
- // device that never transitions to the lost state.
2936
- get lost() {
2937
- return NEVER;
2938
- }
2939
- createBuffer(_desc) {
2940
- return ok(this.internalBookkeeper.register("Buffer"));
2941
- }
2942
- createTexture(_desc) {
2943
- return ok(this.internalBookkeeper.register("Texture"));
2944
- }
2945
- destroyBuffer(buf) {
2946
- return this.internalBookkeeper.destroy(buf);
2947
- }
2948
- destroyQuerySet(querySet) {
2949
- return this.internalBookkeeper.destroy(querySet);
2950
- }
2951
- destroyTexture(tex) {
2952
- return this.internalBookkeeper.destroy(tex);
2953
- }
2954
- createTextureView(_texture, _desc) {
2955
- return ok(this.internalBookkeeper.register("TextureView"));
2956
- }
2957
- createSampler(_desc) {
2958
- return ok(this.internalBookkeeper.register("Sampler"));
2959
- }
2960
- createBindGroupLayout(_desc) {
2961
- return ok(this.internalBookkeeper.register("BindGroupLayout"));
2962
- }
2963
- createBindGroup(_desc) {
2964
- return ok(this.internalBookkeeper.register("BindGroup"));
2965
- }
2966
- createPipelineLayout(_desc) {
2967
- return ok(this.internalBookkeeper.register("PipelineLayout"));
2968
- }
2969
- createRenderPipeline(_desc) {
2970
- return ok(this.makePipeline("RenderPipeline"));
2971
- }
2972
- createComputePipeline(_desc) {
2973
- return ok(this.makePipeline("ComputePipeline"));
2974
- }
2975
- createQuerySet(desc) {
2976
- if (desc.type === "timestamp") {
2977
- return err(
2978
- new RhiError({
2979
- code: "feature-not-enabled",
2980
- expected: "caps.timestampQuery === true (timestamp-query feature)",
2981
- hint: "RhiNull is structural-only and cannot produce GPU timestamp ticks"
2982
- })
2983
- );
2984
- }
2985
- return ok(this.internalBookkeeper.register("QuerySet"));
2986
- }
2987
- createCommandEncoder(_desc) {
2988
- return ok(this.encoderFactory(this.internalBookkeeper, this));
2989
- }
2990
- /**
2991
- * Mint a pipeline handle whose object also carries the no-op
2992
- * `getBindGroupLayout(index)` ops method (D-2). The prod auto-layout path
2993
- * (debug-draw.ts) and the existing mock unit tests both call
2994
- * `pipeline.getBindGroupLayout(n)`; returning a legal BindGroupLayout brand
2995
- * (recorded in the ledger) keeps those consumers from crashing on a missing
2996
- * method.
2997
- */
2998
- makePipeline(kind) {
2999
- const handle = this.internalBookkeeper.register(kind);
3000
- const getBindGroupLayout = (_index) => this.internalBookkeeper.register("BindGroupLayout");
3001
- return Object.assign(handle, { getBindGroupLayout });
3002
- }
3003
- };
3004
- EMPTY_FEATURES = /* @__PURE__ */ new Set();
3005
- EMPTY_LIMITS = {};
3006
- NEVER = new Promise(() => {
3007
- });
3008
- RhiNullQueue = class {
3009
- writeBuffer(_buffer, _bufferOffset, _data, _dataOffset, _size) {
3010
- return ok(void 0);
3011
- }
3012
- writeTexture(_destination, _data, _dataLayout, _size) {
3013
- return ok(void 0);
3014
- }
3015
- copyExternalImageToTexture(_source, _destination, _copySize) {
3016
- return ok(void 0);
3017
- }
3018
- submit(_commandBuffers) {
3019
- return ok(void 0);
3020
- }
3021
- // forgeax-async-whitelist: dom-native — spec `GPUQueue.onSubmittedWorkDone`
3022
- // never rejects. The headless backend has no pending GPU work, so it resolves
3023
- // immediately (AC-12: read-back idioms must not hang).
3024
- onSubmittedWorkDone() {
3025
- return Promise.resolve(void 0);
3026
- }
3027
- };
3028
- RhiNullAdapter = class {
3029
- features = /* @__PURE__ */ new Set();
3030
- limits = {};
3031
- // forgeax-async-whitelist is not needed: this returns Promise<Result<...>>
3032
- // per the spec contract; never rejects.
3033
- requestDevice(_opts) {
3034
- const device = new RhiNullDevice(
3035
- new RhiNullQueue(),
3036
- (bookkeeper, dev) => new RhiNullCommandEncoder(bookkeeper, dev)
3037
- );
3038
- return Promise.resolve(ok(device));
3039
- }
3040
- };
3041
- RhiNullCanvasContext = class {
3042
- configure(_desc) {
3043
- return ok(void 0);
3044
- }
3045
- unconfigure() {
3046
- }
3047
- getConfiguration() {
3048
- return void 0;
3049
- }
3050
- getCurrentTexture() {
3051
- return ok({});
3052
- }
3053
- };
3054
- rhi = {
3055
- requestAdapter,
3056
- acquireCanvasContext,
3057
- createShaderModule
3058
- };
2998
+ execFileAsync3 = promisify(execFile);
3059
2999
  }
3060
3000
  });
3061
3001
  function createCliRhiDebugOperationContext() {
@@ -3063,7 +3003,7 @@ function createCliRhiDebugOperationContext() {
3063
3003
  captureFrame: async () => err(
3064
3004
  createRhiDebugError("capture-unavailable", {
3065
3005
  stage: "capture",
3066
- cause: "the standalone CLI has no live App capture provider"
3006
+ cause: "the standalone CLI has no live App capture provider; start a recorder-enabled live host and invoke its rhiCapture root"
3067
3007
  })
3068
3008
  ),
3069
3009
  async readArtifact(artifact) {
@@ -3126,7 +3066,6 @@ function createCliRhiDebugOperationContext() {
3126
3066
  }
3127
3067
  var init_cli_context = __esm({
3128
3068
  "src/rhi-debug/cli-context.ts"() {
3129
- init_dist3();
3130
3069
  }
3131
3070
  });
3132
3071
  function createRhiDebugOperationContext(host) {
@@ -3431,7 +3370,7 @@ async function sdkInstallCommand(options) {
3431
3370
  try {
3432
3371
  staging = await mkdtemp(resolve(parent, `.${basename(root)}.forgeax-sdk-staging-`));
3433
3372
  download = await mkdtemp(resolve(parent, `.${basename(root)}.forgeax-sdk-download-`));
3434
- await execFileAsync3(
3373
+ await execFileAsync4(
3435
3374
  process.env.FORGEAX_NPM_CLIENT ?? "npm",
3436
3375
  [
3437
3376
  "install",
@@ -3464,7 +3403,15 @@ async function sdkInstallCommand(options) {
3464
3403
  await rename(resolve(staging, name), resolve(root, name));
3465
3404
  }
3466
3405
  }
3467
- return { ok: true, value: { root, sdkVersion: version, source: "@forgeax/engine-sdk" } };
3406
+ return {
3407
+ ok: true,
3408
+ value: {
3409
+ root,
3410
+ sdkVersion: version,
3411
+ source: "@forgeax/engine-sdk",
3412
+ next: { cwd: root, argv: ["node", "./bin/forgeax.mjs", "init"] }
3413
+ }
3414
+ };
3468
3415
  } catch (cause) {
3469
3416
  return { ok: false, error: commandError(cause, "sdk-install-failed") };
3470
3417
  } finally {
@@ -3472,11 +3419,11 @@ async function sdkInstallCommand(options) {
3472
3419
  if (download !== void 0) await rm(download, { recursive: true, force: true });
3473
3420
  }
3474
3421
  }
3475
- var execFileAsync3;
3422
+ var execFileAsync4;
3476
3423
  var init_sdk_install = __esm({
3477
3424
  "src/sdk-install.ts"() {
3478
3425
  init_project();
3479
- execFileAsync3 = promisify(execFile);
3426
+ execFileAsync4 = promisify(execFile);
3480
3427
  }
3481
3428
  });
3482
3429
  async function shaderCheckCommand(options = {}) {
@@ -3547,6 +3494,21 @@ __export(commands_exports, {
3547
3494
  function runRhiDebugCommand(name, input, context) {
3548
3495
  return runRhiDebugOperation(name, input, context);
3549
3496
  }
3497
+ async function materializeViteDevPort(port) {
3498
+ if (port !== 0) return port;
3499
+ const reservation = createServer$1();
3500
+ await new Promise((resolveListen, rejectListen) => {
3501
+ reservation.once("error", rejectListen);
3502
+ reservation.listen(0, "127.0.0.1", resolveListen);
3503
+ });
3504
+ const address = reservation.address();
3505
+ const assigned = typeof address === "object" && address !== null ? address.port : 0;
3506
+ await new Promise((resolveClose, rejectClose) => {
3507
+ reservation.close((error) => error === void 0 ? resolveClose() : rejectClose(error));
3508
+ });
3509
+ if (assigned === 0) throw new Error("OS did not assign an ephemeral preview port");
3510
+ return assigned;
3511
+ }
3550
3512
  async function buildCommand(options = {}) {
3551
3513
  const facts = await readProjectFacts(options.root);
3552
3514
  if (!facts.ok) return facts;
@@ -3649,13 +3611,38 @@ async function devCommand(options = {}) {
3649
3611
  const facts = await readProjectFacts(options.root);
3650
3612
  if (!facts.ok) return facts;
3651
3613
  const previous = process.cwd();
3614
+ let server;
3652
3615
  try {
3616
+ const { createServer: createServer5 } = await import('vite');
3617
+ const { createViteConfig: createViteConfig2 } = await Promise.resolve().then(() => (init_host(), host_exports));
3618
+ const port = resolveProjectPort(options.port);
3619
+ const vitePort = { ...port, port: await materializeViteDevPort(port.port) };
3653
3620
  process.chdir(facts.value.root);
3654
- const server = await createServer(await createViteConfig(facts.value, "serve"));
3655
- await server.listen();
3621
+ server = await createServer5(
3622
+ await createViteConfig2(facts.value, "serve", "/", {
3623
+ server: vitePort
3624
+ })
3625
+ );
3626
+ await server.listen(vitePort.port);
3656
3627
  if (options.json !== true) server.printUrls();
3657
- return { ok: true, value: { root: facts.value.root, urls: server.resolvedUrls } };
3628
+ return {
3629
+ ok: true,
3630
+ value: {
3631
+ root: facts.value.root,
3632
+ urls: server.resolvedUrls,
3633
+ mode: "dev",
3634
+ serves: "source",
3635
+ capabilities: {
3636
+ "rhi.capture": {
3637
+ available: false,
3638
+ realm: "host",
3639
+ reason: "standalone-dev-server-has-no-live-app-cli-attachment"
3640
+ }
3641
+ }
3642
+ }
3643
+ };
3658
3644
  } catch (cause) {
3645
+ await server?.close();
3659
3646
  process.chdir(previous);
3660
3647
  return { ok: false, error: commandError(cause, "dev-server-failed") };
3661
3648
  }
@@ -3665,15 +3652,31 @@ async function previewCommand(options = {}) {
3665
3652
  const verified = await verifyDist(resolve(root, "dist"));
3666
3653
  if (!verified.ok) return verified;
3667
3654
  try {
3655
+ const port = resolveProjectPort(options.port);
3668
3656
  const server = await preview({
3669
3657
  root,
3670
3658
  configFile: false,
3671
3659
  base: verified.value.base,
3672
- preview: { open: false, port: 0, strictPort: false },
3660
+ preview: { open: false, ...port },
3673
3661
  build: { outDir: resolve(root, "dist") }
3674
3662
  });
3675
3663
  if (options.json !== true) server.printUrls();
3676
- return { ok: true, value: { root, urls: server.resolvedUrls } };
3664
+ return {
3665
+ ok: true,
3666
+ value: {
3667
+ root,
3668
+ urls: server.resolvedUrls,
3669
+ mode: "preview",
3670
+ serves: "dist",
3671
+ capabilities: {
3672
+ "rhi.capture": {
3673
+ available: false,
3674
+ realm: "host",
3675
+ reason: "static-dist-host-does-not-install-dev-capture"
3676
+ }
3677
+ }
3678
+ }
3679
+ };
3677
3680
  } catch (cause) {
3678
3681
  return { ok: false, error: commandError(cause, "preview-server-failed") };
3679
3682
  }
@@ -3723,6 +3726,7 @@ var init_commands = __esm({
3723
3726
  init_dist();
3724
3727
  init_host();
3725
3728
  init_project();
3729
+ init_types();
3726
3730
  init_assets();
3727
3731
  init_bootstrap_commands();
3728
3732
  init_plugin_authoring();
@@ -4117,7 +4121,7 @@ function hasStaticToolDeclarations(entry) {
4117
4121
  return Array.isArray(Reflect.get(entry.config, "tools"));
4118
4122
  }
4119
4123
  async function createViteModuleLoader(root) {
4120
- const server = await createServer({
4124
+ const server = await createServer$2({
4121
4125
  root,
4122
4126
  appType: "custom",
4123
4127
  configFile: false,
@@ -4319,7 +4323,7 @@ __export(contributions_exports, {
4319
4323
  createBuildContribution: () => createBuildContribution,
4320
4324
  createDefaultContributions: () => createDefaultContributions
4321
4325
  });
4322
- function commandFailure(error) {
4326
+ function commandFailure2(error) {
4323
4327
  return { ok: false, error: { ...error, detail: error.detail } };
4324
4328
  }
4325
4329
  function createBuildContribution(projectRoot = process.cwd()) {
@@ -4328,7 +4332,7 @@ function createBuildContribution(projectRoot = process.cwd()) {
4328
4332
  async (options) => {
4329
4333
  const { buildCommand: buildCommand2 } = await Promise.resolve().then(() => (init_commands(), commands_exports));
4330
4334
  const result = await buildCommand2({ ...options, root: options.root ?? projectRoot });
4331
- return result.ok ? result.value : commandFailure(result.error);
4335
+ return result.ok ? result.value : commandFailure2(result.error);
4332
4336
  }
4333
4337
  );
4334
4338
  }
@@ -4338,7 +4342,7 @@ function createAuthorContribution(projectRoot = process.cwd()) {
4338
4342
  async (options) => {
4339
4343
  const { pluginInstallCommand: pluginInstallCommand2 } = await Promise.resolve().then(() => (init_plugin_authoring(), plugin_authoring_exports));
4340
4344
  const result = await pluginInstallCommand2({ ...options, root: options.root ?? projectRoot });
4341
- return result.ok ? result.value : commandFailure(result.error);
4345
+ return result.ok ? result.value : commandFailure2(result.error);
4342
4346
  }
4343
4347
  );
4344
4348
  }
@@ -4499,9 +4503,11 @@ function createAdmissionReport(recipe, samples, thresholds = DEFAULT_ADMISSION_T
4499
4503
  async function runBenchmarkAdmission(options) {
4500
4504
  const sampleCount = options.thresholds?.sampleCountPerPhase ?? 30;
4501
4505
  const samples = [];
4502
- for (const phase of ["cold", "warm"]) {
4506
+ const phases = ["cold", "warm"];
4507
+ const modes = ["private", "service"];
4508
+ for (const phase of phases) {
4503
4509
  for (let index = 0; index < sampleCount; index += 1) {
4504
- for (const mode of ["private", "service"]) {
4510
+ for (const mode of modes) {
4505
4511
  const measurement = await options.measure(mode, phase, index);
4506
4512
  samples.push({
4507
4513
  mode,
@@ -4608,7 +4614,7 @@ function reply(response, status, payload) {
4608
4614
  async function createCarrierProviderService(options) {
4609
4615
  const host = options.host ?? "127.0.0.1";
4610
4616
  const bearerToken = options.machine.offer.bearerToken;
4611
- const server = createServer$1(async (request, response) => {
4617
+ const server = createServer(async (request, response) => {
4612
4618
  if (request.method !== "POST" || !request.url?.startsWith("/carrier/")) {
4613
4619
  reply(
4614
4620
  response,
@@ -4727,14 +4733,14 @@ async function createCarrierProviderService(options) {
4727
4733
  );
4728
4734
  }
4729
4735
  });
4730
- await new Promise((resolve15, reject) => {
4736
+ await new Promise((resolve16, reject) => {
4731
4737
  const onError = (error) => {
4732
4738
  server.off("listening", onListening);
4733
4739
  reject(error);
4734
4740
  };
4735
4741
  const onListening = () => {
4736
4742
  server.off("error", onError);
4737
- resolve15();
4743
+ resolve16();
4738
4744
  };
4739
4745
  server.once("error", onError);
4740
4746
  server.once("listening", onListening);
@@ -4752,7 +4758,7 @@ async function createCarrierProviderService(options) {
4752
4758
  if (closed) return;
4753
4759
  closed = true;
4754
4760
  await new Promise(
4755
- (resolve15, reject) => server.close((error) => error === void 0 ? resolve15() : reject(error))
4761
+ (resolve16, reject) => server.close((error) => error === void 0 ? resolve16() : reject(error))
4756
4762
  );
4757
4763
  }
4758
4764
  };
@@ -5044,16 +5050,16 @@ function projectUri(projectRoot, path) {
5044
5050
  return relative(projectRoot, path).split(sep).join("/");
5045
5051
  }
5046
5052
  async function allocateLoopbackPort() {
5047
- const probe = createServer$2();
5053
+ const probe = createServer$1();
5048
5054
  try {
5049
- await new Promise((resolve15, reject) => {
5055
+ await new Promise((resolve16, reject) => {
5050
5056
  const onError = (error) => {
5051
5057
  probe.off("listening", onListening);
5052
5058
  reject(error);
5053
5059
  };
5054
5060
  const onListening = () => {
5055
5061
  probe.off("error", onError);
5056
- resolve15();
5062
+ resolve16();
5057
5063
  };
5058
5064
  probe.once("error", onError);
5059
5065
  probe.once("listening", onListening);
@@ -5066,7 +5072,7 @@ async function allocateLoopbackPort() {
5066
5072
  return address.port;
5067
5073
  } finally {
5068
5074
  if (probe.listening) {
5069
- await new Promise((resolve15) => probe.close(() => resolve15()));
5075
+ await new Promise((resolve16) => probe.close(() => resolve16()));
5070
5076
  }
5071
5077
  }
5072
5078
  }
@@ -5223,7 +5229,7 @@ async function runBrowserPreviewHost(projectRoot, recipe, snapshot, runId, signa
5223
5229
  let server;
5224
5230
  try {
5225
5231
  const port = await allocateLoopbackPort();
5226
- server = await createServer({
5232
+ server = await createServer$2({
5227
5233
  ...config,
5228
5234
  cacheDir,
5229
5235
  logLevel: "silent",
@@ -5345,9 +5351,7 @@ async function runBrowserPreviewHost(projectRoot, recipe, snapshot, runId, signa
5345
5351
  );
5346
5352
  });
5347
5353
  await Promise.all(responseDiagnostics);
5348
- if (!captured.ok) {
5349
- return browserFailure("capture-run", captured.error, pageErrors);
5350
- }
5354
+ if (!captured.ok) return browserFailure("capture-run", captured.error, pageErrors);
5351
5355
  if (pageErrors.length > 0)
5352
5356
  return browserFailure("capture-page-runtime", pageErrors[0], pageErrors);
5353
5357
  const capturePng = await page.screenshot({ type: "png" });
@@ -5688,7 +5692,7 @@ function isAuthorized(request, bearerToken) {
5688
5692
  async function createAuthenticatedLoopbackService(options) {
5689
5693
  if (options.bearerToken.length < 8) throw new TypeError("service bearer token is too short");
5690
5694
  const host = options.host ?? "127.0.0.1";
5691
- const server = createServer$1(async (request, response) => {
5695
+ const server = createServer(async (request, response) => {
5692
5696
  if (request.method !== "POST" || request.url !== "/run") {
5693
5697
  reply2(response, 404, { error: "service route not found" });
5694
5698
  return;
@@ -5713,14 +5717,14 @@ async function createAuthenticatedLoopbackService(options) {
5713
5717
  reply2(response, 400, { error: message });
5714
5718
  }
5715
5719
  });
5716
- await new Promise((resolve15, reject) => {
5720
+ await new Promise((resolve16, reject) => {
5717
5721
  const onError = (error) => {
5718
5722
  server.off("listening", onListening);
5719
5723
  reject(error);
5720
5724
  };
5721
5725
  const onListening = () => {
5722
5726
  server.off("error", onError);
5723
- resolve15();
5727
+ resolve16();
5724
5728
  };
5725
5729
  server.once("error", onError);
5726
5730
  server.once("listening", onListening);
@@ -5728,7 +5732,7 @@ async function createAuthenticatedLoopbackService(options) {
5728
5732
  });
5729
5733
  const address = server.address();
5730
5734
  if (address === null || typeof address === "string") {
5731
- await new Promise((resolve15) => server.close(() => resolve15()));
5735
+ await new Promise((resolve16) => server.close(() => resolve16()));
5732
5736
  throw new Error("loopback service did not expose a TCP address");
5733
5737
  }
5734
5738
  const endpoint = `http://${host}:${address.port}/run`;
@@ -5743,14 +5747,17 @@ async function createAuthenticatedLoopbackService(options) {
5743
5747
  async close() {
5744
5748
  if (closed) return;
5745
5749
  closed = true;
5746
- await new Promise((resolve15, reject) => {
5747
- server.close((error) => error === void 0 ? resolve15() : reject(error));
5750
+ await new Promise((resolve16, reject) => {
5751
+ server.close((error) => error === void 0 ? resolve16() : reject(error));
5748
5752
  });
5749
5753
  transport.close();
5750
5754
  }
5751
5755
  };
5752
5756
  }
5753
5757
 
5754
- export { DEFAULT_ADMISSION_THRESHOLDS, RHI_DEBUG_OPERATION_MANIFEST, analyzePreviewArtifacts, assetAddCommand, assetInspectCommand, assetListCommand, assetVerifyCommand, bootstrapRealm, buildCommand, createAdmissionReport, createAuthenticatedLoopbackService, createAuthorContribution, createBuildContribution, createCarrierProvider, createCarrierProviderService, createCarrierRendezvous, createCliRhiDebugOperationContext, createDefaultContributions, createDevkitToolRuntime, createDomainPreviewContributions, createInitPlan, createMigrationRoster, createOfflineAnalysisContribution, createPreviewContribution, createPreviewContributions, createPreviewToolRuntime, createResourceProbe, createRhiDebugOperationContext, createServiceCache, createServiceExecutor, createToolClient, describeTool, devCommand, discoverRhiDebugOperations, doctorCommand, initCommand, listTools, materializeToolCatalog, newCommand, packageCommand, pluginInstallCommand, pluginUninstallCommand, previewCommand, readProjectFacts, rebuildToolCatalog, recoverRhiDebugError, renderRhiDebugHelp, resolveMigration, resolveRealmCapability, runBenchmarkAdmission, runCarrierPreviewRoute, runGenericTool, runLibraryTool, runNamedTool, runPreviewHost, runPrivateTool, runRhiDebugCommand, runRhiDebugOperation, sdkInstallCommand, shaderCheckCommand, skillInstallCommand, skillVerifyCommand, summarizeBenchmarkSamples, testCommand, verifyDist, writeDistManifest };
5758
+ // src/index.ts
5759
+ init_types();
5760
+
5761
+ export { DEFAULT_ADMISSION_THRESHOLDS, RHI_DEBUG_OPERATION_MANIFEST, analyzePreviewArtifacts, assetAddCommand, assetInspectCommand, assetListCommand, assetVerifyCommand, bootstrapRealm, buildCommand, createAdmissionReport, createAuthenticatedLoopbackService, createAuthorContribution, createBuildContribution, createCarrierProvider, createCarrierProviderService, createCarrierRendezvous, createCliRhiDebugOperationContext, createDefaultContributions, createDevkitToolRuntime, createDomainPreviewContributions, createInitPlan, createMigrationRoster, createOfflineAnalysisContribution, createPreviewContribution, createPreviewContributions, createPreviewToolRuntime, createResourceProbe, createRhiDebugOperationContext, createServiceCache, createServiceExecutor, createToolClient, describeTool, devCommand, discoverRhiDebugOperations, doctorCommand, initCommand, listTools, materializeToolCatalog, newCommand, packageCommand, pluginInstallCommand, pluginUninstallCommand, previewCommand, readProjectFacts, rebuildToolCatalog, recoverRhiDebugError, renderRhiDebugHelp, resolveMigration, resolveProjectPort, resolveRealmCapability, runBenchmarkAdmission, runCarrierPreviewRoute, runGenericTool, runLibraryTool, runNamedTool, runPreviewHost, runPrivateTool, runRhiDebugCommand, runRhiDebugOperation, sdkInstallCommand, shaderCheckCommand, skillInstallCommand, skillVerifyCommand, summarizeBenchmarkSamples, testCommand, verifyDist, writeDistManifest };
5755
5762
  //# sourceMappingURL=index.mjs.map
5756
5763
  //# sourceMappingURL=index.mjs.map