@forgeax/engine-devkit 0.1.4 → 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.
- package/README.md +19 -4
- package/dist/.tsbuildinfo +1 -1
- package/dist/__tests__/cli-output.unit.test.d.ts +2 -0
- package/dist/__tests__/cli-output.unit.test.d.ts.map +1 -0
- package/dist/__tests__/gltf-source-key-diagnostics.integration.test.d.ts +2 -0
- package/dist/__tests__/gltf-source-key-diagnostics.integration.test.d.ts.map +1 -0
- package/dist/__tests__/preview-port.integration.test.d.ts +2 -0
- package/dist/__tests__/preview-port.integration.test.d.ts.map +1 -0
- package/dist/__tests__/rhi-debug-build-graph.integration.test.d.ts +2 -0
- package/dist/__tests__/rhi-debug-build-graph.integration.test.d.ts.map +1 -0
- package/dist/__tests__/sdk-update.unit.test.d.ts +2 -0
- package/dist/__tests__/sdk-update.unit.test.d.ts.map +1 -0
- package/dist/assets.d.ts.map +1 -1
- package/dist/bootstrap-commands.d.ts.map +1 -1
- package/dist/cli-output.d.ts +4 -0
- package/dist/cli-output.d.ts.map +1 -0
- package/dist/cli.mjs +380 -567
- package/dist/cli.mjs.map +1 -1
- package/dist/commands.d.ts.map +1 -1
- package/dist/host/__tests__/bootstrap-root-owner.test-d.d.ts +2 -0
- package/dist/host/__tests__/bootstrap-root-owner.test-d.d.ts.map +1 -0
- package/dist/host/project-bootstrap.d.ts +4 -2
- package/dist/host/project-bootstrap.d.ts.map +1 -1
- package/dist/host/resource-bootstrap.d.ts +5 -3
- package/dist/host/resource-bootstrap.d.ts.map +1 -1
- package/dist/host.d.ts +2 -1
- package/dist/host.d.ts.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +303 -570
- package/dist/index.mjs.map +1 -1
- package/dist/project.d.ts +0 -1
- package/dist/project.d.ts.map +1 -1
- package/dist/rhi-debug/cli-context.d.ts.map +1 -1
- package/dist/sdk-bootstrap.d.ts +9 -0
- package/dist/sdk-bootstrap.d.ts.map +1 -1
- package/dist/sdk-cli.mjs +190 -24
- package/dist/sdk-cli.mjs.map +1 -1
- package/dist/sdk-install.d.ts.map +1 -1
- package/dist/sdk-update.d.ts +23 -0
- package/dist/sdk-update.d.ts.map +1 -0
- package/dist/tools/benchmark/__tests__/benchmark-vocabulary-owner.test-d.d.ts +2 -0
- package/dist/tools/benchmark/__tests__/benchmark-vocabulary-owner.test-d.d.ts.map +1 -0
- package/dist/tools/benchmark/harness.d.ts.map +1 -1
- package/dist/tools/browser-host.d.ts.map +1 -1
- package/dist/tools/native-preview.d.ts.map +1 -1
- package/dist/types.d.ts +7 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +29 -30
- package/dist/__tests__/tool-migration.test.d.ts +0 -2
- package/dist/__tests__/tool-migration.test.d.ts.map +0 -1
package/dist/cli.mjs
CHANGED
|
@@ -27,7 +27,9 @@ import { execFile } from 'child_process';
|
|
|
27
27
|
import { tmpdir } from 'os';
|
|
28
28
|
import { promisify } from 'util';
|
|
29
29
|
import { decodeTape, openReplay, buildFrameModel, createRhiDebugError } from '@forgeax/engine-rhi-debug';
|
|
30
|
-
import {
|
|
30
|
+
import { rhi, createShaderModule } from '@forgeax/engine-rhi-null';
|
|
31
|
+
import { build, preview, createServer as createServer$1 } from 'vite';
|
|
32
|
+
import { createServer } from 'net';
|
|
31
33
|
import { startVitest } from 'vitest/node';
|
|
32
34
|
import materialPreviewPlugin from '@forgeax/engine-preview/material';
|
|
33
35
|
import meshPreviewPlugin from '@forgeax/engine-preview/mesh';
|
|
@@ -299,16 +301,17 @@ var init_dist = __esm({
|
|
|
299
301
|
"src/dist.ts"() {
|
|
300
302
|
}
|
|
301
303
|
});
|
|
304
|
+
|
|
305
|
+
// src/host.ts
|
|
306
|
+
var host_exports = {};
|
|
307
|
+
__export(host_exports, {
|
|
308
|
+
createViteConfig: () => createViteConfig,
|
|
309
|
+
devKitDdcRoots: () => devKitDdcRoots,
|
|
310
|
+
ignoreDevKitCatalogPath: () => ignoreDevKitCatalogPath
|
|
311
|
+
});
|
|
302
312
|
function ignoreDevKitCatalogPath(path) {
|
|
303
313
|
const normalized = path.replace(/\\/g, "/");
|
|
304
|
-
|
|
305
|
-
if (!normalized.endsWith(".meta.json")) return false;
|
|
306
|
-
try {
|
|
307
|
-
const meta = JSON.parse(readFileSync(path, "utf8"));
|
|
308
|
-
return meta.importer === "game-default-target-profile";
|
|
309
|
-
} catch {
|
|
310
|
-
return false;
|
|
311
|
-
}
|
|
314
|
+
return normalized.split("/").includes("shaders");
|
|
312
315
|
}
|
|
313
316
|
function devKitDdcRoots(projectRoot2) {
|
|
314
317
|
return {
|
|
@@ -574,17 +577,20 @@ await app.pluginContext.plugin({
|
|
|
574
577
|
return `import { forgeaxBundlerAdapter } from 'virtual:forgeax/bundler';
|
|
575
578
|
import { createApp, createToolPreviewHost, createToolPreviewRecipe, fitToolPreviewCameraToAabb, gameHostPlugin, replayToolPreviewCapture } from '@forgeax/engine/app';
|
|
576
579
|
import { createCatalogSource } from '@forgeax/engine/assets-runtime';
|
|
580
|
+
import {
|
|
581
|
+
createRuntimeAssetImportTransport,
|
|
582
|
+
runtimeBinding,
|
|
583
|
+
} from 'virtual:forgeax/pack-runtime';
|
|
577
584
|
import { installCatalogLoader, projectPluginEntries } from '@forgeax/engine/plugin/loader';
|
|
578
585
|
import { audioPlugin } from '@forgeax/engine/audio';
|
|
579
586
|
import { webAudioPlugin } from '@forgeax/engine/audio-webaudio';
|
|
580
587
|
import { physicsPlugin } from '@forgeax/engine/physics';
|
|
581
588
|
import { skinningPlugin } from '@forgeax/engine/skinning';
|
|
582
589
|
import { createPrimitiveMesh } from '@forgeax/engine/geometry';
|
|
583
|
-
import { createDevImportTransport } from '@forgeax/engine/runtime';
|
|
584
590
|
import { mat4 } from '@forgeax/engine/math';
|
|
585
591
|
import { CAMERA_PROJECTION_ORTHOGRAPHIC, Camera, DirectionalLight, Materials, MeshFilter, MeshRenderer, Skylight, SkyboxBackground, TONEMAP_NONE, TONEMAP_REINHARD_EXTENDED } from '@forgeax/engine/render';
|
|
586
|
-
import {
|
|
587
|
-
import {
|
|
592
|
+
import { Transform } from '@forgeax/engine/scene';
|
|
593
|
+
import { type SceneAsset } from '@forgeax/engine/types';
|
|
588
594
|
import { ParticleEffectPlayer, vfxGpuEffectContribution } from '@forgeax/engine/vfx';
|
|
589
595
|
import { createVfxRuntimeHost } from '@forgeax/engine/vfx-render';
|
|
590
596
|
|
|
@@ -685,17 +691,39 @@ const resizeCanvas = () => {
|
|
|
685
691
|
const resizeObserver = new ResizeObserver(resizeCanvas);
|
|
686
692
|
resizeObserver.observe(canvas);
|
|
687
693
|
resizeCanvas();
|
|
688
|
-
const runtimeScopeBinding =
|
|
694
|
+
const runtimeScopeBinding = runtimeBinding;
|
|
695
|
+
if (runtimeScopeBinding === undefined) {
|
|
696
|
+
throw new Error('forgeax: Vite Pack runtime binding is required in the generated host');
|
|
697
|
+
}
|
|
689
698
|
const assetCatalog = createCatalogSource({
|
|
690
699
|
url: import.meta.env.DEV
|
|
691
700
|
? runtimeScopeBinding.catalogUrl
|
|
692
701
|
: new URL('pack-index.json', document.baseURI).href,
|
|
693
|
-
expectedScope: runtimeScopeBinding,
|
|
702
|
+
...(import.meta.env.DEV ? { expectedScope: runtimeScopeBinding } : {}),
|
|
694
703
|
});
|
|
695
704
|
const bundler = {
|
|
696
705
|
...forgeaxBundlerAdapter(),
|
|
697
|
-
...(import.meta.env.DEV
|
|
706
|
+
...(import.meta.env.DEV
|
|
707
|
+
? { importTransport: createRuntimeAssetImportTransport(runtimeScopeBinding) }
|
|
708
|
+
: {}),
|
|
698
709
|
};
|
|
710
|
+
const assetPreparation = new WeakMap();
|
|
711
|
+
|
|
712
|
+
function prepareAssetRegistry(assets) {
|
|
713
|
+
const existing = assetPreparation.get(assets);
|
|
714
|
+
if (existing !== undefined) return existing;
|
|
715
|
+
const pending = (async () => {
|
|
716
|
+
assets.configureRuntimeBinding(runtimeScopeBinding);
|
|
717
|
+
assets.setCatalogSource(assetCatalog);
|
|
718
|
+
if (vfxRuntimeHost !== undefined) {
|
|
719
|
+
assets.installDecoder(vfxGpuEffectContribution.kind, vfxGpuEffectContribution.decoder);
|
|
720
|
+
}
|
|
721
|
+
const catalog = await assets.enumerateCatalog();
|
|
722
|
+
if (!catalog.ok) throw catalog.error;
|
|
723
|
+
})();
|
|
724
|
+
assetPreparation.set(assets, pending);
|
|
725
|
+
return pending;
|
|
726
|
+
}
|
|
699
727
|
|
|
700
728
|
function previewStable(value) {
|
|
701
729
|
if (value instanceof ArrayBuffer) return 'ArrayBuffer:' + JSON.stringify(Array.from(new Uint8Array(value)));
|
|
@@ -756,13 +784,14 @@ async function previewOwnerFacts(assets, resource, payload) {
|
|
|
756
784
|
}
|
|
757
785
|
|
|
758
786
|
async function prepareProject(app) {
|
|
787
|
+
await prepareAssetRegistry(app.assets);
|
|
759
788
|
previewWorld = app.world;
|
|
760
789
|
const assets = app.assets;
|
|
761
790
|
let canonicalEnvironment;
|
|
762
791
|
if (resource !== undefined) {
|
|
763
792
|
if (resource.kind !== 'texture') {
|
|
764
793
|
if (canonicalEnvironmentGuid === null) throw new Error('canonical preview environment is unavailable');
|
|
765
|
-
const environment = await assets.
|
|
794
|
+
const environment = await assets.loadByGuid(assets.parseGuid(canonicalEnvironmentGuid));
|
|
766
795
|
if (!environment.ok) throw environment.error;
|
|
767
796
|
if (environment.value.kind !== 'equirect') {
|
|
768
797
|
throw new Error(\`canonical preview environment expected equirect, received \${environment.value.kind}\`);
|
|
@@ -777,7 +806,7 @@ if (resource !== undefined) {
|
|
|
777
806
|
const attached = await vfxRuntimeHost.attachWorld({ world: app.world, assets });
|
|
778
807
|
if (!attached.ok) throw attached.error;
|
|
779
808
|
}
|
|
780
|
-
const loaded = await assets.
|
|
809
|
+
const loaded = await assets.loadByGuid(assets.parseGuid(resource.guid));
|
|
781
810
|
if (!loaded.ok) throw loaded.error;
|
|
782
811
|
const payload = loaded.value;
|
|
783
812
|
if (resource.kind === 'vfx' && payload.kind !== 'particle-effect') {
|
|
@@ -790,7 +819,7 @@ if (resource !== undefined) {
|
|
|
790
819
|
const meshMaterialHandles = resource.kind === 'mesh' && payload.kind === 'mesh'
|
|
791
820
|
? await Promise.all(payload.materialSlots.map(async (slot) => {
|
|
792
821
|
if (slot.defaultMaterial === undefined) return 0;
|
|
793
|
-
const material = await assets.
|
|
822
|
+
const material = await assets.loadByGuid(assets.parseGuid(slot.defaultMaterial));
|
|
794
823
|
if (!material.ok) throw material.error;
|
|
795
824
|
if (material.value.kind !== 'material') {
|
|
796
825
|
throw new Error('mesh material slot resolved to a non-material asset');
|
|
@@ -920,15 +949,13 @@ let defaultScene;
|
|
|
920
949
|
let defaultSceneRoot;
|
|
921
950
|
const defaultSceneGuid = ${JSON.stringify(facts.defaultScene)};
|
|
922
951
|
if (defaultSceneGuid !== undefined) {
|
|
923
|
-
const loaded = await assets.
|
|
952
|
+
const loaded = await assets.loadByGuid<SceneAsset>(assets.parseGuid(defaultSceneGuid));
|
|
924
953
|
if (!loaded.ok) throw loaded.error;
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
const handle = app.world.allocSharedRef('SceneAsset', projected.value);
|
|
929
|
-
const instantiated = worldInstantiateScene(app.world, handle);
|
|
954
|
+
defaultScene = loaded.value;
|
|
955
|
+
const handle = app.world.allocSharedRef('SceneAsset', loaded.value);
|
|
956
|
+
const instantiated = assets.instantiate<SceneAsset>(handle, app.world);
|
|
930
957
|
if (!instantiated.ok) throw instantiated.error;
|
|
931
|
-
defaultSceneRoot = instantiated.value
|
|
958
|
+
defaultSceneRoot = instantiated.value;
|
|
932
959
|
}
|
|
933
960
|
const uiRoot = document.querySelector('#game-ui');
|
|
934
961
|
await app.pluginContext.plugin(gameHostPlugin({
|
|
@@ -967,8 +994,6 @@ if (query.has('forgeax-tool-replay')) {
|
|
|
967
994
|
...(resource === undefined ? {} : { resource }),
|
|
968
995
|
canvas,
|
|
969
996
|
app: {
|
|
970
|
-
...(assetCatalog === undefined ? {} : { assetCatalog }),
|
|
971
|
-
...(vfxRuntimeHost === undefined ? {} : { assetDecoders: [vfxGpuEffectContribution] }),
|
|
972
997
|
plugins: [${plugins.join(", ")}],
|
|
973
998
|
...(vfxRuntimeHost === undefined ? {} : { features: [vfxRuntimeHost.feature] }),
|
|
974
999
|
},
|
|
@@ -1081,8 +1106,6 @@ if (query.has('forgeax-tool-replay')) {
|
|
|
1081
1106
|
const result = await createApp(
|
|
1082
1107
|
canvas,
|
|
1083
1108
|
{
|
|
1084
|
-
...(assetCatalog === undefined ? {} : { assetCatalog }),
|
|
1085
|
-
...(vfxRuntimeHost === undefined ? {} : { assetDecoders: [vfxGpuEffectContribution] }),
|
|
1086
1109
|
plugins: [${plugins.join(", ")}],
|
|
1087
1110
|
...(pointerLockAllowed === undefined ? {} : { pointerLockAllowed }),
|
|
1088
1111
|
},
|
|
@@ -1230,9 +1253,9 @@ async function createViteConfig(facts, command2, base = "/", options = {}) {
|
|
|
1230
1253
|
plugins,
|
|
1231
1254
|
resolve: {
|
|
1232
1255
|
alias: consumerAliases,
|
|
1233
|
-
dedupe: ["@forgeax/engine
|
|
1256
|
+
dedupe: ["@forgeax/engine"]
|
|
1234
1257
|
},
|
|
1235
|
-
server: { fs: { allow: [facts.root, ...roots] } },
|
|
1258
|
+
server: { ...options.server, fs: { allow: [facts.root, ...roots] } },
|
|
1236
1259
|
build: {
|
|
1237
1260
|
target: "esnext",
|
|
1238
1261
|
outDir: options.outDir ?? resolve(facts.root, "dist"),
|
|
@@ -1256,7 +1279,7 @@ async function readJson(path) {
|
|
|
1256
1279
|
function firstUnsupportedStandaloneRealm(entries2, inheritedRealm = "engine") {
|
|
1257
1280
|
for (const entry of entries2) {
|
|
1258
1281
|
const realm = entry.realm ?? inheritedRealm ?? "engine";
|
|
1259
|
-
if (realm
|
|
1282
|
+
if (realm !== "engine") return { id: entry.id, realm };
|
|
1260
1283
|
if (entry.group === true) {
|
|
1261
1284
|
const unsupported = firstUnsupportedStandaloneRealm(
|
|
1262
1285
|
entry.config,
|
|
@@ -1374,13 +1397,10 @@ async function readProjectFacts(rootInput = process.cwd()) {
|
|
|
1374
1397
|
}
|
|
1375
1398
|
};
|
|
1376
1399
|
}
|
|
1377
|
-
function isCommandError(value) {
|
|
1378
|
-
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
1379
|
-
const candidate = value;
|
|
1380
|
-
return typeof candidate.code === "string" && typeof candidate.expected === "string" && typeof candidate.hint === "string" && candidate.detail !== null && typeof candidate.detail === "object" && !Array.isArray(candidate.detail);
|
|
1381
|
-
}
|
|
1382
1400
|
function commandError(cause, fallbackCode) {
|
|
1383
|
-
if (
|
|
1401
|
+
if (cause !== null && typeof cause === "object" && "code" in cause && "expected" in cause && "hint" in cause && "detail" in cause) {
|
|
1402
|
+
return cause;
|
|
1403
|
+
}
|
|
1384
1404
|
return {
|
|
1385
1405
|
code: fallbackCode,
|
|
1386
1406
|
expected: "the ForgeaX command to complete",
|
|
@@ -1392,16 +1412,33 @@ var init_project = __esm({
|
|
|
1392
1412
|
"src/project.ts"() {
|
|
1393
1413
|
}
|
|
1394
1414
|
});
|
|
1395
|
-
|
|
1396
|
-
|
|
1415
|
+
|
|
1416
|
+
// src/types.ts
|
|
1417
|
+
function parseProjectPortOption(value, provided) {
|
|
1418
|
+
if (!provided) return { ok: true, value: void 0 };
|
|
1419
|
+
if (value !== void 0 && /^(0|[1-9]\d*)$/.test(value)) {
|
|
1420
|
+
const port = Number(value);
|
|
1421
|
+
if (Number.isSafeInteger(port) && port <= 65535) return { ok: true, value: port };
|
|
1422
|
+
}
|
|
1423
|
+
return {
|
|
1424
|
+
ok: false,
|
|
1425
|
+
error: {
|
|
1426
|
+
code: "cli-parse-error",
|
|
1427
|
+
expected: "--port to be 0 or an integer from 1 to 65535",
|
|
1428
|
+
hint: "Omit --port for strict 5173, pass a positive port for a strict binding, or pass 0 for an OS-assigned port.",
|
|
1429
|
+
detail: { option: "--port", received: value ?? null }
|
|
1430
|
+
}
|
|
1431
|
+
};
|
|
1397
1432
|
}
|
|
1398
|
-
function
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1433
|
+
function resolveProjectPort(port) {
|
|
1434
|
+
return { port: port ?? 5173, strictPort: port !== 0 };
|
|
1435
|
+
}
|
|
1436
|
+
var init_types = __esm({
|
|
1437
|
+
"src/types.ts"() {
|
|
1403
1438
|
}
|
|
1404
|
-
|
|
1439
|
+
});
|
|
1440
|
+
function failure(error) {
|
|
1441
|
+
return { ok: false, error };
|
|
1405
1442
|
}
|
|
1406
1443
|
async function sourcesAt(path) {
|
|
1407
1444
|
const info = await stat(path);
|
|
@@ -1486,15 +1523,16 @@ async function addGltf(sourcePath, dryRun2) {
|
|
|
1486
1523
|
stderrWrite: (line) => stderr.push(line)
|
|
1487
1524
|
});
|
|
1488
1525
|
if (exitCode !== 0) {
|
|
1489
|
-
|
|
1490
|
-
stderr
|
|
1491
|
-
|
|
1526
|
+
try {
|
|
1527
|
+
return failure(JSON.parse(stderr.at(-1) ?? ""));
|
|
1528
|
+
} catch {
|
|
1529
|
+
return failure({
|
|
1492
1530
|
code: "asset-add-failed",
|
|
1493
1531
|
expected: "the glTF producer to create or reuse a valid sidecar",
|
|
1494
|
-
hint: "Inspect the glTF source and its external references."
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1532
|
+
hint: "Inspect the glTF source and its external references.",
|
|
1533
|
+
detail: { source: sourcePath, diagnostic: stderr.join("\n") }
|
|
1534
|
+
});
|
|
1535
|
+
}
|
|
1498
1536
|
}
|
|
1499
1537
|
return { ok: true, value: { source: sourcePath, metaPath: `${sourcePath}.meta.json` } };
|
|
1500
1538
|
}
|
|
@@ -1537,15 +1575,16 @@ async function entries(options) {
|
|
|
1537
1575
|
{ stdoutWrite: (line) => stdout.push(line), stderrWrite: (line) => stderr.push(line) }
|
|
1538
1576
|
);
|
|
1539
1577
|
if (!result.ok) {
|
|
1540
|
-
|
|
1541
|
-
stderr
|
|
1542
|
-
|
|
1578
|
+
try {
|
|
1579
|
+
return failure(JSON.parse(stderr.at(-1) ?? ""));
|
|
1580
|
+
} catch {
|
|
1581
|
+
return failure({
|
|
1543
1582
|
code: "asset-authority-invalid",
|
|
1544
1583
|
expected: "all asset roots and sidecars to pass the pack scanner",
|
|
1545
|
-
hint: "Repair the first invalid asset authority reported by the scanner."
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1584
|
+
hint: "Repair the first invalid asset authority reported by the scanner.",
|
|
1585
|
+
detail: { diagnostic: stderr.join("\n") }
|
|
1586
|
+
});
|
|
1587
|
+
}
|
|
1549
1588
|
}
|
|
1550
1589
|
return { ok: true, value: { facts: facts.value, entries: result.value } };
|
|
1551
1590
|
}
|
|
@@ -1630,7 +1669,6 @@ var init_package = __esm({
|
|
|
1630
1669
|
"@forgeax/engine-audio-webaudio": "workspace:*",
|
|
1631
1670
|
"@forgeax/engine-fbx": "workspace:*",
|
|
1632
1671
|
"@forgeax/engine-font": "workspace:*",
|
|
1633
|
-
"@forgeax/engine-geometry": "workspace:*",
|
|
1634
1672
|
"@forgeax/engine-gltf": "workspace:*",
|
|
1635
1673
|
"@forgeax/engine-image": "workspace:*",
|
|
1636
1674
|
"@forgeax/engine-input": "workspace:*",
|
|
@@ -1640,6 +1678,7 @@ var init_package = __esm({
|
|
|
1640
1678
|
"@forgeax/engine-preview": "workspace:*",
|
|
1641
1679
|
"@forgeax/engine-project": "workspace:*",
|
|
1642
1680
|
"@forgeax/engine-rhi-debug": "workspace:*",
|
|
1681
|
+
"@forgeax/engine-rhi-null": "workspace:*",
|
|
1643
1682
|
"@forgeax/engine-render": "workspace:*",
|
|
1644
1683
|
"@forgeax/engine-render-graph": "workspace:*",
|
|
1645
1684
|
"@forgeax/engine-runtime": "workspace:*",
|
|
@@ -1657,7 +1696,6 @@ var init_package = __esm({
|
|
|
1657
1696
|
vitest: "4.1.5"
|
|
1658
1697
|
},
|
|
1659
1698
|
devDependencies: {
|
|
1660
|
-
"@forgeax/engine-rhi-null": "workspace:*",
|
|
1661
1699
|
"@types/node": "^20.14.0"
|
|
1662
1700
|
},
|
|
1663
1701
|
forgeax: {
|
|
@@ -1871,6 +1909,20 @@ var init_sdk = __esm({
|
|
|
1871
1909
|
"src/sdk.ts"() {
|
|
1872
1910
|
}
|
|
1873
1911
|
});
|
|
1912
|
+
function agentOnboarding(sdk, projectRoot2) {
|
|
1913
|
+
const root = projectRoot2 ?? sdk.root;
|
|
1914
|
+
return {
|
|
1915
|
+
read: [
|
|
1916
|
+
resolve(root, "AGENTS.md"),
|
|
1917
|
+
resolve(root, "skills", "forgeax-engine-sdk", "SKILL.md"),
|
|
1918
|
+
resolve(root, "skills", "forgeax-engine-sdk", "references", "feature-catalog.md")
|
|
1919
|
+
],
|
|
1920
|
+
next: projectRoot2 === void 0 ? {
|
|
1921
|
+
cwd: root,
|
|
1922
|
+
argv: ["node", "./bin/forgeax.mjs", "new", "../my-game"]
|
|
1923
|
+
} : { cwd: root, argv: ["pnpm", "exec", "forgeax", "list", "--json"] }
|
|
1924
|
+
};
|
|
1925
|
+
}
|
|
1874
1926
|
function sdkInitPath(sdk) {
|
|
1875
1927
|
return resolve(sdk.root, ...SDK_INIT_PATH);
|
|
1876
1928
|
}
|
|
@@ -1885,7 +1937,7 @@ function sdkProjectInstallArgs(store) {
|
|
|
1885
1937
|
"--side-effects-cache=true",
|
|
1886
1938
|
"--child-concurrency=1"
|
|
1887
1939
|
];
|
|
1888
|
-
return store === void 0 ? common : [...common, "--offline", "--trust-lockfile", "--store-dir", store];
|
|
1940
|
+
return store === void 0 ? common : [...common, "--offline", "--config.trust-lockfile=true", "--store-dir", store];
|
|
1889
1941
|
}
|
|
1890
1942
|
function sdkBootstrapInstallArgs(store) {
|
|
1891
1943
|
const common = [
|
|
@@ -1894,7 +1946,7 @@ function sdkBootstrapInstallArgs(store) {
|
|
|
1894
1946
|
"--child-concurrency=1",
|
|
1895
1947
|
"--side-effects-cache=true"
|
|
1896
1948
|
];
|
|
1897
|
-
return store === void 0 ? common : [...common, "--offline", "--trust-lockfile", "--store-dir", store];
|
|
1949
|
+
return store === void 0 ? common : [...common, "--offline", "--config.trust-lockfile=true", "--store-dir", store];
|
|
1898
1950
|
}
|
|
1899
1951
|
function supportedPnpm(version) {
|
|
1900
1952
|
const [major = 0, minor = 0] = version.split(".").map(Number);
|
|
@@ -1984,7 +2036,8 @@ async function sdkInitCommand(sdk, options = {}) {
|
|
|
1984
2036
|
const report = {
|
|
1985
2037
|
...state,
|
|
1986
2038
|
root: sdk.root,
|
|
1987
|
-
store: sdk.store === void 0 ? "registry" : "offline"
|
|
2039
|
+
store: sdk.store === void 0 ? "registry" : "offline",
|
|
2040
|
+
onboarding: agentOnboarding(sdk)
|
|
1988
2041
|
};
|
|
1989
2042
|
if (options.dryRun === true || options.install === false) return { ok: true, value: report };
|
|
1990
2043
|
let staging;
|
|
@@ -2019,6 +2072,107 @@ var init_sdk_bootstrap = __esm({
|
|
|
2019
2072
|
SDK_INIT_SCHEMA_VERSION = "1.0.0";
|
|
2020
2073
|
}
|
|
2021
2074
|
});
|
|
2075
|
+
|
|
2076
|
+
// src/sdk-update.ts
|
|
2077
|
+
function parseVersion(version) {
|
|
2078
|
+
const match = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/.exec(
|
|
2079
|
+
version
|
|
2080
|
+
);
|
|
2081
|
+
if (match === null) return void 0;
|
|
2082
|
+
const major = match[1];
|
|
2083
|
+
const minor = match[2];
|
|
2084
|
+
const patch = match[3];
|
|
2085
|
+
if (major === void 0 || minor === void 0 || patch === void 0) return void 0;
|
|
2086
|
+
const prerelease = match[4]?.split(".");
|
|
2087
|
+
if (prerelease?.some(
|
|
2088
|
+
(identifier) => identifier.length === 0 || /^\d+$/.test(identifier) && /^0\d+/.test(identifier)
|
|
2089
|
+
)) {
|
|
2090
|
+
return void 0;
|
|
2091
|
+
}
|
|
2092
|
+
return {
|
|
2093
|
+
core: [major, minor, patch],
|
|
2094
|
+
...prerelease === void 0 ? {} : { prerelease }
|
|
2095
|
+
};
|
|
2096
|
+
}
|
|
2097
|
+
function compareNumericIdentifier(current, latest) {
|
|
2098
|
+
if (current.length !== latest.length) return latest.length > current.length ? 1 : -1;
|
|
2099
|
+
if (current === latest) return 0;
|
|
2100
|
+
return latest > current ? 1 : -1;
|
|
2101
|
+
}
|
|
2102
|
+
function comparePrerelease(current, latest) {
|
|
2103
|
+
const length = Math.max(current.length, latest.length);
|
|
2104
|
+
for (let index = 0; index < length; index += 1) {
|
|
2105
|
+
const currentIdentifier = current[index];
|
|
2106
|
+
const latestIdentifier = latest[index];
|
|
2107
|
+
if (currentIdentifier === void 0) return 1;
|
|
2108
|
+
if (latestIdentifier === void 0) return -1;
|
|
2109
|
+
if (currentIdentifier === latestIdentifier) continue;
|
|
2110
|
+
const currentNumeric = /^\d+$/.test(currentIdentifier);
|
|
2111
|
+
const latestNumeric = /^\d+$/.test(latestIdentifier);
|
|
2112
|
+
if (currentNumeric && latestNumeric) {
|
|
2113
|
+
return compareNumericIdentifier(currentIdentifier, latestIdentifier);
|
|
2114
|
+
}
|
|
2115
|
+
if (currentNumeric !== latestNumeric) return latestNumeric ? -1 : 1;
|
|
2116
|
+
return latestIdentifier > currentIdentifier ? 1 : -1;
|
|
2117
|
+
}
|
|
2118
|
+
return 0;
|
|
2119
|
+
}
|
|
2120
|
+
function newerSdkVersion(currentVersion, latestVersion) {
|
|
2121
|
+
const current = parseVersion(currentVersion);
|
|
2122
|
+
const latest = parseVersion(latestVersion);
|
|
2123
|
+
if (current === void 0 || latest === void 0) return false;
|
|
2124
|
+
for (let index = 0; index < current.core.length; index += 1) {
|
|
2125
|
+
const precedence = compareNumericIdentifier(
|
|
2126
|
+
current.core[index] ?? "0",
|
|
2127
|
+
latest.core[index] ?? "0"
|
|
2128
|
+
);
|
|
2129
|
+
if (precedence !== 0) return precedence > 0;
|
|
2130
|
+
}
|
|
2131
|
+
if (current.prerelease !== void 0 && latest.prerelease === void 0) return true;
|
|
2132
|
+
if (current.prerelease === void 0 || latest.prerelease === void 0) return false;
|
|
2133
|
+
return comparePrerelease(current.prerelease, latest.prerelease) > 0;
|
|
2134
|
+
}
|
|
2135
|
+
function offline() {
|
|
2136
|
+
return ["1", "true"].includes((process.env.npm_config_offline ?? "").toLowerCase());
|
|
2137
|
+
}
|
|
2138
|
+
async function checkSdkUpdate(currentVersion) {
|
|
2139
|
+
if (process.env.FORGEAX_DISABLE_UPDATE_CHECK === "1") {
|
|
2140
|
+
return { status: "skipped", currentVersion, reason: "disabled" };
|
|
2141
|
+
}
|
|
2142
|
+
if (offline()) return { status: "skipped", currentVersion, reason: "offline" };
|
|
2143
|
+
try {
|
|
2144
|
+
const registry = process.env.npm_config_registry ?? "https://registry.npmjs.org/";
|
|
2145
|
+
const response = await fetch(
|
|
2146
|
+
new URL("@forgeax%2Fengine-sdk", `${registry.replace(/\/$/, "")}/`),
|
|
2147
|
+
{
|
|
2148
|
+
headers: { accept: "application/vnd.npm.install-v1+json" },
|
|
2149
|
+
signal: AbortSignal.timeout(2e3)
|
|
2150
|
+
}
|
|
2151
|
+
);
|
|
2152
|
+
if (!response.ok) throw new Error(`registry-http-${response.status}`);
|
|
2153
|
+
const metadata = await response.json();
|
|
2154
|
+
const latestVersion = metadata["dist-tags"]?.latest;
|
|
2155
|
+
if (typeof latestVersion !== "string") throw new Error("registry-latest-version-missing");
|
|
2156
|
+
return newerSdkVersion(currentVersion, latestVersion) ? {
|
|
2157
|
+
status: "available",
|
|
2158
|
+
currentVersion,
|
|
2159
|
+
latestVersion,
|
|
2160
|
+
migrationRisk: UPDATE_MIGRATION_RISK
|
|
2161
|
+
} : { status: "current", currentVersion, latestVersion };
|
|
2162
|
+
} catch (cause) {
|
|
2163
|
+
return {
|
|
2164
|
+
status: "unavailable",
|
|
2165
|
+
currentVersion,
|
|
2166
|
+
reason: cause instanceof Error ? cause.message : String(cause)
|
|
2167
|
+
};
|
|
2168
|
+
}
|
|
2169
|
+
}
|
|
2170
|
+
var UPDATE_MIGRATION_RISK;
|
|
2171
|
+
var init_sdk_update = __esm({
|
|
2172
|
+
"src/sdk-update.ts"() {
|
|
2173
|
+
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.";
|
|
2174
|
+
}
|
|
2175
|
+
});
|
|
2022
2176
|
function slash(path) {
|
|
2023
2177
|
return path.split(sep).join("/");
|
|
2024
2178
|
}
|
|
@@ -2646,9 +2800,16 @@ async function newCommand(options = {}) {
|
|
|
2646
2800
|
env: { ...process.env, CI: "true" },
|
|
2647
2801
|
maxBuffer: 16 * 1024 * 1024
|
|
2648
2802
|
});
|
|
2803
|
+
const sdkUpdate = await checkSdkUpdate(sdk.manifest.sdkVersion);
|
|
2649
2804
|
return {
|
|
2650
2805
|
ok: true,
|
|
2651
|
-
value: {
|
|
2806
|
+
value: {
|
|
2807
|
+
root,
|
|
2808
|
+
template: templateId,
|
|
2809
|
+
sdkVersion: sdk.manifest.sdkVersion,
|
|
2810
|
+
onboarding: agentOnboarding(sdk, root),
|
|
2811
|
+
sdkUpdate
|
|
2812
|
+
}
|
|
2652
2813
|
};
|
|
2653
2814
|
} catch (cause) {
|
|
2654
2815
|
if (staging !== void 0) await rm(staging, { recursive: true, force: true });
|
|
@@ -2681,6 +2842,7 @@ var init_bootstrap_commands = __esm({
|
|
|
2681
2842
|
init_project();
|
|
2682
2843
|
init_sdk();
|
|
2683
2844
|
init_sdk_bootstrap();
|
|
2845
|
+
init_sdk_update();
|
|
2684
2846
|
init_skill_install();
|
|
2685
2847
|
execFileAsync2 = promisify(execFile);
|
|
2686
2848
|
}
|
|
@@ -2849,491 +3011,12 @@ var init_plugin_authoring = __esm({
|
|
|
2849
3011
|
execFileAsync3 = promisify(execFile);
|
|
2850
3012
|
}
|
|
2851
3013
|
});
|
|
2852
|
-
var RhiError;
|
|
2853
|
-
var init_dist2 = __esm({
|
|
2854
|
-
"../rhi/dist/index.mjs"() {
|
|
2855
|
-
RhiError = class extends Error {
|
|
2856
|
-
code;
|
|
2857
|
-
expected;
|
|
2858
|
-
hint;
|
|
2859
|
-
detail;
|
|
2860
|
-
constructor(args2) {
|
|
2861
|
-
super(`[RhiError ${args2.code}] expected: ${args2.expected}; hint: ${args2.hint}`);
|
|
2862
|
-
this.name = "RhiError";
|
|
2863
|
-
this.code = args2.code;
|
|
2864
|
-
this.expected = args2.expected;
|
|
2865
|
-
this.hint = args2.hint;
|
|
2866
|
-
this.detail = args2.detail;
|
|
2867
|
-
}
|
|
2868
|
-
};
|
|
2869
|
-
}
|
|
2870
|
-
});
|
|
2871
|
-
function readPassLabel(desc) {
|
|
2872
|
-
if (desc && typeof desc.label === "string" && desc.label.length > 0) {
|
|
2873
|
-
return desc.label;
|
|
2874
|
-
}
|
|
2875
|
-
return "<unnamed>";
|
|
2876
|
-
}
|
|
2877
|
-
function readRecord(handle) {
|
|
2878
|
-
if (handle === null || typeof handle !== "object") return void 0;
|
|
2879
|
-
const tagged = handle;
|
|
2880
|
-
return tagged[BOOKKEEPING_KEY];
|
|
2881
|
-
}
|
|
2882
|
-
function isHandleDestroyed(handle) {
|
|
2883
|
-
if (readRecord(handle)?.destroyed === true) {
|
|
2884
|
-
return err(
|
|
2885
|
-
new RhiError({
|
|
2886
|
-
code: "destroy-after-destroy",
|
|
2887
|
-
expected: "GPU buffer/texture handle has not been destroyed yet",
|
|
2888
|
-
hint: "object already destroyed; track lifecycle in caller or check isDestroyed before re-destroy"
|
|
2889
|
-
})
|
|
2890
|
-
);
|
|
2891
|
-
}
|
|
2892
|
-
return ok(void 0);
|
|
2893
|
-
}
|
|
2894
|
-
function acquireCanvasContext(_canvas) {
|
|
2895
|
-
return ok(new RhiNullCanvasContext());
|
|
2896
|
-
}
|
|
2897
|
-
function createShaderModule(_device, _desc) {
|
|
2898
|
-
return Promise.resolve(ok({}));
|
|
2899
|
-
}
|
|
2900
|
-
function requestAdapter(_opts, _compatibleSurface) {
|
|
2901
|
-
return Promise.resolve(ok(new RhiNullAdapter()));
|
|
2902
|
-
}
|
|
2903
|
-
var RhiNullRenderPassEncoder, RhiNullComputePassEncoder, DeviceCounter, RhiNullCommandEncoder, BOOKKEEPING_KEY, Bookkeeper, nextDeviceId, RhiNullDevice, EMPTY_FEATURES, EMPTY_LIMITS, NEVER, RhiNullQueue, RhiNullAdapter, RhiNullCanvasContext, rhi;
|
|
2904
|
-
var init_dist3 = __esm({
|
|
2905
|
-
"../rhi-null/dist/index.mjs"() {
|
|
2906
|
-
init_dist2();
|
|
2907
|
-
RhiNullRenderPassEncoder = class {
|
|
2908
|
-
/** Number of draw* calls issued on this pass (AC-06 readback). */
|
|
2909
|
-
drawCount = 0;
|
|
2910
|
-
bindGroupCount = 0;
|
|
2911
|
-
/** Most recent setVertexBuffer / setBindGroup ownership validation; ok unless
|
|
2912
|
-
* a foreign handle was passed (AC-09 readback). */
|
|
2913
|
-
lastValidation = ok(void 0);
|
|
2914
|
-
bookkeeper;
|
|
2915
|
-
counter;
|
|
2916
|
-
passName;
|
|
2917
|
-
constructor(bookkeeper, counter, passName) {
|
|
2918
|
-
this.bookkeeper = bookkeeper;
|
|
2919
|
-
this.counter = counter;
|
|
2920
|
-
this.passName = passName;
|
|
2921
|
-
}
|
|
2922
|
-
setPipeline(_pipeline) {
|
|
2923
|
-
}
|
|
2924
|
-
setVertexBuffer(_slot, buffer, _offset, _size) {
|
|
2925
|
-
this.lastValidation = this.bookkeeper.validateOwnership(buffer);
|
|
2926
|
-
}
|
|
2927
|
-
setIndexBuffer(_buffer, _format, _offset, _size) {
|
|
2928
|
-
}
|
|
2929
|
-
setBindGroup(_index, bindGroup, _dynamicOffsetsData, _dynamicOffsetsDataStart, _dynamicOffsetsDataLength) {
|
|
2930
|
-
this.bindGroupCount++;
|
|
2931
|
-
this.counter?.recordBindGroup();
|
|
2932
|
-
this.lastValidation = this.bookkeeper.validateOwnership(bindGroup);
|
|
2933
|
-
}
|
|
2934
|
-
draw(_vertexCount, _instanceCount, _firstVertex, _firstInstance) {
|
|
2935
|
-
this.drawCount++;
|
|
2936
|
-
this.counter?.recordDraw();
|
|
2937
|
-
}
|
|
2938
|
-
drawIndexed(_indexCount, _instanceCount, _firstIndex, _baseVertex, _firstInstance) {
|
|
2939
|
-
this.drawCount++;
|
|
2940
|
-
this.counter?.recordDraw();
|
|
2941
|
-
}
|
|
2942
|
-
end() {
|
|
2943
|
-
this.counter?.recordPassName(this.passName);
|
|
2944
|
-
}
|
|
2945
|
-
setViewport(_x, _y, _w, _h, _minDepth, _maxDepth) {
|
|
2946
|
-
}
|
|
2947
|
-
setScissorRect(_x, _y, _w, _h) {
|
|
2948
|
-
}
|
|
2949
|
-
setBlendConstant(_color) {
|
|
2950
|
-
}
|
|
2951
|
-
setStencilReference(_reference) {
|
|
2952
|
-
}
|
|
2953
|
-
drawIndirect(_indirectBuffer, _indirectOffset) {
|
|
2954
|
-
this.drawCount++;
|
|
2955
|
-
this.counter?.recordDraw();
|
|
2956
|
-
}
|
|
2957
|
-
drawIndexedIndirect(_indirectBuffer, _indirectOffset) {
|
|
2958
|
-
this.drawCount++;
|
|
2959
|
-
this.counter?.recordDraw();
|
|
2960
|
-
}
|
|
2961
|
-
pushDebugGroup(_groupLabel) {
|
|
2962
|
-
}
|
|
2963
|
-
popDebugGroup() {
|
|
2964
|
-
}
|
|
2965
|
-
insertDebugMarker(_markerLabel) {
|
|
2966
|
-
}
|
|
2967
|
-
executeBundles(_bundles) {
|
|
2968
|
-
return ok(void 0);
|
|
2969
|
-
}
|
|
2970
|
-
beginOcclusionQuery(_queryIndex) {
|
|
2971
|
-
return ok(void 0);
|
|
2972
|
-
}
|
|
2973
|
-
endOcclusionQuery() {
|
|
2974
|
-
return ok(void 0);
|
|
2975
|
-
}
|
|
2976
|
-
};
|
|
2977
|
-
RhiNullComputePassEncoder = class {
|
|
2978
|
-
/** Number of dispatchWorkgroups calls issued on this pass (readback). */
|
|
2979
|
-
dispatchCount = 0;
|
|
2980
|
-
/** Most recent setBindGroup ownership validation (AC-09 readback). */
|
|
2981
|
-
lastValidation = ok(void 0);
|
|
2982
|
-
bookkeeper;
|
|
2983
|
-
counter;
|
|
2984
|
-
passName;
|
|
2985
|
-
constructor(bookkeeper, counter, passName) {
|
|
2986
|
-
this.bookkeeper = bookkeeper;
|
|
2987
|
-
this.counter = counter;
|
|
2988
|
-
this.passName = passName;
|
|
2989
|
-
}
|
|
2990
|
-
setPipeline(_pipeline) {
|
|
2991
|
-
}
|
|
2992
|
-
setBindGroup(_index, bindGroup, _dynamicOffsets) {
|
|
2993
|
-
this.lastValidation = this.bookkeeper.validateOwnership(bindGroup);
|
|
2994
|
-
}
|
|
2995
|
-
dispatchWorkgroups(_x, _y, _z) {
|
|
2996
|
-
this.dispatchCount++;
|
|
2997
|
-
this.counter?.recordDispatch();
|
|
2998
|
-
}
|
|
2999
|
-
dispatchWorkgroupsIndirect(indirectBuffer, _indirectOffset) {
|
|
3000
|
-
this.lastValidation = this.bookkeeper.validateOwnership(indirectBuffer);
|
|
3001
|
-
this.dispatchCount++;
|
|
3002
|
-
this.counter?.recordDispatch();
|
|
3003
|
-
}
|
|
3004
|
-
end() {
|
|
3005
|
-
this.counter?.recordPassName(this.passName);
|
|
3006
|
-
}
|
|
3007
|
-
};
|
|
3008
|
-
DeviceCounter = class {
|
|
3009
|
-
constructor(device) {
|
|
3010
|
-
this.device = device;
|
|
3011
|
-
}
|
|
3012
|
-
device;
|
|
3013
|
-
recordDraw() {
|
|
3014
|
-
this.device.totalDrawCount++;
|
|
3015
|
-
}
|
|
3016
|
-
recordDispatch() {
|
|
3017
|
-
this.device.totalDispatchCount++;
|
|
3018
|
-
}
|
|
3019
|
-
recordBindGroup() {
|
|
3020
|
-
this.device.totalBindGroupCount++;
|
|
3021
|
-
}
|
|
3022
|
-
recordPassName(name) {
|
|
3023
|
-
this.device.framePassNames.push(name);
|
|
3024
|
-
}
|
|
3025
|
-
};
|
|
3026
|
-
RhiNullCommandEncoder = class {
|
|
3027
|
-
bookkeeper;
|
|
3028
|
-
counter;
|
|
3029
|
-
constructor(bookkeeper, device) {
|
|
3030
|
-
this.bookkeeper = bookkeeper;
|
|
3031
|
-
this.counter = new DeviceCounter(device);
|
|
3032
|
-
}
|
|
3033
|
-
beginRenderPass(desc) {
|
|
3034
|
-
const label = readPassLabel(desc);
|
|
3035
|
-
return new RhiNullRenderPassEncoder(this.bookkeeper, this.counter, label);
|
|
3036
|
-
}
|
|
3037
|
-
beginComputePass(desc) {
|
|
3038
|
-
const label = readPassLabel(desc);
|
|
3039
|
-
return new RhiNullComputePassEncoder(this.bookkeeper, this.counter, label);
|
|
3040
|
-
}
|
|
3041
|
-
copyBufferToBuffer(_source, _sourceOffsetOrDestination, _destinationOrSize, _destinationOffset, _size) {
|
|
3042
|
-
}
|
|
3043
|
-
copyBufferToTexture(_source, _destination, _copySize) {
|
|
3044
|
-
}
|
|
3045
|
-
copyTextureToBuffer(_source, _destination, _copySize) {
|
|
3046
|
-
}
|
|
3047
|
-
copyTextureToTexture(_source, _destination, _copySize) {
|
|
3048
|
-
}
|
|
3049
|
-
clearBuffer(_buffer, _offset, _size) {
|
|
3050
|
-
}
|
|
3051
|
-
resolveQuerySet(_querySet, _firstQuery, _queryCount, _destination, _destinationOffset) {
|
|
3052
|
-
return ok(void 0);
|
|
3053
|
-
}
|
|
3054
|
-
writeTimestamp(_querySet, _queryIndex) {
|
|
3055
|
-
}
|
|
3056
|
-
pushDebugGroup(_groupLabel) {
|
|
3057
|
-
}
|
|
3058
|
-
popDebugGroup() {
|
|
3059
|
-
}
|
|
3060
|
-
insertDebugMarker(_markerLabel) {
|
|
3061
|
-
}
|
|
3062
|
-
finish() {
|
|
3063
|
-
return ok(this.bookkeeper.register("CommandBuffer"));
|
|
3064
|
-
}
|
|
3065
|
-
};
|
|
3066
|
-
BOOKKEEPING_KEY = /* @__PURE__ */ Symbol("forgeax-rhi-null-bookkeeping");
|
|
3067
|
-
Bookkeeper = class {
|
|
3068
|
-
deviceId;
|
|
3069
|
-
nextHandleId = 0;
|
|
3070
|
-
records = /* @__PURE__ */ new Map();
|
|
3071
|
-
constructor(deviceId) {
|
|
3072
|
-
this.deviceId = deviceId;
|
|
3073
|
-
}
|
|
3074
|
-
/**
|
|
3075
|
-
* Register a freshly-minted handle of the given kind, returning a plain
|
|
3076
|
-
* object that carries its ledger row. The caller casts the return value to
|
|
3077
|
-
* the concrete brand (`as unknown as Buffer` etc.).
|
|
3078
|
-
*/
|
|
3079
|
-
register(kind) {
|
|
3080
|
-
const id = this.nextHandleId++;
|
|
3081
|
-
const record = {
|
|
3082
|
-
id,
|
|
3083
|
-
kind,
|
|
3084
|
-
destroyed: false,
|
|
3085
|
-
sourceDeviceId: this.deviceId
|
|
3086
|
-
};
|
|
3087
|
-
this.records.set(id, record);
|
|
3088
|
-
return { [BOOKKEEPING_KEY]: record };
|
|
3089
|
-
}
|
|
3090
|
-
/**
|
|
3091
|
-
* Mark a handle destroyed. Fail-fasts on a second destroy
|
|
3092
|
-
* ('destroy-after-destroy') or on a handle issued by a different device
|
|
3093
|
-
* ('rhi-not-available'); otherwise flips the destroyed flag and returns ok.
|
|
3094
|
-
*/
|
|
3095
|
-
destroy(handle) {
|
|
3096
|
-
const ownership = this.validateOwnership(handle);
|
|
3097
|
-
if (!ownership.ok) return ownership;
|
|
3098
|
-
const destroyedCheck = isHandleDestroyed(handle);
|
|
3099
|
-
if (!destroyedCheck.ok) return destroyedCheck;
|
|
3100
|
-
ownership.value.destroyed = true;
|
|
3101
|
-
return ok(void 0);
|
|
3102
|
-
}
|
|
3103
|
-
/**
|
|
3104
|
-
* Report whether a handle has already been destroyed (true once destroy()
|
|
3105
|
-
* has flipped its flag). Used by command-stream methods that must not
|
|
3106
|
-
* consume a stale handle.
|
|
3107
|
-
*/
|
|
3108
|
-
isDestroyed(handle) {
|
|
3109
|
-
return readRecord(handle)?.destroyed === true;
|
|
3110
|
-
}
|
|
3111
|
-
/**
|
|
3112
|
-
* Validate that a handle was issued by THIS device. Returns the ledger row on
|
|
3113
|
-
* success so callers can mutate it (e.g. flip destroyed); returns
|
|
3114
|
-
* 'rhi-not-available' for a foreign handle (AC-09 — no silent pass).
|
|
3115
|
-
*/
|
|
3116
|
-
validateOwnership(handle) {
|
|
3117
|
-
const record = readRecord(handle);
|
|
3118
|
-
if (record === void 0 || record.sourceDeviceId !== this.deviceId) {
|
|
3119
|
-
return err(
|
|
3120
|
-
new RhiError({
|
|
3121
|
-
code: "rhi-not-available",
|
|
3122
|
-
expected: "handle was issued by this RhiNull device",
|
|
3123
|
-
hint: "do not pass a handle created on a different RhiNull device into this device; create resources on the device they are used with"
|
|
3124
|
-
})
|
|
3125
|
-
);
|
|
3126
|
-
}
|
|
3127
|
-
return ok(record);
|
|
3128
|
-
}
|
|
3129
|
-
/**
|
|
3130
|
-
* Return all ledger rows as a readonly array. M3 unit tests (w17) read this
|
|
3131
|
-
* to assert create/destroy pairing, BGL/PSO shape counts, and resource
|
|
3132
|
-
* lifecycle coverage (AC-05/06/07). Returns a snapshot of the current Map
|
|
3133
|
-
* so callers can iterate without a stale reference after further mutations.
|
|
3134
|
-
*/
|
|
3135
|
-
allRecords() {
|
|
3136
|
-
return [...this.records.values()];
|
|
3137
|
-
}
|
|
3138
|
-
/** Report the total number of records in the ledger. */
|
|
3139
|
-
recordCount() {
|
|
3140
|
-
return this.records.size;
|
|
3141
|
-
}
|
|
3142
|
-
};
|
|
3143
|
-
nextDeviceId = 0;
|
|
3144
|
-
RhiNullDevice = class {
|
|
3145
|
-
internalBookkeeper;
|
|
3146
|
-
nullQueue;
|
|
3147
|
-
encoderFactory;
|
|
3148
|
-
/** Per-frame total draw count across all pass encoders executed this frame
|
|
3149
|
-
* (aggregated by the command encoder on finish, then reset). M3 unit tests
|
|
3150
|
-
* (w17) read this to assert draw count >= 1 (AC-06). */
|
|
3151
|
-
totalDrawCount = 0;
|
|
3152
|
-
/** Per-frame total direct and indirect compute dispatch count. */
|
|
3153
|
-
totalDispatchCount = 0;
|
|
3154
|
-
/** Per-frame total bind group set count (AC-06 / AC-05 readback). */
|
|
3155
|
-
totalBindGroupCount = 0;
|
|
3156
|
-
/** Per-frame pass names executed this frame, in schedule order (AC-04). */
|
|
3157
|
-
framePassNames = [];
|
|
3158
|
-
/** The per-device handle ledger — exposed so M3 tests can assert create/destroy
|
|
3159
|
-
* pairing and BGL/PSO shape counts (AC-05/06/07). */
|
|
3160
|
-
get bookkeeper() {
|
|
3161
|
-
return this.internalBookkeeper;
|
|
3162
|
-
}
|
|
3163
|
-
constructor(queue, encoderFactory) {
|
|
3164
|
-
this.internalBookkeeper = new Bookkeeper(nextDeviceId++);
|
|
3165
|
-
this.nullQueue = queue;
|
|
3166
|
-
this.encoderFactory = encoderFactory;
|
|
3167
|
-
}
|
|
3168
|
-
get caps() {
|
|
3169
|
-
return {
|
|
3170
|
-
backendKind: "null",
|
|
3171
|
-
compute: true,
|
|
3172
|
-
timestampQuery: false,
|
|
3173
|
-
timestampPeriodNanoseconds: null,
|
|
3174
|
-
indirectDrawing: true,
|
|
3175
|
-
textureCompressionBc: false,
|
|
3176
|
-
textureCompressionEtc2: false,
|
|
3177
|
-
textureCompressionAstc: false,
|
|
3178
|
-
// 3 wgpu-native-only reserved flags stay false on non-native backends
|
|
3179
|
-
// (D-5); the headless backend is not a native runtime.
|
|
3180
|
-
multiDrawIndirect: false,
|
|
3181
|
-
pushConstants: false,
|
|
3182
|
-
textureBindingArray: false,
|
|
3183
|
-
samplerAliasing: true,
|
|
3184
|
-
firstInstanceIndirect: true,
|
|
3185
|
-
storageBuffer: true,
|
|
3186
|
-
storageTexture: true,
|
|
3187
|
-
rgba16floatRenderable: true,
|
|
3188
|
-
rg11b10ufloatRenderable: true,
|
|
3189
|
-
float32Filterable: true,
|
|
3190
|
-
maxColorAttachments: 8
|
|
3191
|
-
};
|
|
3192
|
-
}
|
|
3193
|
-
get features() {
|
|
3194
|
-
return EMPTY_FEATURES;
|
|
3195
|
-
}
|
|
3196
|
-
get limits() {
|
|
3197
|
-
return EMPTY_LIMITS;
|
|
3198
|
-
}
|
|
3199
|
-
get queue() {
|
|
3200
|
-
return this.nullQueue;
|
|
3201
|
-
}
|
|
3202
|
-
// forgeax-async-whitelist: dom-native — spec `GPUDevice.lost` Promise
|
|
3203
|
-
// passthrough. The headless backend never loses a device (no GPU), so the
|
|
3204
|
-
// Promise stays unsettled for the lifetime of the device, mirroring a live
|
|
3205
|
-
// device that never transitions to the lost state.
|
|
3206
|
-
get lost() {
|
|
3207
|
-
return NEVER;
|
|
3208
|
-
}
|
|
3209
|
-
createBuffer(_desc) {
|
|
3210
|
-
return ok(this.internalBookkeeper.register("Buffer"));
|
|
3211
|
-
}
|
|
3212
|
-
createTexture(_desc) {
|
|
3213
|
-
return ok(this.internalBookkeeper.register("Texture"));
|
|
3214
|
-
}
|
|
3215
|
-
destroyBuffer(buf) {
|
|
3216
|
-
return this.internalBookkeeper.destroy(buf);
|
|
3217
|
-
}
|
|
3218
|
-
destroyQuerySet(querySet) {
|
|
3219
|
-
return this.internalBookkeeper.destroy(querySet);
|
|
3220
|
-
}
|
|
3221
|
-
destroyTexture(tex) {
|
|
3222
|
-
return this.internalBookkeeper.destroy(tex);
|
|
3223
|
-
}
|
|
3224
|
-
createTextureView(_texture, _desc) {
|
|
3225
|
-
return ok(this.internalBookkeeper.register("TextureView"));
|
|
3226
|
-
}
|
|
3227
|
-
createSampler(_desc) {
|
|
3228
|
-
return ok(this.internalBookkeeper.register("Sampler"));
|
|
3229
|
-
}
|
|
3230
|
-
createBindGroupLayout(_desc) {
|
|
3231
|
-
return ok(this.internalBookkeeper.register("BindGroupLayout"));
|
|
3232
|
-
}
|
|
3233
|
-
createBindGroup(_desc) {
|
|
3234
|
-
return ok(this.internalBookkeeper.register("BindGroup"));
|
|
3235
|
-
}
|
|
3236
|
-
createPipelineLayout(_desc) {
|
|
3237
|
-
return ok(this.internalBookkeeper.register("PipelineLayout"));
|
|
3238
|
-
}
|
|
3239
|
-
createRenderPipeline(_desc) {
|
|
3240
|
-
return ok(this.makePipeline("RenderPipeline"));
|
|
3241
|
-
}
|
|
3242
|
-
createComputePipeline(_desc) {
|
|
3243
|
-
return ok(this.makePipeline("ComputePipeline"));
|
|
3244
|
-
}
|
|
3245
|
-
createQuerySet(desc) {
|
|
3246
|
-
if (desc.type === "timestamp") {
|
|
3247
|
-
return err(
|
|
3248
|
-
new RhiError({
|
|
3249
|
-
code: "feature-not-enabled",
|
|
3250
|
-
expected: "caps.timestampQuery === true (timestamp-query feature)",
|
|
3251
|
-
hint: "RhiNull is structural-only and cannot produce GPU timestamp ticks"
|
|
3252
|
-
})
|
|
3253
|
-
);
|
|
3254
|
-
}
|
|
3255
|
-
return ok(this.internalBookkeeper.register("QuerySet"));
|
|
3256
|
-
}
|
|
3257
|
-
createCommandEncoder(_desc) {
|
|
3258
|
-
return ok(this.encoderFactory(this.internalBookkeeper, this));
|
|
3259
|
-
}
|
|
3260
|
-
/**
|
|
3261
|
-
* Mint a pipeline handle whose object also carries the no-op
|
|
3262
|
-
* `getBindGroupLayout(index)` ops method (D-2). The prod auto-layout path
|
|
3263
|
-
* (debug-draw.ts) and the existing mock unit tests both call
|
|
3264
|
-
* `pipeline.getBindGroupLayout(n)`; returning a legal BindGroupLayout brand
|
|
3265
|
-
* (recorded in the ledger) keeps those consumers from crashing on a missing
|
|
3266
|
-
* method.
|
|
3267
|
-
*/
|
|
3268
|
-
makePipeline(kind) {
|
|
3269
|
-
const handle = this.internalBookkeeper.register(kind);
|
|
3270
|
-
const getBindGroupLayout = (_index) => this.internalBookkeeper.register("BindGroupLayout");
|
|
3271
|
-
return Object.assign(handle, { getBindGroupLayout });
|
|
3272
|
-
}
|
|
3273
|
-
};
|
|
3274
|
-
EMPTY_FEATURES = /* @__PURE__ */ new Set();
|
|
3275
|
-
EMPTY_LIMITS = {};
|
|
3276
|
-
NEVER = new Promise(() => {
|
|
3277
|
-
});
|
|
3278
|
-
RhiNullQueue = class {
|
|
3279
|
-
writeBuffer(_buffer, _bufferOffset, _data, _dataOffset, _size) {
|
|
3280
|
-
return ok(void 0);
|
|
3281
|
-
}
|
|
3282
|
-
writeTexture(_destination, _data, _dataLayout, _size) {
|
|
3283
|
-
return ok(void 0);
|
|
3284
|
-
}
|
|
3285
|
-
copyExternalImageToTexture(_source, _destination, _copySize) {
|
|
3286
|
-
return ok(void 0);
|
|
3287
|
-
}
|
|
3288
|
-
submit(_commandBuffers) {
|
|
3289
|
-
return ok(void 0);
|
|
3290
|
-
}
|
|
3291
|
-
// forgeax-async-whitelist: dom-native — spec `GPUQueue.onSubmittedWorkDone`
|
|
3292
|
-
// never rejects. The headless backend has no pending GPU work, so it resolves
|
|
3293
|
-
// immediately (AC-12: read-back idioms must not hang).
|
|
3294
|
-
onSubmittedWorkDone() {
|
|
3295
|
-
return Promise.resolve(void 0);
|
|
3296
|
-
}
|
|
3297
|
-
};
|
|
3298
|
-
RhiNullAdapter = class {
|
|
3299
|
-
features = /* @__PURE__ */ new Set();
|
|
3300
|
-
limits = {};
|
|
3301
|
-
// forgeax-async-whitelist is not needed: this returns Promise<Result<...>>
|
|
3302
|
-
// per the spec contract; never rejects.
|
|
3303
|
-
requestDevice(_opts) {
|
|
3304
|
-
const device = new RhiNullDevice(
|
|
3305
|
-
new RhiNullQueue(),
|
|
3306
|
-
(bookkeeper, dev) => new RhiNullCommandEncoder(bookkeeper, dev)
|
|
3307
|
-
);
|
|
3308
|
-
return Promise.resolve(ok(device));
|
|
3309
|
-
}
|
|
3310
|
-
};
|
|
3311
|
-
RhiNullCanvasContext = class {
|
|
3312
|
-
configure(_desc) {
|
|
3313
|
-
return ok(void 0);
|
|
3314
|
-
}
|
|
3315
|
-
unconfigure() {
|
|
3316
|
-
}
|
|
3317
|
-
getConfiguration() {
|
|
3318
|
-
return void 0;
|
|
3319
|
-
}
|
|
3320
|
-
getCurrentTexture() {
|
|
3321
|
-
return ok({});
|
|
3322
|
-
}
|
|
3323
|
-
};
|
|
3324
|
-
rhi = {
|
|
3325
|
-
requestAdapter,
|
|
3326
|
-
acquireCanvasContext,
|
|
3327
|
-
createShaderModule
|
|
3328
|
-
};
|
|
3329
|
-
}
|
|
3330
|
-
});
|
|
3331
3014
|
function createCliRhiDebugOperationContext() {
|
|
3332
3015
|
return {
|
|
3333
3016
|
captureFrame: async () => err(
|
|
3334
3017
|
createRhiDebugError("capture-unavailable", {
|
|
3335
3018
|
stage: "capture",
|
|
3336
|
-
cause: "the standalone CLI has no live App capture provider"
|
|
3019
|
+
cause: "the standalone CLI has no live App capture provider; start a recorder-enabled live host and invoke its rhiCapture root"
|
|
3337
3020
|
})
|
|
3338
3021
|
),
|
|
3339
3022
|
async readArtifact(artifact) {
|
|
@@ -3396,7 +3079,6 @@ function createCliRhiDebugOperationContext() {
|
|
|
3396
3079
|
}
|
|
3397
3080
|
var init_cli_context = __esm({
|
|
3398
3081
|
"src/rhi-debug/cli-context.ts"() {
|
|
3399
|
-
init_dist3();
|
|
3400
3082
|
}
|
|
3401
3083
|
});
|
|
3402
3084
|
function createRhiDebugOperationContext(host) {
|
|
@@ -3734,7 +3416,15 @@ async function sdkInstallCommand(options) {
|
|
|
3734
3416
|
await rename(resolve(staging, name), resolve(root, name));
|
|
3735
3417
|
}
|
|
3736
3418
|
}
|
|
3737
|
-
return {
|
|
3419
|
+
return {
|
|
3420
|
+
ok: true,
|
|
3421
|
+
value: {
|
|
3422
|
+
root,
|
|
3423
|
+
sdkVersion: version,
|
|
3424
|
+
source: "@forgeax/engine-sdk",
|
|
3425
|
+
next: { cwd: root, argv: ["node", "./bin/forgeax.mjs", "init"] }
|
|
3426
|
+
}
|
|
3427
|
+
};
|
|
3738
3428
|
} catch (cause) {
|
|
3739
3429
|
return { ok: false, error: commandError(cause, "sdk-install-failed") };
|
|
3740
3430
|
} finally {
|
|
@@ -3817,6 +3507,21 @@ __export(commands_exports, {
|
|
|
3817
3507
|
function runRhiDebugCommand(name, input, context) {
|
|
3818
3508
|
return runRhiDebugOperation(name, input, context);
|
|
3819
3509
|
}
|
|
3510
|
+
async function materializeViteDevPort(port) {
|
|
3511
|
+
if (port !== 0) return port;
|
|
3512
|
+
const reservation = createServer();
|
|
3513
|
+
await new Promise((resolveListen, rejectListen) => {
|
|
3514
|
+
reservation.once("error", rejectListen);
|
|
3515
|
+
reservation.listen(0, "127.0.0.1", resolveListen);
|
|
3516
|
+
});
|
|
3517
|
+
const address = reservation.address();
|
|
3518
|
+
const assigned = typeof address === "object" && address !== null ? address.port : 0;
|
|
3519
|
+
await new Promise((resolveClose, rejectClose) => {
|
|
3520
|
+
reservation.close((error) => error === void 0 ? resolveClose() : rejectClose(error));
|
|
3521
|
+
});
|
|
3522
|
+
if (assigned === 0) throw new Error("OS did not assign an ephemeral preview port");
|
|
3523
|
+
return assigned;
|
|
3524
|
+
}
|
|
3820
3525
|
async function buildCommand(options = {}) {
|
|
3821
3526
|
const facts = await readProjectFacts(options.root);
|
|
3822
3527
|
if (!facts.ok) return facts;
|
|
@@ -3919,13 +3624,38 @@ async function devCommand(options = {}) {
|
|
|
3919
3624
|
const facts = await readProjectFacts(options.root);
|
|
3920
3625
|
if (!facts.ok) return facts;
|
|
3921
3626
|
const previous = process.cwd();
|
|
3627
|
+
let server;
|
|
3922
3628
|
try {
|
|
3629
|
+
const { createServer: createServer2 } = await import('vite');
|
|
3630
|
+
const { createViteConfig: createViteConfig2 } = await Promise.resolve().then(() => (init_host(), host_exports));
|
|
3631
|
+
const port = resolveProjectPort(options.port);
|
|
3632
|
+
const vitePort = { ...port, port: await materializeViteDevPort(port.port) };
|
|
3923
3633
|
process.chdir(facts.value.root);
|
|
3924
|
-
|
|
3925
|
-
|
|
3634
|
+
server = await createServer2(
|
|
3635
|
+
await createViteConfig2(facts.value, "serve", "/", {
|
|
3636
|
+
server: vitePort
|
|
3637
|
+
})
|
|
3638
|
+
);
|
|
3639
|
+
await server.listen(vitePort.port);
|
|
3926
3640
|
if (options.json !== true) server.printUrls();
|
|
3927
|
-
return {
|
|
3641
|
+
return {
|
|
3642
|
+
ok: true,
|
|
3643
|
+
value: {
|
|
3644
|
+
root: facts.value.root,
|
|
3645
|
+
urls: server.resolvedUrls,
|
|
3646
|
+
mode: "dev",
|
|
3647
|
+
serves: "source",
|
|
3648
|
+
capabilities: {
|
|
3649
|
+
"rhi.capture": {
|
|
3650
|
+
available: false,
|
|
3651
|
+
realm: "host",
|
|
3652
|
+
reason: "standalone-dev-server-has-no-live-app-cli-attachment"
|
|
3653
|
+
}
|
|
3654
|
+
}
|
|
3655
|
+
}
|
|
3656
|
+
};
|
|
3928
3657
|
} catch (cause) {
|
|
3658
|
+
await server?.close();
|
|
3929
3659
|
process.chdir(previous);
|
|
3930
3660
|
return { ok: false, error: commandError(cause, "dev-server-failed") };
|
|
3931
3661
|
}
|
|
@@ -3935,15 +3665,31 @@ async function previewCommand(options = {}) {
|
|
|
3935
3665
|
const verified = await verifyDist(resolve(root, "dist"));
|
|
3936
3666
|
if (!verified.ok) return verified;
|
|
3937
3667
|
try {
|
|
3668
|
+
const port = resolveProjectPort(options.port);
|
|
3938
3669
|
const server = await preview({
|
|
3939
3670
|
root,
|
|
3940
3671
|
configFile: false,
|
|
3941
3672
|
base: verified.value.base,
|
|
3942
|
-
preview: { open: false, port
|
|
3673
|
+
preview: { open: false, ...port },
|
|
3943
3674
|
build: { outDir: resolve(root, "dist") }
|
|
3944
3675
|
});
|
|
3945
3676
|
if (options.json !== true) server.printUrls();
|
|
3946
|
-
return {
|
|
3677
|
+
return {
|
|
3678
|
+
ok: true,
|
|
3679
|
+
value: {
|
|
3680
|
+
root,
|
|
3681
|
+
urls: server.resolvedUrls,
|
|
3682
|
+
mode: "preview",
|
|
3683
|
+
serves: "dist",
|
|
3684
|
+
capabilities: {
|
|
3685
|
+
"rhi.capture": {
|
|
3686
|
+
available: false,
|
|
3687
|
+
realm: "host",
|
|
3688
|
+
reason: "static-dist-host-does-not-install-dev-capture"
|
|
3689
|
+
}
|
|
3690
|
+
}
|
|
3691
|
+
}
|
|
3692
|
+
};
|
|
3947
3693
|
} catch (cause) {
|
|
3948
3694
|
return { ok: false, error: commandError(cause, "preview-server-failed") };
|
|
3949
3695
|
}
|
|
@@ -3993,6 +3739,7 @@ var init_commands = __esm({
|
|
|
3993
3739
|
init_dist();
|
|
3994
3740
|
init_host();
|
|
3995
3741
|
init_project();
|
|
3742
|
+
init_types();
|
|
3996
3743
|
init_assets();
|
|
3997
3744
|
init_bootstrap_commands();
|
|
3998
3745
|
init_plugin_authoring();
|
|
@@ -4365,7 +4112,7 @@ function hasStaticToolDeclarations(entry) {
|
|
|
4365
4112
|
return Array.isArray(Reflect.get(entry.config, "tools"));
|
|
4366
4113
|
}
|
|
4367
4114
|
async function createViteModuleLoader(root) {
|
|
4368
|
-
const server = await createServer({
|
|
4115
|
+
const server = await createServer$1({
|
|
4369
4116
|
root,
|
|
4370
4117
|
appType: "custom",
|
|
4371
4118
|
configFile: false,
|
|
@@ -4622,6 +4369,36 @@ var init_runtime = __esm({
|
|
|
4622
4369
|
}
|
|
4623
4370
|
});
|
|
4624
4371
|
|
|
4372
|
+
// src/cli-output.ts
|
|
4373
|
+
function agentOnboardingLines(value) {
|
|
4374
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return [];
|
|
4375
|
+
const onboarding = value.onboarding;
|
|
4376
|
+
if (onboarding === null || typeof onboarding !== "object" || Array.isArray(onboarding)) return [];
|
|
4377
|
+
const candidate = onboarding;
|
|
4378
|
+
const lines = Array.isArray(candidate.read) ? candidate.read.filter((path) => typeof path === "string").map((path) => `[forgeax] read: ${path}`) : [];
|
|
4379
|
+
if (typeof candidate.next?.cwd === "string" && Array.isArray(candidate.next.argv) && candidate.next.argv.every((part) => typeof part === "string")) {
|
|
4380
|
+
lines.push(`[forgeax] next cwd: ${candidate.next.cwd}`);
|
|
4381
|
+
lines.push(`[forgeax] next: ${candidate.next.argv.join(" ")}`);
|
|
4382
|
+
}
|
|
4383
|
+
return lines;
|
|
4384
|
+
}
|
|
4385
|
+
function sdkUpdateLines(value) {
|
|
4386
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return [];
|
|
4387
|
+
const update = value.sdkUpdate;
|
|
4388
|
+
if (update === null || typeof update !== "object" || Array.isArray(update)) return [];
|
|
4389
|
+
const candidate = update;
|
|
4390
|
+
if (candidate.status !== "available" || typeof candidate.currentVersion !== "string" || typeof candidate.latestVersion !== "string" || typeof candidate.migrationRisk !== "string") {
|
|
4391
|
+
return [];
|
|
4392
|
+
}
|
|
4393
|
+
return [
|
|
4394
|
+
`[forgeax] update available: @forgeax/engine-sdk ${candidate.currentVersion} -> ${candidate.latestVersion}`,
|
|
4395
|
+
`[forgeax] update note: ${candidate.migrationRisk}`
|
|
4396
|
+
];
|
|
4397
|
+
}
|
|
4398
|
+
function renderForgeaxUsage() {
|
|
4399
|
+
return "Usage: forgeax new [directory] [--template empty|game-default]\n forgeax <init|doctor|test|dev|build|package|serve|preview> [directory]\n forgeax package [directory] [--output release/game-web.zip]\n forgeax run <rhi.capture|rhi.summary|rhi.inspect> [options]\n forgeax run <operation-id> --input <request.json>\n forgeax asset <add|verify|inspect|list> [subject]\n forgeax shader check [path]\n forgeax plugin <install|uninstall> <module-or-id> [options]\n forgeax skill <install|verify> [--root directory]\n forgeax sdk install <directory> [--version VERSION]\n";
|
|
4400
|
+
}
|
|
4401
|
+
|
|
4625
4402
|
// src/cli.ts
|
|
4626
4403
|
init_commands();
|
|
4627
4404
|
|
|
@@ -4821,13 +4598,16 @@ async function execCommand(options) {
|
|
|
4821
4598
|
}
|
|
4822
4599
|
|
|
4823
4600
|
// src/cli.ts
|
|
4824
|
-
|
|
4601
|
+
init_types();
|
|
4602
|
+
var rawArgs = process.argv.slice(2);
|
|
4603
|
+
var args = [...rawArgs];
|
|
4825
4604
|
var primary = args.shift();
|
|
4826
4605
|
var nested = primary === "asset" || primary === "shader" || primary === "plugin" || primary === "skill" || primary === "sdk" ? args.shift() : void 0;
|
|
4827
4606
|
var command = primary === "asset" || primary === "shader" || primary === "plugin" || primary === "skill" || primary === "sdk" ? `${primary}.${nested ?? ""}` : primary;
|
|
4828
4607
|
var json = args.includes("--json");
|
|
4829
4608
|
var dryRun = args.includes("--dry-run");
|
|
4830
4609
|
var noInstall = args.includes("--no-install");
|
|
4610
|
+
var help = rawArgs.includes("--help") || rawArgs.includes("-h");
|
|
4831
4611
|
function option(name) {
|
|
4832
4612
|
const index = args.indexOf(name);
|
|
4833
4613
|
return index < 0 ? void 0 : args[index + 1];
|
|
@@ -4844,6 +4624,7 @@ var optionValueIndexes = new Set(
|
|
|
4844
4624
|
"--input",
|
|
4845
4625
|
"--output",
|
|
4846
4626
|
"--root",
|
|
4627
|
+
"--port",
|
|
4847
4628
|
"--template",
|
|
4848
4629
|
"--version",
|
|
4849
4630
|
"--work-index"
|
|
@@ -4882,6 +4663,18 @@ var commands = [
|
|
|
4882
4663
|
"describe",
|
|
4883
4664
|
"exec"
|
|
4884
4665
|
];
|
|
4666
|
+
function projectServerOptions() {
|
|
4667
|
+
const parsedPort = parseProjectPortOption(option("--port"), args.includes("--port"));
|
|
4668
|
+
if (!parsedPort.ok) return parsedPort;
|
|
4669
|
+
return {
|
|
4670
|
+
ok: true,
|
|
4671
|
+
value: {
|
|
4672
|
+
root: projectRoot,
|
|
4673
|
+
json,
|
|
4674
|
+
...parsedPort.value === void 0 ? {} : { port: parsedPort.value }
|
|
4675
|
+
}
|
|
4676
|
+
};
|
|
4677
|
+
}
|
|
4885
4678
|
async function run(value) {
|
|
4886
4679
|
switch (value) {
|
|
4887
4680
|
case "new": {
|
|
@@ -4898,8 +4691,10 @@ async function run(value) {
|
|
|
4898
4691
|
return doctorCommand({ root: projectRoot});
|
|
4899
4692
|
case "test":
|
|
4900
4693
|
return testCommand({ root: projectRoot});
|
|
4901
|
-
case "dev":
|
|
4902
|
-
|
|
4694
|
+
case "dev": {
|
|
4695
|
+
const options = projectServerOptions();
|
|
4696
|
+
return options.ok ? devCommand(options.value) : options;
|
|
4697
|
+
}
|
|
4903
4698
|
case "build": {
|
|
4904
4699
|
const base = option("--base");
|
|
4905
4700
|
const outDir = option("--out-dir");
|
|
@@ -4917,10 +4712,14 @@ async function run(value) {
|
|
|
4917
4712
|
...output === void 0 ? {} : { output }
|
|
4918
4713
|
});
|
|
4919
4714
|
}
|
|
4920
|
-
case "preview":
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
|
|
4715
|
+
case "preview": {
|
|
4716
|
+
const options = projectServerOptions();
|
|
4717
|
+
return options.ok ? previewCommand(options.value) : options;
|
|
4718
|
+
}
|
|
4719
|
+
case "serve": {
|
|
4720
|
+
const options = projectServerOptions();
|
|
4721
|
+
return options.ok ? devCommand(options.value) : options;
|
|
4722
|
+
}
|
|
4924
4723
|
case "plugin.install": {
|
|
4925
4724
|
const module = positionals[0];
|
|
4926
4725
|
const id = option("--id");
|
|
@@ -5097,10 +4896,10 @@ async function run(value) {
|
|
|
5097
4896
|
}
|
|
5098
4897
|
}
|
|
5099
4898
|
}
|
|
5100
|
-
if (
|
|
5101
|
-
process.
|
|
5102
|
-
|
|
5103
|
-
);
|
|
4899
|
+
if (help && primary !== "run") {
|
|
4900
|
+
process.stdout.write(renderForgeaxUsage());
|
|
4901
|
+
} else if (command === void 0 || !commands.includes(command)) {
|
|
4902
|
+
process.stderr.write(renderForgeaxUsage());
|
|
5104
4903
|
process.exitCode = 2;
|
|
5105
4904
|
} else {
|
|
5106
4905
|
const stdoutWrite = process.stdout.write;
|
|
@@ -5120,9 +4919,23 @@ if (command === void 0 || !commands.includes(command)) {
|
|
|
5120
4919
|
const envelope = result.ok ? { schemaVersion: "1.0.0", command, ok: true, value: result.value } : { schemaVersion: "1.0.0", command, ok: false, error: result.error };
|
|
5121
4920
|
if (json) stdoutWrite.call(process.stdout, `${JSON.stringify(envelope)}
|
|
5122
4921
|
`);
|
|
5123
|
-
else if (result.ok)
|
|
4922
|
+
else if (result.ok) {
|
|
4923
|
+
process.stdout.write(`[forgeax] ${command} ready
|
|
4924
|
+
`);
|
|
4925
|
+
for (const line of agentOnboardingLines(result.value)) process.stdout.write(`${line}
|
|
4926
|
+
`);
|
|
4927
|
+
for (const line of sdkUpdateLines(result.value)) process.stdout.write(`${line}
|
|
4928
|
+
`);
|
|
4929
|
+
if (command === "sdk.install") {
|
|
4930
|
+
const value = result.value;
|
|
4931
|
+
if (typeof value.next?.cwd === "string" && Array.isArray(value.next.argv) && value.next.argv.every((part) => typeof part === "string")) {
|
|
4932
|
+
process.stdout.write(`[forgeax] next cwd: ${value.next.cwd}
|
|
5124
4933
|
`);
|
|
5125
|
-
|
|
4934
|
+
process.stdout.write(`[forgeax] next: ${value.next.argv.join(" ")}
|
|
4935
|
+
`);
|
|
4936
|
+
}
|
|
4937
|
+
}
|
|
4938
|
+
} else process.stderr.write(`[forgeax] ${result.error.code}: ${result.error.hint}
|
|
5126
4939
|
`);
|
|
5127
4940
|
if (!result.ok) process.exitCode = result.error.code === "cli-parse-error" ? 2 : 1;
|
|
5128
4941
|
}
|