@helix3/helix-cli 0.1.18-helix3.348 → 0.1.18-helix3.349
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 +22 -0
- package/dist/index.js +57 -0
- package/dist/index.js.map +1 -1
- package/dist/lib.js +5 -0
- package/dist/lib.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/vehicle/validate.d.ts +76 -0
- package/dist/vehicle/validate.js +515 -0
- package/dist/vehicle/validate.js.map +1 -0
- package/package.json +1 -1
- package/plugins/beamng-vehicle/bin/__pycache__/beamng_abs.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/vendor/vehicle-package-v2.mjs +6209 -734
- package/vendor/README.md +13 -11
- package/vendor/vehicle-package-v2.mjs +6209 -734
- package/vendor/vehicle-package-v2.engine-validator.ts +0 -690
package/README.md
CHANGED
|
@@ -45,6 +45,7 @@ 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 validate <vehicle-package.json> [--glb <model.glb>] [--json]` | One-second local gate for a hand-authored Universal Vehicle, run before `item publish`. 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 and the articulation nodes. Exit `0` no errors, `1` errors, `2` unreadable input. See [Vehicle validate](#vehicle-validate). |
|
|
48
49
|
| `helix item list-slots [--json]` | Print the Character-Creator vocabulary a wearable declares: the cosmetic slot tags, and the genders. |
|
|
49
50
|
| `helix item publish <mesh.glb> --title <t> [--collectible-supply <n>] [initial distribution flags] [--dry-run\|--quote]` | Publish a Standard item by default, or one fixed-supply Collectible. The item definition is separate from acquisition routes. |
|
|
50
51
|
| `helix item distribution create\|list\|update\|disable …` | Manage Marketplace/World Claim or Buy routes without republishing the item or paying another publish fee. |
|
|
@@ -545,6 +546,27 @@ How each field is resolved, and what a null means:
|
|
|
545
546
|
and **no `vault` key** — absent, not `null`, so "not looked up" cannot be read as "not there".
|
|
546
547
|
- Lookups run at most 8 in flight and are cached for the life of the process.
|
|
547
548
|
|
|
549
|
+
## Vehicle validate
|
|
550
|
+
|
|
551
|
+
`helix vehicle validate <vehicle-package.json> [--glb <model.glb>] [--json]` needs no login and no network.
|
|
552
|
+
A vehicle that did not come through the BeamNG bridge is otherwise validated against nothing: `item publish`
|
|
553
|
+
returns ok and the runtime then refuses to spawn it. `helix item publish --kind vehicle` now runs the package
|
|
554
|
+
checks on `--vehicle-package` itself (publish, `--dry-run` and `--quote`) and refuses on any error.
|
|
555
|
+
|
|
556
|
+
Every finding prints as `CODE path — message` plus a `fix:` line; `--json` returns
|
|
557
|
+
`{ ok, errors, warnings, issues: [{ code, level, path, message, fix }], engineCommit }`.
|
|
558
|
+
|
|
559
|
+
| Layer | Codes |
|
|
560
|
+
|---|---|
|
|
561
|
+
| Engine validator (all its errors and warnings) | `ENGINE_VALIDATOR`, `ENGINE_VALIDATOR_CRASH` |
|
|
562
|
+
| 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` |
|
|
563
|
+
| 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) |
|
|
564
|
+
| `--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` |
|
|
565
|
+
|
|
566
|
+
The model frame is glTF: +X is the vehicle's left, +Y up, +Z forward, metres. Wheel nodes are found by name with
|
|
567
|
+
the engine's own resolver (`wheel_lf` / `wheel_rf` / `wheel_lr` / `wheel_rr` and the usual aliases); all four must
|
|
568
|
+
resolve. The engine validator is vendored by `scripts/vendor-vehicle-package-v2.mjs` (see `vendor/README.md`).
|
|
569
|
+
|
|
548
570
|
## Item publish flow
|
|
549
571
|
|
|
550
572
|
`helix item publish` creates an item definition. It defaults to **Standard**: unlimited issuance,
|
package/dist/index.js
CHANGED
|
@@ -40,6 +40,7 @@ const normalMapGuard_1 = require("./character/normalMapGuard");
|
|
|
40
40
|
const itemSetVersion_1 = require("./itemSetVersion");
|
|
41
41
|
const moveUx_1 = require("./visualQa/moveUx");
|
|
42
42
|
const restFrameGuard_1 = require("./character/restFrameGuard");
|
|
43
|
+
const validate_1 = require("./vehicle/validate");
|
|
43
44
|
const commander_1 = require("commander");
|
|
44
45
|
const commands_1 = require("./sceneHlod/commands");
|
|
45
46
|
const placementRequest_1 = require("./placementRequest");
|
|
@@ -555,6 +556,58 @@ deviceCommand
|
|
|
555
556
|
if (reports.some((report) => !report.ok))
|
|
556
557
|
process.exitCode = 1;
|
|
557
558
|
});
|
|
559
|
+
const vehicleCommand = program
|
|
560
|
+
.command('vehicle')
|
|
561
|
+
.description('Check a Universal Vehicle before publish');
|
|
562
|
+
vehicleCommand
|
|
563
|
+
.command('validate')
|
|
564
|
+
.description('Validate a vehicle package (and optionally its GLB) against the engine contract, the backend pin rules and the publish budgets. '
|
|
565
|
+
+ 'Exit 0 = no errors, 1 = errors, 2 = unreadable input')
|
|
566
|
+
.argument('<vehicle-package.json>', 'the vehicle package JSON (the file given to `item publish --vehicle-package`)')
|
|
567
|
+
.option('--glb <model.glb>', 'also check the model: wheel nodes and pivots, frame orientation, budgets, articulation nodes')
|
|
568
|
+
.option('--json', 'print the machine-readable report')
|
|
569
|
+
.action(async (packageFile, opts) => {
|
|
570
|
+
let pkg;
|
|
571
|
+
let glb;
|
|
572
|
+
try {
|
|
573
|
+
pkg = (0, validate_1.readJsonFile)((0, node_path_1.resolve)(packageFile));
|
|
574
|
+
}
|
|
575
|
+
catch (error) {
|
|
576
|
+
process.stderr.write(`✖ cannot read vehicle package ${packageFile}: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
577
|
+
process.exitCode = 2;
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
580
|
+
if (opts.glb !== undefined) {
|
|
581
|
+
try {
|
|
582
|
+
glb = (0, node_fs_1.readFileSync)((0, node_path_1.resolve)(opts.glb));
|
|
583
|
+
}
|
|
584
|
+
catch (error) {
|
|
585
|
+
process.stderr.write(`✖ cannot read GLB ${opts.glb}: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
586
|
+
process.exitCode = 2;
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
let result;
|
|
591
|
+
try {
|
|
592
|
+
result = await (0, validate_1.validateVehicle)(pkg, glb);
|
|
593
|
+
}
|
|
594
|
+
catch (error) {
|
|
595
|
+
process.stderr.write(`✖ cannot validate ${opts.glb ?? packageFile}: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
596
|
+
process.exitCode = 2;
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
if (opts.json) {
|
|
600
|
+
console.log(JSON.stringify({ file: (0, node_path_1.resolve)(packageFile), glb: opts.glb ? (0, node_path_1.resolve)(opts.glb) : null, ...result }, null, 2));
|
|
601
|
+
}
|
|
602
|
+
else {
|
|
603
|
+
if (result.issues.length > 0)
|
|
604
|
+
console.log((0, validate_1.formatVehicleIssues)(result.issues));
|
|
605
|
+
console.log(`${result.ok ? '✔' : '✖'} ${result.ok ? 'Valid' : 'Invalid'} vehicle package: ${result.errors} error${result.errors === 1 ? '' : 's'}, ${result.warnings} warning${result.warnings === 1 ? '' : 's'}`
|
|
606
|
+
+ `${glb ? ' (package + GLB)' : ' (package only — add --glb <model.glb> to check wheels, pivots and budgets)'} [engine ${result.engineCommit.slice(0, 8)}]`);
|
|
607
|
+
}
|
|
608
|
+
if (!result.ok)
|
|
609
|
+
process.exitCode = 1;
|
|
610
|
+
});
|
|
558
611
|
// Validate a token against the API, then persist it. Shared by the browser
|
|
559
612
|
// flow and the --token (CI/headless) path.
|
|
560
613
|
async function finishLogin(apiUrl, token) {
|
|
@@ -1638,6 +1691,7 @@ item
|
|
|
1638
1691
|
if (opts.dryRun) {
|
|
1639
1692
|
try {
|
|
1640
1693
|
const plan = (0, lib_1.planItemPublish)(publishOptions);
|
|
1694
|
+
const vehicleWarnings = await (0, validate_1.assertPublishVehiclePackage)(publishOptions);
|
|
1641
1695
|
await (0, baseColorGuard_1.assertPublishBaseColor)(publishOptions, plan.meshBytes);
|
|
1642
1696
|
await (0, normalMapGuard_1.assertPublishNormalMaps)(publishOptions, plan.meshBytes);
|
|
1643
1697
|
await (0, restFrameGuard_1.assertPublishRestFrames)(publishOptions, plan.meshBytes);
|
|
@@ -1649,6 +1703,7 @@ item
|
|
|
1649
1703
|
mesh: plan.mesh,
|
|
1650
1704
|
thumbnail: plan.thumbnail,
|
|
1651
1705
|
warnings: plan.warnings,
|
|
1706
|
+
...(vehicleWarnings.length > 0 ? { vehicleWarnings } : {}),
|
|
1652
1707
|
}, null, 2));
|
|
1653
1708
|
return;
|
|
1654
1709
|
}
|
|
@@ -1659,6 +1714,8 @@ item
|
|
|
1659
1714
|
console.log(` payload: ${JSON.stringify(plan.payload, null, 2).replace(/\n/g, '\n ')}`);
|
|
1660
1715
|
for (const warning of plan.warnings)
|
|
1661
1716
|
console.log(` • ${warning}`);
|
|
1717
|
+
if (vehicleWarnings.length > 0)
|
|
1718
|
+
console.log((0, validate_1.formatVehicleIssues)(vehicleWarnings));
|
|
1662
1719
|
const status = (0, lib_1.authStatus)();
|
|
1663
1720
|
if (!status.loggedIn)
|
|
1664
1721
|
console.log(' • not logged in — run `helix login` before the real publish');
|