@helix3/helix-cli 0.1.20-helix3.378 → 0.1.20-helix3.380
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 +9 -3
- package/dist/index.js +87 -4
- package/dist/index.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/vehicle/preview.d.ts +39 -0
- package/dist/vehicle/preview.js +100 -3
- package/dist/vehicle/preview.js.map +1 -1
- package/dist/vehicle/simulate.d.ts +174 -0
- package/dist/vehicle/simulate.js +472 -0
- package/dist/vehicle/simulate.js.map +1 -0
- package/dist/vehicle/simulateRuntime.d.ts +76 -0
- package/dist/vehicle/simulateRuntime.js +252 -0
- package/dist/vehicle/simulateRuntime.js.map +1 -0
- package/dist/vehicle/validate.d.ts +70 -4
- package/dist/vehicle/validate.js +229 -9
- package/dist/vehicle/validate.js.map +1 -1
- package/dist/visualQa/contactSheet.d.ts +16 -0
- package/dist/visualQa/contactSheet.js +41 -0
- package/dist/visualQa/contactSheet.js.map +1 -1
- package/dist/visualQa/harness.d.ts +1 -1
- package/dist/visualQa/vehicle.d.ts +21 -0
- package/dist/visualQa/vehicle.js +286 -32
- package/dist/visualQa/vehicle.js.map +1 -1
- package/dist/visualQa/vehicleCabinFit.d.ts +78 -0
- package/dist/visualQa/vehicleCabinFit.js +55 -0
- package/dist/visualQa/vehicleCabinFit.js.map +1 -0
- package/dist/visualQa/vehicleClearance.d.ts +129 -0
- package/dist/visualQa/vehicleClearance.js +293 -0
- package/dist/visualQa/vehicleClearance.js.map +1 -0
- package/dist/visualQa/vehicleMath.d.ts +8 -0
- package/dist/visualQa/vehicleMath.js +6 -4
- package/dist/visualQa/vehicleMath.js.map +1 -1
- package/dist/visualQa/vehicleOccupants.js +39 -16
- package/dist/visualQa/vehicleOccupants.js.map +1 -1
- package/dist/visualQa/vehiclePoses.d.ts +25 -0
- package/dist/visualQa/vehiclePoses.js +56 -0
- package/dist/visualQa/vehiclePoses.js.map +1 -0
- package/dist/visualQa/vehicleProbe.d.ts +1 -1
- package/dist/visualQa/vehicleProbe.js +98 -5
- package/dist/visualQa/vehicleProbe.js.map +1 -1
- package/dist/visualQa/vehicleStance.js +8 -3
- package/dist/visualQa/vehicleStance.js.map +1 -1
- package/docs/vehicle-simulate.md +121 -0
- package/docs/visual-qa.md +86 -1
- package/package.json +2 -1
- package/plugins/beamng-vehicle/bin/__pycache__/beamng_abs.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/beamng_audio.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/beamng_engine.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/beamng_expression.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/beamng_jbeam.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/beamng_materials.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/test_beamng_engine.cpython-312.pyc +0 -0
package/README.md
CHANGED
|
@@ -45,7 +45,8 @@ The token is stored in `~/.helix/credentials.json` (mode `600`). It's sent as
|
|
|
45
45
|
| `helix validate [dir]` | Validate a bundle locally — the exact rules the server enforces. |
|
|
46
46
|
| `helix publish [dir] [--thumbnail <file>] [--upload-source] [--source-dir <path>]` | Validate → resolve/create the world → upload files → finalize → set the cover image → print the play URL. `--upload-source` additionally sends the project's source through a separate private channel so the world can be edited on the website later — **off by default**. |
|
|
47
47
|
| `helix list` | List your worlds. |
|
|
48
|
-
| `helix vehicle
|
|
48
|
+
| `helix vehicle simulate <dir\|vehicle-package.json> [--item <id>] [--spec <file>] [--offline] [--json]` | Drive the package offline with the platform's **own** engine instrument (the runtime `^0.3` serves today, cached by sha; offline = last cached, labelled) and judge 0-100 km/h, 0-60 mph, quarter mile, top speed, 100-0 km/h / 60-0 mph braking, skidpad g, turbo lag and the rest pose against the package `targets` and `--spec`. Exit `0` pass, `1` a figure out of tolerance, `2` could not simulate. See [docs/vehicle-simulate.md](docs/vehicle-simulate.md). |
|
|
49
|
+
| `helix vehicle validate <vehicle-package.json> [--glb <model.glb>] [--no-simulate] [--json]` | One-second local gate for a hand-authored Universal Vehicle, run before Package publication. Checks the package against the engine's own validator (vendored, pinned to an engine commit), the engine publish gate, the backend pin requirements, `groundToBodyOrigin` vs `wheelRadius + staticRideHeight`, and the `componentBuild` digest; with `--glb` also resolves the four wheel nodes, checks each wheel node's origin is at its hub (`WHEEL_PIVOT_NOT_AT_HUB`), the +X-left / +Z-forward frame, the vehicle publish budgets the articulation nodes and the add-on host manifest (`asset.extras.HELIX_vehicle`); drives packages that declare `targets` (`helix vehicle simulate`). Exit `0` no errors, `1` errors, `2` unreadable input. See [Vehicle validate](#vehicle-validate). |
|
|
49
50
|
| `helix vehicle preview [<dir-or-package.json>] [--glb <model.glb>] [--addon <dir>]… [--item <id>] [--tolerance-cm <cm>] [--out <dir>] [--proxy-item <id>] [--json]` | Render a vehicle that is **not published yet** headless in the real runtime (hosted test grounds, Try Now through a published proxy vehicle whose car is swapped for your package + GLB, served from disk; nothing uploaded or recorded) and measure it: body-origin height against the declared `groundToBodyOrigin` (the number that locks at first mint), wheels on their hubs, arch clearance, rolling radius vs `wheelRadius`, steering, doors, seats. Prints a numeric summary with PASS/FAIL per check and writes frames, a contact sheet, `bundle.json` and `preview.json`. Boards with the candidate's own seat ids. With `--addon <dir>` (repeatable) it previews unpublished vehicle **add-ons** instead: fits each to the candidate car or a published car and measures the drawn geometry in world space against its anchor (wheels within 1 cm of each hub, socket/host-framed parts at their mount, not at the car's origin; tune-only ops read back from the live tuning). Exit `0` pass, `1` fail, `2` could not measure. See [docs/vehicle-preview.md](docs/vehicle-preview.md). |
|
|
50
51
|
| `helix continuum publish-glb <mesh.glb> --title <t> [--package-type asset\|character] [--offer marketplace\|world --price-lix <n> --content-rating <rating>]` | Seal a Package first; optional offer makes it ownable. See [Package-first GLB publication](#package-first-glb-publication). |
|
|
51
52
|
| `helix continuum make-item --package-id <id> --package-version <v> --manifest-cid <cid> --content-rating <rating> [--offer marketplace\|world --price-lix <n>]` | Project one exact sealed Package Version into an Item, with an optional first offer. |
|
|
@@ -570,8 +571,11 @@ How each field is resolved, and what a null means:
|
|
|
570
571
|
|
|
571
572
|
## Vehicle validate
|
|
572
573
|
|
|
573
|
-
`helix vehicle validate <vehicle-package.json> [--glb <model.glb>] [--json]` needs no login
|
|
574
|
-
This local gate checks the vehicle Package and optional GLB before any publication.
|
|
574
|
+
`helix vehicle validate <vehicle-package.json> [--glb <model.glb>] [--no-simulate] [--offline] [--json]` needs no login.
|
|
575
|
+
This local gate checks the vehicle Package and optional GLB before any publication. When the package declares
|
|
576
|
+
`targets` it also drives the car with the platform's engine instrument (`helix vehicle simulate`; the runtime is
|
|
577
|
+
fetched once and cached, `--offline` uses the cache, `--no-simulate` skips the drive) and reports every figure
|
|
578
|
+
outside its tolerance as `SIMULATED_PERFORMANCE_OUT_OF_TOLERANCE`.
|
|
575
579
|
|
|
576
580
|
Every finding prints as `CODE path — message` plus a `fix:` line; `--json` returns
|
|
577
581
|
`{ ok, errors, warnings, issues: [{ code, level, path, message, fix }], engineCommit }`.
|
|
@@ -581,6 +585,8 @@ Every finding prints as `CODE path — message` plus a `fix:` line; `--json` re
|
|
|
581
585
|
| Engine validator (all its errors and warnings) | `ENGINE_VALIDATOR`, `ENGINE_VALIDATOR_CRASH` |
|
|
582
586
|
| Publish gate + backend pin | `PACKAGE_NOT_OBJECT`, `PACKAGE_VERSION_UNSUPPORTED`, `SCHEMA_VERSION_MISMATCH`, `REQUIRED_FIELD_MISSING`, `REQUIRED_BLOCK_MISSING`, `MESH_CLASS_ID_MISSING`, `MESH_PARTS_MISSING`, `WHEEL_VISUALS_MISSING`, `WHEEL_VISUALS_INVALID`, `NOT_DISTRIBUTABLE`, `MESH_SOURCE_FIXTURE`, `PUBLISH_GATE` |
|
|
583
587
|
| Cross-field | `GROUND_TO_BODY_ORIGIN_MISMATCH` (> 5 mm; fix the ride height, the field is locked after first publish), `COMPONENT_BUILD_DIGEST_MISMATCH` (prints the computed digest), `COMPONENT_ACTIONS_MISMATCH` (prints the compiled protocol), `COMPONENT_BUILD_INVALID`, `PANEL_SIGNAL_NO_ACTION` (warn), `SEAT_SOCKET_UNKNOWN` (warn) |
|
|
588
|
+
| Drive (package `targets`) | `SIMULATED_PERFORMANCE_OUT_OF_TOLERANCE` (figure, simulated, target, error, tolerance, engine version), `SIMULATED_REST_POSE_INVALID`, `SIMULATION_UNAVAILABLE` (warn: no runtime reachable or cached) |
|
|
589
|
+
| `--glb` host manifest | `HOST_MANIFEST_MISSING`, `HOST_MANIFEST_MISPLACED` (found in scene/document/node extras; the backend reads only `asset.extras.HELIX_vehicle`), `HOST_MANIFEST_EMPTY`, `HOST_MANIFEST_DIVERGENT`, `HOST_MANIFEST_SOCKET_INVALID`, `HOST_MANIFEST_SOCKET_NAME_INVALID`, `HOST_MANIFEST_SOCKET_NODE_MISSING`. `helix continuum publish-package` refuses a `vehicle` Package descriptor whose model GLB has any of these errors before sealing. See [docs/vehicle-simulate.md](docs/vehicle-simulate.md#host-manifest) |
|
|
584
590
|
| `--glb` | `WHEEL_NODES_MISSING`, `WHEEL_PIVOT_NOT_AT_HUB` (origin > 2 cm from the centre of its geometry — the wheel add-on anchor lands there), `WHEEL_ORIGINS_NOT_DISTINCT`, `WHEEL_FRAME_MIRRORED_X`, `WHEEL_FRAME_FRONT_NOT_PLUS_Z`, `WHEEL_NODE_EMPTY` (warn), `LENGTH_NOT_ALONG_Z` (warn), `PANEL_NODE_MISSING`, `VEHICLE_BUDGET_TRIANGLES` / `_MATERIALS` / `_TEXTURE_EDGE` / `_DRAW_CALLS` / `_FILE_SIZE` |
|
|
585
591
|
|
|
586
592
|
The model frame is glTF: +X is the vehicle's left, +Y up, +Z forward, metres. Wheel nodes are found by name with
|
package/dist/index.js
CHANGED
|
@@ -574,7 +574,10 @@ vehicleCommand
|
|
|
574
574
|
.description('Validate a vehicle package (and optionally its GLB) against the engine contract, the backend pin rules and the publish budgets. '
|
|
575
575
|
+ 'Exit 0 = no errors, 1 = errors, 2 = unreadable input')
|
|
576
576
|
.argument('<vehicle-package.json>', 'the vehicle package JSON that will be sealed in the Vehicle Package')
|
|
577
|
-
.option('--glb <model.glb>', 'also check the model: wheel nodes and pivots, frame orientation, budgets, articulation nodes')
|
|
577
|
+
.option('--glb <model.glb>', 'also check the model: wheel nodes and pivots, frame orientation, budgets, articulation nodes, and the add-on host manifest (asset.extras.HELIX_vehicle)')
|
|
578
|
+
.option('--no-simulate', 'skip the offline drive (by default a package with `targets` is driven by the platform\'s own engine instrument and every figure outside its tolerance is an ERROR)')
|
|
579
|
+
.option('--offline', 'drive with the last cached engine runtime; never fetch')
|
|
580
|
+
.option('--api-url <url>', 'API whose engine runtime drives the car (default: this CLI\'s environment)')
|
|
578
581
|
.option('--json', 'print the machine-readable report')
|
|
579
582
|
.action(async (packageFile, opts) => {
|
|
580
583
|
let pkg;
|
|
@@ -605,7 +608,7 @@ vehicleCommand
|
|
|
605
608
|
const { provisionalAudio } = await Promise.resolve().then(() => __importStar(require('./vehicle/audio')));
|
|
606
609
|
const prepared = provisionalAudio(pkg, (0, node_path_1.resolve)(packageFile, '..'));
|
|
607
610
|
provisional = prepared.filled;
|
|
608
|
-
result = await (0, validate_1.validateVehicle)(prepared.pkg, glb);
|
|
611
|
+
result = await (0, validate_1.validateVehicle)(prepared.pkg, glb, opts.simulate === false ? {} : { simulate: { apiUrl: opts.apiUrl ?? config_1.DEFAULT_API_URL, offline: opts.offline } });
|
|
609
612
|
}
|
|
610
613
|
catch (error) {
|
|
611
614
|
process.stderr.write(`✖ cannot validate ${opts.glb ?? packageFile}: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
@@ -618,10 +621,15 @@ vehicleCommand
|
|
|
618
621
|
else {
|
|
619
622
|
if (result.issues.length > 0)
|
|
620
623
|
console.log((0, validate_1.formatVehicleIssues)(result.issues));
|
|
624
|
+
if (result.simulation) {
|
|
625
|
+
const { formatSimulationReport } = await Promise.resolve().then(() => __importStar(require('./vehicle/simulate')));
|
|
626
|
+
console.log(formatSimulationReport(result.simulation).split('\n').map((line) => ` ${line}`).join('\n'));
|
|
627
|
+
}
|
|
621
628
|
if (provisional > 0)
|
|
622
629
|
console.log(` ${provisional} audio row${provisional === 1 ? '' : 's'} backed by a local WAV: seal the WAV in the Vehicle Package before making its Item; \`helix vehicle audio-check\` proves ${provisional === 1 ? 'it plays' : 'they play'}`);
|
|
623
630
|
console.log(`${result.ok ? '✔' : '✖'} ${result.ok ? 'Valid' : 'Invalid'} vehicle package: ${result.errors} error${result.errors === 1 ? '' : 's'}, ${result.warnings} warning${result.warnings === 1 ? '' : 's'}`
|
|
624
|
-
+ `${glb ? ' (package + GLB)' : ' (package only — add --glb <model.glb> to check wheels, pivots and
|
|
631
|
+
+ `${glb ? ' (package + GLB)' : ' (package only — add --glb <model.glb> to check wheels, pivots, budgets and the host manifest)'}`
|
|
632
|
+
+ `${result.simulation ? ` + drive (engine runtime ${result.simulation.runtime.version})` : opts.simulate === false ? ' (drive skipped: --no-simulate)' : ''} [validator ${result.engineCommit.slice(0, 8)}]`);
|
|
625
633
|
}
|
|
626
634
|
if (!result.ok)
|
|
627
635
|
process.exitCode = 1;
|
|
@@ -630,6 +638,7 @@ vehicleCommand
|
|
|
630
638
|
.command('preview')
|
|
631
639
|
.description('Render a vehicle that is not published yet in the real HELIX runtime (headless) and measure it: ride height against groundToBodyOrigin, '
|
|
632
640
|
+ 'wheels on their hubs, arch clearance, rolling radius, steering, doors, seats. Run it BEFORE the first mint, which locks groundToBodyOrigin. '
|
|
641
|
+
+ 'Every car preview also writes the fixed appearance poses (poses/pose.front|side|rear|top|front34|rear34|cockpit.jpg and poses.jpg) to compare with reference photos. '
|
|
633
642
|
+ 'With --addon <dir> it previews unpublished vehicle ADD-ONS instead: each add-on directory (the layout `vehicle addon check|publish` reads) is fitted '
|
|
634
643
|
+ 'to the candidate car, or to a published car (--item <id>, no car directory) by the engine\'s own fold, and the DRAWN geometry is measured in world space '
|
|
635
644
|
+ 'against its anchor (wheels within 1 cm of each hub, socket-framed parts on their socket, host-framed parts at their authored position, inside the car, '
|
|
@@ -642,7 +651,7 @@ vehicleCommand
|
|
|
642
651
|
.option('--tolerance-cm <cm>', 'with --addon: how far a drawn part may sit from its anchor (default: wheels 1 cm, socket-framed and host-framed parts 3 cm)', (v) => Number(v))
|
|
643
652
|
.option('--out <dir>', 'frames, contact-sheet.jpg, bundle.json and preview.json go here (default: <dir>/.helix-preview)')
|
|
644
653
|
.option('--proxy-item <id>', 'a published vehicle the QA account can Try Now; the runtime loads it and the candidate replaces its car (and its slot board is what --addon fits to)')
|
|
645
|
-
.option('--occupants', 'also seat an avatar in every seat and
|
|
654
|
+
.option('--occupants', 'also seat an avatar in every seat, judge the cabin, and report where the runtime\'s cabin fit put each seat vs where it was authored (slow)')
|
|
646
655
|
.option('--storage-state <file>', 'Playwright storageState of a signed-in QA account (default: sign in with `helix login`, or $HELIX_VQA_STORAGE_STATE)')
|
|
647
656
|
.option('--chrome <path>', 'Chromium executable (default: Playwright Chromium / Chrome for Testing, headless; the macOS system Chrome is refused)')
|
|
648
657
|
.option('--api-url <url>', 'platform API base URL')
|
|
@@ -707,6 +716,77 @@ function printVehicleJsonOr(json, value, human) {
|
|
|
707
716
|
else
|
|
708
717
|
human();
|
|
709
718
|
}
|
|
719
|
+
vehicleCommand
|
|
720
|
+
.command('simulate')
|
|
721
|
+
.description('Drive a vehicle package offline with the platform\'s OWN engine instrument (the runtime `^0.3` serves today: Rapier, land solver v2, the code players run) '
|
|
722
|
+
+ 'and judge it: 0-100 km/h, 0-60 mph, quarter mile, top speed (converged or not), 100-0 km/h and 60-0 mph braking, skidpad lateral g, turbo lag, '
|
|
723
|
+
+ 'gears used and the rest pose. Every figure is compared with the package\'s own `targets` (and --spec): error = (simulated - target) / target, '
|
|
724
|
+
+ 'PASS iff |error| <= tolerance. The runtime is fetched once per version and cached by sha under ~/.helix/cache/engine-runtime; offline, the last '
|
|
725
|
+
+ 'cached runtime is used and labelled. TURBO LAG: with boostTorqueCoef 0 the solver adds no boost torque, so a turbo car whose curve is the full-boost '
|
|
726
|
+
+ 'dyno curve pulls with no lag; author lag by derating the torque curve below spool, then check `turbo lag` here (docs/vehicle-simulate.md). '
|
|
727
|
+
+ 'Exit 0 = every compared figure passes, 1 = a figure is out of tolerance or the rest pose is wrong, 2 = could not simulate')
|
|
728
|
+
.argument('[dir-or-package.json]', 'a vehicle package directory (vehicle-package.json), the package JSON, or a saved item read')
|
|
729
|
+
.option('--item <id>', 'simulate a PUBLISHED vehicle item\'s stored package (public read) instead of a local file')
|
|
730
|
+
.option('--spec <file>', 'extra targets: a package / {targets}, or a reference spec (perf.0_100_kmh_s, perf.braking_100_0_m, perf.braking_70mph_0_m, perf.skidpad_g, perf.turbo_lag_s … as {value, tol_pct})')
|
|
731
|
+
.option('--offline', 'use the last cached engine runtime; never fetch')
|
|
732
|
+
.option('--runtime <file-or-url>', 'drive with this runtime bundle instead of the served one (a candidate engine build)')
|
|
733
|
+
.option('--range <range>', 'engine range to resolve (default ^0.3)')
|
|
734
|
+
.option('--api-url <url>', 'API to resolve the runtime (and --item) from (default: this CLI\'s environment)')
|
|
735
|
+
.option('--json', 'print the machine-readable report')
|
|
736
|
+
.action(async (input, opts) => {
|
|
737
|
+
const { readVehiclePackageArg, simulateVehicle, targetsFromSpec, formatSimulationReport } = await Promise.resolve().then(() => __importStar(require('./vehicle/simulate')));
|
|
738
|
+
const { loadEngineRuntime } = await Promise.resolve().then(() => __importStar(require('./vehicle/simulateRuntime')));
|
|
739
|
+
const { provisionalAudio } = await Promise.resolve().then(() => __importStar(require('./vehicle/audio')));
|
|
740
|
+
const apiUrl = (opts.apiUrl ?? config_1.DEFAULT_API_URL).replace(/\/$/, '');
|
|
741
|
+
let pkg;
|
|
742
|
+
let source;
|
|
743
|
+
let specTargets;
|
|
744
|
+
try {
|
|
745
|
+
if (opts.item) {
|
|
746
|
+
// A public read; retried on a 5xx so one flaky gateway answer does not read as "no such car".
|
|
747
|
+
let res = await fetch(`${apiUrl}/api/v1/universal-items/${encodeURIComponent(opts.item)}`, { headers: { accept: 'application/json' } });
|
|
748
|
+
for (let attempt = 1; res.status >= 500 && attempt < 4; attempt += 1) {
|
|
749
|
+
await new Promise((done) => setTimeout(done, 1000 * attempt));
|
|
750
|
+
res = await fetch(`${apiUrl}/api/v1/universal-items/${encodeURIComponent(opts.item)}`, { headers: { accept: 'application/json' } });
|
|
751
|
+
}
|
|
752
|
+
if (!res.ok)
|
|
753
|
+
throw new Error(`GET /api/v1/universal-items/${opts.item} answered HTTP ${res.status}`);
|
|
754
|
+
const body = await res.json();
|
|
755
|
+
pkg = (body.item ?? body).properties?.vehiclePackage;
|
|
756
|
+
if (!pkg)
|
|
757
|
+
throw new Error(`item ${opts.item} carries no properties.vehiclePackage`);
|
|
758
|
+
source = `item ${opts.item}`;
|
|
759
|
+
}
|
|
760
|
+
else {
|
|
761
|
+
if (!input)
|
|
762
|
+
throw new Error('pass a vehicle package directory or JSON file, or --item <id>');
|
|
763
|
+
const read = readVehiclePackageArg(input);
|
|
764
|
+
pkg = provisionalAudio(read.pkg, read.dir).pkg;
|
|
765
|
+
source = read.file;
|
|
766
|
+
}
|
|
767
|
+
if (opts.spec)
|
|
768
|
+
specTargets = targetsFromSpec(JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.resolve)(opts.spec), 'utf8')), opts.spec);
|
|
769
|
+
}
|
|
770
|
+
catch (error) {
|
|
771
|
+
process.stderr.write(`✖ ${error instanceof Error ? error.message : String(error)}\n`);
|
|
772
|
+
process.exitCode = 2;
|
|
773
|
+
return;
|
|
774
|
+
}
|
|
775
|
+
try {
|
|
776
|
+
const runtime = await loadEngineRuntime({ apiUrl, offline: opts.offline, runtime: opts.runtime, range: opts.range });
|
|
777
|
+
const report = await simulateVehicle(pkg, runtime, { specTargets });
|
|
778
|
+
if (opts.json)
|
|
779
|
+
console.log(JSON.stringify({ source, spec: opts.spec ? (0, node_path_1.resolve)(opts.spec) : null, ...report }, null, 2));
|
|
780
|
+
else
|
|
781
|
+
console.log(formatSimulationReport(report));
|
|
782
|
+
if (!report.ok)
|
|
783
|
+
process.exitCode = 1;
|
|
784
|
+
}
|
|
785
|
+
catch (error) {
|
|
786
|
+
process.stderr.write(`✖ cannot simulate ${source}: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
787
|
+
process.exitCode = 2;
|
|
788
|
+
}
|
|
789
|
+
});
|
|
710
790
|
vehicleCommand
|
|
711
791
|
.command('audio-check')
|
|
712
792
|
.description('Prove every sound a vehicle declares will actually play: each engine-ladder rung and slot sample the runtime selects must resolve (a local WAV '
|
|
@@ -4786,6 +4866,9 @@ continuum
|
|
|
4786
4866
|
const { readContinuumPublicationFile } = await Promise.resolve().then(() => __importStar(require('./continuum/cli')));
|
|
4787
4867
|
const loaded = await readContinuumPublicationFile(path);
|
|
4788
4868
|
const descriptor = loaded.input;
|
|
4869
|
+
// A vehicle model the backend cannot read as an add-on host would seal a silent non-host (docs/vehicle-simulate.md).
|
|
4870
|
+
const { assertVehiclePackageDescriptorHost } = await Promise.resolve().then(() => __importStar(require('./vehicle/validate')));
|
|
4871
|
+
assertVehiclePackageDescriptorHost(descriptor, loaded.objectPublications);
|
|
4789
4872
|
const publication = explicitPublicListings({
|
|
4790
4873
|
...descriptor, sourceDigest: loaded.sourceDigest, objects: loaded.objectPublications,
|
|
4791
4874
|
...(opts.stage === true ? { stage: true } : {}),
|