@auraindustry/aurajs 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/README.md +7 -0
  2. package/benchmarks/perf-thresholds.json +27 -0
  3. package/package.json +6 -1
  4. package/src/ai-guidance.mjs +302 -0
  5. package/src/authored-project.mjs +498 -2
  6. package/src/build-contract/capabilities.mjs +87 -1
  7. package/src/build-contract/constants.mjs +1 -0
  8. package/src/build-contract.mjs +2 -0
  9. package/src/bundler.mjs +143 -13
  10. package/src/cli.mjs +681 -13
  11. package/src/commands/packs.mjs +741 -0
  12. package/src/commands/project-authoring.mjs +128 -1
  13. package/src/conformance/cases/app-and-ui-runtime-cases.mjs +1 -2
  14. package/src/conformance/cases/core-runtime-cases.mjs +6 -2
  15. package/src/conformance/cases/scene3d-and-media-cases.mjs +238 -0
  16. package/src/conformance/cases/systems-and-gameplay-cases.mjs +265 -4
  17. package/src/conformance-mobile.mjs +166 -0
  18. package/src/conformance.mjs +89 -30
  19. package/src/evidence-bundle.mjs +242 -0
  20. package/src/headless-test/runtime-coordinator.mjs +186 -33
  21. package/src/headless-test.mjs +2 -0
  22. package/src/helpers/2d/index.mjs +183 -0
  23. package/src/helpers/index.mjs +26 -0
  24. package/src/helpers/starter-utils/adventure-objectives.js +102 -0
  25. package/src/helpers/starter-utils/adventure-world-2d.js +221 -0
  26. package/src/helpers/starter-utils/animation-2d.js +337 -0
  27. package/src/helpers/starter-utils/animation-packaging-2d.js +203 -0
  28. package/src/helpers/starter-utils/atlas-assets-2d.js +111 -0
  29. package/src/helpers/starter-utils/autoplay-debug-2d.js +215 -0
  30. package/src/helpers/starter-utils/avatar-3d.js +404 -0
  31. package/src/helpers/starter-utils/combat-feedback-2d.js +320 -0
  32. package/src/helpers/starter-utils/combat-runtime-2d.js +290 -0
  33. package/src/helpers/starter-utils/core.js +150 -0
  34. package/src/helpers/starter-utils/dialogue-2d.js +351 -0
  35. package/src/helpers/starter-utils/enemy-archetypes-2d.js +68 -0
  36. package/src/helpers/starter-utils/index.js +26 -0
  37. package/src/helpers/starter-utils/inventory-2d.js +268 -0
  38. package/src/helpers/starter-utils/journal-2d.js +267 -0
  39. package/src/helpers/starter-utils/platformer-3d.js +132 -0
  40. package/src/helpers/starter-utils/scene-audio-2d.js +236 -0
  41. package/src/helpers/starter-utils/streamed-world-2d.js +378 -0
  42. package/src/helpers/starter-utils/tilemap-nav-2d.js +499 -0
  43. package/src/helpers/starter-utils/tilemap-world-2d.js +205 -0
  44. package/src/helpers/starter-utils/triggers.js +662 -0
  45. package/src/helpers/starter-utils/tween-2d.js +615 -0
  46. package/src/helpers/starter-utils/wave-director.js +101 -0
  47. package/src/helpers/starter-utils/world-compositor-2d.js +253 -0
  48. package/src/helpers/starter-utils/world-persistence-2d.js +180 -0
  49. package/src/mobile/android/build.mjs +606 -0
  50. package/src/mobile/android/host-artifact.mjs +280 -0
  51. package/src/mobile/ios/build.mjs +1323 -0
  52. package/src/mobile/ios/host-artifact.mjs +819 -0
  53. package/src/mobile/shared/capabilities.mjs +174 -0
  54. package/src/packs/catalog.mjs +259 -0
  55. package/src/perf-benchmark-runner.mjs +17 -12
  56. package/src/perf-benchmark.mjs +408 -4
  57. package/src/publish-command.mjs +303 -6
  58. package/src/replay-runtime.mjs +257 -0
  59. package/src/scaffold/config.mjs +2 -0
  60. package/src/scaffold/fs.mjs +8 -1
  61. package/src/scaffold/project-docs.mjs +43 -1
  62. package/src/scaffold.mjs +4 -0
  63. package/src/session-runtime.mjs +4 -3
  64. package/src/web-conformance.mjs +0 -36
  65. package/templates/create/2d-adventure/config/gameplay/adventure.config.js +9 -6
  66. package/templates/create/2d-adventure/content/gameplay/dialogue.js +85 -0
  67. package/templates/create/2d-adventure/content/gameplay/world.js +32 -36
  68. package/templates/create/2d-adventure/content/gameplay/world.tilemap.json +273 -0
  69. package/templates/create/2d-adventure/docs/design/loop.md +4 -3
  70. package/templates/create/2d-adventure/prefabs/relic.prefab.js +10 -10
  71. package/templates/create/2d-adventure/prefabs/world.prefab.js +127 -74
  72. package/templates/create/2d-adventure/scenes/gameplay.scene.js +603 -112
  73. package/templates/create/2d-adventure/src/runtime/capabilities.js +16 -0
  74. package/templates/create/2d-adventure/ui/hud.screen.js +187 -4
  75. package/templates/create/2d-adventure/ui/journal.screen.js +183 -0
  76. package/templates/create/3d/scenes/gameplay.scene.js +30 -3
  77. package/templates/create/3d/src/runtime/capabilities.js +5 -0
  78. package/templates/create/3d/src/runtime/materials.js +10 -0
  79. package/templates/create/3d-adventure/scenes/gameplay.scene.js +30 -3
  80. package/templates/create/3d-adventure/src/runtime/capabilities.js +5 -0
  81. package/templates/create/3d-adventure/src/runtime/materials.js +11 -0
  82. package/templates/create/3d-collectathon/scenes/gameplay.scene.js +30 -3
  83. package/templates/create/3d-collectathon/src/runtime/capabilities.js +5 -0
  84. package/templates/create/3d-collectathon/src/runtime/materials.js +10 -0
  85. package/templates/create/shared/src/runtime/ui-forms.js +552 -0
  86. package/templates/create/shared/src/starter-utils/adventure-world-2d.js +221 -0
  87. package/templates/create/shared/src/starter-utils/animation-packaging-2d.js +203 -0
  88. package/templates/create/shared/src/starter-utils/atlas-assets-2d.js +111 -0
  89. package/templates/create/shared/src/starter-utils/autoplay-debug-2d.js +215 -0
  90. package/templates/create/shared/src/starter-utils/combat-runtime-2d.js +290 -0
  91. package/templates/create/shared/src/starter-utils/dialogue-2d.js +351 -0
  92. package/templates/create/shared/src/starter-utils/index.js +15 -1
  93. package/templates/create/shared/src/starter-utils/inventory-2d.js +268 -0
  94. package/templates/create/shared/src/starter-utils/journal-2d.js +267 -0
  95. package/templates/create/shared/src/starter-utils/scene-audio-2d.js +236 -0
  96. package/templates/create/shared/src/starter-utils/streamed-world-2d.js +378 -0
  97. package/templates/create/shared/src/starter-utils/tilemap-nav-2d.js +499 -0
  98. package/templates/create/shared/src/starter-utils/tilemap-world-2d.js +205 -0
  99. package/templates/create/shared/src/starter-utils/world-compositor-2d.js +253 -0
  100. package/templates/create/shared/src/starter-utils/world-persistence-2d.js +180 -0
  101. package/templates/create-bin/play.js +36 -7
  102. package/templates/skills/auramaxx/SKILL.md +46 -0
  103. package/templates/skills/auramaxx/project-requirements.md +68 -0
  104. package/templates/skills/auramaxx/starter-recipes.md +104 -0
  105. package/templates/skills/auramaxx/validation-checklist.md +49 -0
  106. package/templates/skills/aurajs/SKILL.md +0 -96
  107. package/templates/skills/aurajs/api-contract-3d.md +0 -7
  108. package/templates/skills/aurajs/api-contract.md +0 -7
package/src/cli.mjs CHANGED
@@ -28,15 +28,24 @@ import {
28
28
  MAKE_SUPPORTED_KINDS_HELP_TEXT,
29
29
  MAKE_USAGE,
30
30
  PREFAB_ROLE_OPTION_LIST,
31
+ VENDOR_USAGE,
31
32
  cmdCheck,
32
33
  cmdCreate,
33
34
  cmdExplain,
34
35
  cmdFrom,
35
36
  cmdInit,
36
37
  cmdMake,
38
+ cmdVendor,
37
39
  } from './commands/project-authoring.mjs';
40
+ import { PACKS_USAGE, cmdPacks } from './commands/packs.mjs';
38
41
  import { packageAssets } from './asset-pack.mjs';
39
- import { writeBuildManifest, writeWebBuildArtifacts } from './build-contract.mjs';
42
+ import {
43
+ buildRuntimeConfig,
44
+ readProjectCapabilityDeclaration,
45
+ writeBuildManifest,
46
+ writeCanonicalJsonFile,
47
+ writeWebBuildArtifacts,
48
+ } from './build-contract.mjs';
40
49
  import { discoverProjectIconPath, SUPPORTED_BUILD_ICON_EXTENSIONS } from './icon-discovery.mjs';
41
50
  import { runPublishCommand } from './publish-command.mjs';
42
51
  import { runExternalAssetsCommand } from './self-hosted-assets.mjs';
@@ -55,6 +64,10 @@ import {
55
64
  } from './headless-action.mjs';
56
65
  import { loadConfig, ConfigError } from './config.mjs';
57
66
  import { ensureHostBinaryAvailable, HostBinaryResolutionError } from './host-binary.mjs';
67
+ import { buildAndroidArtifacts, stageAndroidProject } from './mobile/android/build.mjs';
68
+ import { produceAndroidHostArtifact } from './mobile/android/host-artifact.mjs';
69
+ import { buildIosArtifacts, stageIosProject } from './mobile/ios/build.mjs';
70
+ import { produceIosHostArtifact } from './mobile/ios/host-artifact.mjs';
58
71
  import { runConformanceCommand } from './conformance-runner.mjs';
59
72
  import { buildRuntimeInspectorSnapshotReport } from './conformance.mjs';
60
73
  import {
@@ -113,6 +126,16 @@ import {
113
126
  writeSessionRegistry,
114
127
  } from './session-client.mjs';
115
128
  import { GAME_ACTION_REQUEST_SCHEMA_VERSION } from './game-action-runtime.mjs';
129
+ import {
130
+ captureHeadlessEvidenceBundle,
131
+ EvidenceBundleError,
132
+ serializeEvidenceManifest,
133
+ } from './evidence-bundle.mjs';
134
+ import {
135
+ ReplayError,
136
+ runHeadlessReplay,
137
+ serializeReplayReport,
138
+ } from './replay-runtime.mjs';
116
139
 
117
140
  // ---------------------------------------------------------------------------
118
141
  // Version
@@ -192,6 +215,35 @@ const COMMANDS = {
192
215
  ' - This is an authoring command on `aura`, not a generated game-wrapper verb.',
193
216
  ],
194
217
  },
218
+ vendor: {
219
+ description: 'Vendor supported helper files into the current AuraJS project',
220
+ usage: VENDOR_USAGE,
221
+ details: [
222
+ ' Notes:',
223
+ ' - `aura vendor helpers` seeds the packaged starter helper copies into src/starter-utils/.',
224
+ ' - Re-run with `--force` to overwrite local helper files and resync them to the packaged version.',
225
+ ' - Use `--json` when you want machine-readable created/overwritten/skipped file lists.',
226
+ ],
227
+ },
228
+ packs: {
229
+ description: 'List and register optional AuraJS packs and bundles',
230
+ usage: PACKS_USAGE,
231
+ details: [
232
+ ' Subcommands:',
233
+ ' list [--json] List known optional packs and bundles',
234
+ ' show <id> [--json] Show one pack or bundle entry',
235
+ ' add <id> [--source package|workspace] [--install] Register one pack or bundle in the current AuraJS project',
236
+ ' vendor <id> [--source auto|workspace|installed|github] [--ref <git-ref>] [--force] Copy pack payloads into the project',
237
+ '',
238
+ ' Notes:',
239
+ ' - `aura packs add` writes optional package dependencies plus aura.packs.json metadata.',
240
+ ' - `--workspace` uses local file: specs that point at pack packages in the AuraJS source checkout.',
241
+ ' - `aura packs vendor` is the editable-content lane: it copies pack payloads into project-local folders such as content/, docs/, or assets/.',
242
+ ' - `--source github` on vendor fetches the pack archive from its registered GitHub repo/ref and vendors from that payload.',
243
+ ' - `--install` runs `npm install` after package.json and aura.packs.json are updated.',
244
+ ' - Bundles expand to their member packs so AI/project introspection can see the concrete installed lanes.',
245
+ ],
246
+ },
195
247
  explain: {
196
248
  description: 'Explain how the current AuraJS project is wired',
197
249
  usage: 'aura explain [--json]',
@@ -233,15 +285,17 @@ const COMMANDS = {
233
285
  },
234
286
  build: {
235
287
  description: 'Build release artifacts (native or web)',
236
- usage: 'aura build [--target <windows|mac|linux|all|web|gb|gbc|gba>] [--asset-mode <embed|sibling>]',
288
+ usage: 'aura build [--target <windows|mac|linux|all|web|android|ios|mobile|gb|gbc|gba>] [--asset-mode <embed|sibling>]',
237
289
  details: [
238
290
  ' Options:',
239
- ' --target <windows|mac|linux|all|web|gb|gbc|gba> Build target',
291
+ ' --target <windows|mac|linux|all|web|android|ios|mobile|gb|gbc|gba> Build target',
240
292
  ' --asset-mode <embed|sibling> Package assets in pak or sibling assets/',
241
293
  '',
242
294
  ' Notes:',
243
295
  ' - `--target web` emits browser artifacts under `build/web` for the current browser-backed subset.',
244
296
  ' - Native targets are current host only in v1.',
297
+ ' - `--target android|ios|mobile` produces host artifacts, stages mobile package roots, and runs Gradle/Xcode export when prerequisites are present; otherwise manifests stay reason-coded and staged.',
298
+ ' - Mobile targets require `--asset-mode embed` in v1.',
245
299
  ' - `--target all` keeps current host output; use CI matrix for all platforms.',
246
300
  ' - Browser runtime truth today: current 2D/input/assets/storage path is supported; `aura.state` and `aura.net` remain reason-coded unsupported.',
247
301
  ' - `--target gb|gbc|gba` routes through the Aura Retro lane under `src/cli/src/retro` and writes cartridge-oriented artifacts under `build/<target>`.',
@@ -280,10 +334,14 @@ const COMMANDS = {
280
334
  },
281
335
  publish: {
282
336
  description: 'Publish the current game package through npm',
283
- usage: 'aura publish [--dry-run] [npm publish options]',
337
+ usage: 'aura publish [--dry-run] [--name <package>] [--version <semver>] [--yes] [npm publish options]',
284
338
  details: [
285
339
  ' Notes:',
286
340
  ' - `aura publish` is the canonical npm-first publish lane for game projects.',
341
+ ' - Interactive terminals can keep or update package.json name/version before publish.',
342
+ ' - `--name <package>` sets the package name before validation and publish.',
343
+ ' - `--version <semver>` sets the package version before validation and publish.',
344
+ ' - `--yes` skips the prompt and uses current package metadata.',
287
345
  ' - Scoped package names default to `--access public` unless explicitly overridden.',
288
346
  ' - npm publish lifecycle recursion is skipped cleanly instead of re-entering the command.',
289
347
  ' - For oversized payloads, generate self-hosted metadata with `aura external-assets generate` after you upload assets to your own HTTPS host.',
@@ -315,6 +373,49 @@ const COMMANDS = {
315
373
  },
316
374
  clean: { description: 'Delete build artifacts', usage: 'aura clean' },
317
375
  test: { description: 'Run game logic in headless mode', usage: 'aura test [file] [--width N] [--height N] [--frames N]' },
376
+ evidence: {
377
+ description: 'Capture one headless evidence bundle for the current authored project',
378
+ usage: 'aura evidence [file] [--replay <replay.json>] [--frames N] [--width N] [--height N] [--out-dir <path>] [--output <path|->]',
379
+ details: [
380
+ ' Options:',
381
+ ' --replay <replay.json> Execute one deterministic replay before capture and include replay artifacts',
382
+ ' --frames <N> Number of lifecycle frames to execute before capture (or after replay when --replay is set)',
383
+ ' --width <N> Override headless viewport width',
384
+ ' --height <N> Override headless viewport height',
385
+ ' --out-dir <path> Directory for manifest.json, summary.json, state.json, inspect.json, perf.json, and optional replay artifacts',
386
+ ' --output <path|-> Write the manifest JSON to file (or "-" for stdout)',
387
+ ' --compact Compact manifest JSON output (single line)',
388
+ ' --pretty Pretty manifest JSON output (default)',
389
+ ' --file <path> Override entry file (default: build.entry from config)',
390
+ '',
391
+ ' Notes:',
392
+ ' - This is headless-first and currently captures one combined evidence bundle per run.',
393
+ ' - The bundle writes manifest.json, summary.json, state.json, inspect.json, and perf.json under --out-dir.',
394
+ ' - When --replay is set, the bundle also writes replay.json and replay-report.json.',
395
+ ],
396
+ },
397
+ replay: {
398
+ description: 'Run one deterministic headless replay against the current authored project',
399
+ usage: 'aura replay run <replay.json> [--output <path|->] [--state-output <path|->] [--file <path>] [--width N] [--height N] [--compact]',
400
+ details: [
401
+ ' Subcommands:',
402
+ ' run <replay.json> Execute one headless replay payload with deterministic input injection',
403
+ '',
404
+ ' Options:',
405
+ ' run:',
406
+ ' --output <path|-> Write replay report JSON to file (or "-" for stdout)',
407
+ ' --state-output <path|-> Write final canonical state payload to file (or "-" for stdout)',
408
+ ' --file <path> Override entry file (default: build.entry from config)',
409
+ ' --width <N> Override headless viewport width',
410
+ ' --height <N> Override headless viewport height',
411
+ ' --compact Compact JSON output (single line)',
412
+ ' --pretty Pretty JSON output (default)',
413
+ '',
414
+ ' Notes:',
415
+ ' - Replay is headless-first today and uses deterministic input injection plus optional fingerprint checkpoints.',
416
+ ' - Use `expectFingerprint` checkpoints inside the replay payload to stop on divergence.',
417
+ ],
418
+ },
318
419
  conformance: { description: 'Run API conformance suites in headless mode', usage: 'aura conformance [--mode shim|native|both] [--json]' },
319
420
  state: {
320
421
  description: 'Export, diff, and patch canonical game-state snapshots',
@@ -518,7 +619,16 @@ if (HAS_AURAPM_PREVIEW) {
518
619
  };
519
620
  }
520
621
 
521
- const SUPPORTED_BUILD_TARGETS = new Set(['windows', 'mac', 'linux', 'all', 'web', ...SUPPORTED_RETRO_BUILD_TARGETS]);
622
+ const SUPPORTED_MOBILE_BUILD_TARGETS = new Set(['android', 'ios', 'mobile']);
623
+ const SUPPORTED_BUILD_TARGETS = new Set([
624
+ 'windows',
625
+ 'mac',
626
+ 'linux',
627
+ 'all',
628
+ 'web',
629
+ ...SUPPORTED_MOBILE_BUILD_TARGETS,
630
+ ...SUPPORTED_RETRO_BUILD_TARGETS,
631
+ ]);
522
632
  const SUPPORTED_STATE_EXPORT_MODES = new Set(['headless', 'native', 'shim']);
523
633
  const STATE_MUTATION_AUDIT_SCHEMA_VERSION = 'aurajs.state-mutation-audit.v1';
524
634
  const WEB_DEV_REFRESH_SIGNAL_SCHEMA = 'aurajs.web-dev-refresh.v1';
@@ -557,6 +667,7 @@ function printHelp() {
557
667
  ' create Scaffold a new game project',
558
668
  ' from Convert a supported foreign project into a new AuraJS project',
559
669
  ' make Generate authored project files from templates',
670
+ ' vendor Vendor supported helper files into the current project',
560
671
  ' explain Explain how the current project is wired',
561
672
  ' check Validate authored project wiring',
562
673
  ' play Start the game with a live dev session',
@@ -636,6 +747,10 @@ function currentPlatformTarget() {
636
747
  return 'linux';
637
748
  }
638
749
 
750
+ function isMobileBuildTarget(target) {
751
+ return SUPPORTED_MOBILE_BUILD_TARGETS.has(target);
752
+ }
753
+
639
754
  function parseAssetMode(args) {
640
755
  for (let i = 0; i < args.length; i += 1) {
641
756
  if (args[i] === '--asset-mode' && args[i + 1]) {
@@ -772,6 +887,260 @@ function parseStateExportArgs(args) {
772
887
  return parsed;
773
888
  }
774
889
 
890
+ function parseEvidenceArgs(args) {
891
+ const parsed = {
892
+ file: null,
893
+ replayPath: null,
894
+ frames: 1,
895
+ width: null,
896
+ height: null,
897
+ outDir: resolve(process.cwd(), '.aura/evidence/latest'),
898
+ outputPath: null,
899
+ compact: false,
900
+ };
901
+
902
+ let positionalFileSeen = false;
903
+ let formattingFlag = 'pretty';
904
+ let framesSpecified = false;
905
+
906
+ for (let i = 0; i < args.length; i += 1) {
907
+ const token = args[i];
908
+
909
+ if (token === '--frames') {
910
+ const value = readRequiredOptionValueWithReasonCode(args, i, '--frames', 'invalid_evidence_args');
911
+ parsed.frames = parseNonNegativeIntegerArg(value, '--frames', 'invalid_evidence_args');
912
+ framesSpecified = true;
913
+ i += 1;
914
+ continue;
915
+ }
916
+ if (token.startsWith('--frames=')) {
917
+ parsed.frames = parseNonNegativeIntegerArg(token.slice('--frames='.length), '--frames', 'invalid_evidence_args');
918
+ framesSpecified = true;
919
+ continue;
920
+ }
921
+
922
+ if (token === '--replay') {
923
+ const value = readRequiredOptionValueWithReasonCode(args, i, '--replay', 'invalid_evidence_args');
924
+ parsed.replayPath = normalizeStateCommandInputPath(value, 'replay', 'invalid_evidence_args');
925
+ i += 1;
926
+ continue;
927
+ }
928
+ if (token.startsWith('--replay=')) {
929
+ parsed.replayPath = normalizeStateCommandInputPath(token.slice('--replay='.length), 'replay', 'invalid_evidence_args');
930
+ continue;
931
+ }
932
+
933
+ if (token === '--width') {
934
+ const value = readRequiredOptionValueWithReasonCode(args, i, '--width', 'invalid_evidence_args');
935
+ parsed.width = parsePositiveIntegerArg(value, '--width', 'invalid_evidence_args');
936
+ i += 1;
937
+ continue;
938
+ }
939
+ if (token.startsWith('--width=')) {
940
+ parsed.width = parsePositiveIntegerArg(token.slice('--width='.length), '--width', 'invalid_evidence_args');
941
+ continue;
942
+ }
943
+
944
+ if (token === '--height') {
945
+ const value = readRequiredOptionValueWithReasonCode(args, i, '--height', 'invalid_evidence_args');
946
+ parsed.height = parsePositiveIntegerArg(value, '--height', 'invalid_evidence_args');
947
+ i += 1;
948
+ continue;
949
+ }
950
+ if (token.startsWith('--height=')) {
951
+ parsed.height = parsePositiveIntegerArg(token.slice('--height='.length), '--height', 'invalid_evidence_args');
952
+ continue;
953
+ }
954
+
955
+ if (token === '--out-dir') {
956
+ const value = readRequiredOptionValueWithReasonCode(args, i, '--out-dir', 'invalid_evidence_args');
957
+ parsed.outDir = resolve(process.cwd(), value);
958
+ i += 1;
959
+ continue;
960
+ }
961
+ if (token.startsWith('--out-dir=')) {
962
+ parsed.outDir = resolve(process.cwd(), token.slice('--out-dir='.length));
963
+ continue;
964
+ }
965
+
966
+ if (token === '--output' || token === '--out') {
967
+ const value = readRequiredOptionValueWithReasonCode(args, i, token, 'invalid_evidence_args');
968
+ parsed.outputPath = normalizeStateExportOutput(value);
969
+ i += 1;
970
+ continue;
971
+ }
972
+ if (token.startsWith('--output=')) {
973
+ parsed.outputPath = normalizeStateExportOutput(token.slice('--output='.length));
974
+ continue;
975
+ }
976
+ if (token.startsWith('--out=')) {
977
+ parsed.outputPath = normalizeStateExportOutput(token.slice('--out='.length));
978
+ continue;
979
+ }
980
+
981
+ if (token === '--compact') {
982
+ if (formattingFlag === 'pretty') {
983
+ parsed.compact = true;
984
+ formattingFlag = 'compact';
985
+ continue;
986
+ }
987
+ if (formattingFlag === 'compact') {
988
+ continue;
989
+ }
990
+ throw new StateExportError('invalid_evidence_args', 'Cannot use --compact and --pretty together.');
991
+ }
992
+ if (token === '--pretty') {
993
+ if (formattingFlag === 'compact') {
994
+ throw new StateExportError('invalid_evidence_args', 'Cannot use --compact and --pretty together.');
995
+ }
996
+ parsed.compact = false;
997
+ formattingFlag = 'pretty';
998
+ continue;
999
+ }
1000
+
1001
+ if (token === '--file') {
1002
+ const value = readRequiredOptionValueWithReasonCode(args, i, '--file', 'invalid_evidence_args');
1003
+ parsed.file = normalizeStateExportFile(value);
1004
+ i += 1;
1005
+ continue;
1006
+ }
1007
+ if (token.startsWith('--file=')) {
1008
+ parsed.file = normalizeStateExportFile(token.slice('--file='.length));
1009
+ continue;
1010
+ }
1011
+
1012
+ if (token.startsWith('--')) {
1013
+ throw new StateExportError('invalid_evidence_args', `Unknown evidence option: ${token}`);
1014
+ }
1015
+
1016
+ if (positionalFileSeen) {
1017
+ throw new StateExportError('invalid_evidence_args', `Unexpected argument: ${token}`);
1018
+ }
1019
+ positionalFileSeen = true;
1020
+ parsed.file = normalizeStateExportFile(token);
1021
+ }
1022
+
1023
+ if (parsed.replayPath && framesSpecified !== true) {
1024
+ parsed.frames = 0;
1025
+ }
1026
+
1027
+ return parsed;
1028
+ }
1029
+
1030
+ function parseReplayRunArgs(args) {
1031
+ const parsed = {
1032
+ replayPath: null,
1033
+ file: null,
1034
+ width: null,
1035
+ height: null,
1036
+ outputPath: null,
1037
+ stateOutputPath: null,
1038
+ compact: false,
1039
+ };
1040
+
1041
+ let formattingFlag = 'pretty';
1042
+
1043
+ for (let i = 0; i < args.length; i += 1) {
1044
+ const token = args[i];
1045
+
1046
+ if (token === '--output' || token === '--out') {
1047
+ const value = readRequiredOptionValueWithReasonCode(args, i, token, 'invalid_replay_args');
1048
+ parsed.outputPath = normalizeStateExportOutput(value);
1049
+ i += 1;
1050
+ continue;
1051
+ }
1052
+ if (token.startsWith('--output=')) {
1053
+ parsed.outputPath = normalizeStateExportOutput(token.slice('--output='.length));
1054
+ continue;
1055
+ }
1056
+ if (token.startsWith('--out=')) {
1057
+ parsed.outputPath = normalizeStateExportOutput(token.slice('--out='.length));
1058
+ continue;
1059
+ }
1060
+
1061
+ if (token === '--state-output') {
1062
+ const value = readRequiredOptionValueWithReasonCode(args, i, '--state-output', 'invalid_replay_args');
1063
+ parsed.stateOutputPath = normalizeStateExportOutput(value);
1064
+ i += 1;
1065
+ continue;
1066
+ }
1067
+ if (token.startsWith('--state-output=')) {
1068
+ parsed.stateOutputPath = normalizeStateExportOutput(token.slice('--state-output='.length));
1069
+ continue;
1070
+ }
1071
+
1072
+ if (token === '--file') {
1073
+ const value = readRequiredOptionValueWithReasonCode(args, i, '--file', 'invalid_replay_args');
1074
+ parsed.file = normalizeStateExportFile(value);
1075
+ i += 1;
1076
+ continue;
1077
+ }
1078
+ if (token.startsWith('--file=')) {
1079
+ parsed.file = normalizeStateExportFile(token.slice('--file='.length));
1080
+ continue;
1081
+ }
1082
+
1083
+ if (token === '--width') {
1084
+ const value = readRequiredOptionValueWithReasonCode(args, i, '--width', 'invalid_replay_args');
1085
+ parsed.width = parsePositiveIntegerArg(value, '--width', 'invalid_replay_args');
1086
+ i += 1;
1087
+ continue;
1088
+ }
1089
+ if (token.startsWith('--width=')) {
1090
+ parsed.width = parsePositiveIntegerArg(token.slice('--width='.length), '--width', 'invalid_replay_args');
1091
+ continue;
1092
+ }
1093
+
1094
+ if (token === '--height') {
1095
+ const value = readRequiredOptionValueWithReasonCode(args, i, '--height', 'invalid_replay_args');
1096
+ parsed.height = parsePositiveIntegerArg(value, '--height', 'invalid_replay_args');
1097
+ i += 1;
1098
+ continue;
1099
+ }
1100
+ if (token.startsWith('--height=')) {
1101
+ parsed.height = parsePositiveIntegerArg(token.slice('--height='.length), '--height', 'invalid_replay_args');
1102
+ continue;
1103
+ }
1104
+
1105
+ if (token === '--compact') {
1106
+ if (formattingFlag === 'pretty') {
1107
+ parsed.compact = true;
1108
+ formattingFlag = 'compact';
1109
+ continue;
1110
+ }
1111
+ if (formattingFlag === 'compact') {
1112
+ continue;
1113
+ }
1114
+ throw new StateExportError('invalid_replay_args', 'Cannot use --compact and --pretty together.');
1115
+ }
1116
+ if (token === '--pretty') {
1117
+ if (formattingFlag === 'compact') {
1118
+ throw new StateExportError('invalid_replay_args', 'Cannot use --compact and --pretty together.');
1119
+ }
1120
+ parsed.compact = false;
1121
+ formattingFlag = 'pretty';
1122
+ continue;
1123
+ }
1124
+
1125
+ if (token.startsWith('--')) {
1126
+ throw new StateExportError('invalid_replay_args', `Unknown replay option: ${token}`);
1127
+ }
1128
+
1129
+ if (parsed.replayPath === null) {
1130
+ parsed.replayPath = normalizeStateCommandInputPath(token, 'replay', 'invalid_replay_args');
1131
+ continue;
1132
+ }
1133
+
1134
+ throw new StateExportError('invalid_replay_args', `Unexpected argument: ${token}`);
1135
+ }
1136
+
1137
+ if (!parsed.replayPath) {
1138
+ throw new StateExportError('invalid_replay_args', 'Replay run requires a replay JSON path.');
1139
+ }
1140
+
1141
+ return parsed;
1142
+ }
1143
+
775
1144
  function parseStateDiffArgs(args) {
776
1145
  const parsed = {
777
1146
  beforePath: null,
@@ -2757,10 +3126,7 @@ function parseDevArgs(args) {
2757
3126
 
2758
3127
  function normalizeDevTarget(value) {
2759
3128
  const normalized = String(value || '').trim().toLowerCase();
2760
- if (normalized === 'macos' || normalized === 'darwin' || normalized === 'mac') return 'mac';
2761
- if (normalized === 'win' || normalized === 'win32' || normalized === 'windows') return 'windows';
2762
3129
  if (normalized === 'browser' || normalized === 'web') return 'web';
2763
- if (normalized === 'linux') return 'linux';
2764
3130
  throw new StateExportError(
2765
3131
  'invalid_dev_args',
2766
3132
  `Unsupported dev target "${value}". Supported values: web.`,
@@ -3438,6 +3804,96 @@ function resolveBuildIdentity(config) {
3438
3804
  };
3439
3805
  }
3440
3806
 
3807
+ function sanitizeMobileIdentifierFragment(value, fallback = 'game') {
3808
+ const normalized = String(value || '')
3809
+ .trim()
3810
+ .toLowerCase()
3811
+ .replace(/[^a-z0-9]+/g, '');
3812
+ return normalized.length > 0 ? normalized : fallback;
3813
+ }
3814
+
3815
+ function resolveAndroidBuildIdentity(identity) {
3816
+ const slug = sanitizeExecutableBaseName(identity.executableBaseName || 'game').toLowerCase();
3817
+ return {
3818
+ name: identity.name,
3819
+ slug,
3820
+ applicationId: process.env.AURA_ANDROID_APPLICATION_ID
3821
+ || `dev.aurajs.${sanitizeMobileIdentifierFragment(slug)}`,
3822
+ versionCode: Number.parseInt(process.env.AURA_ANDROID_VERSION_CODE || '1', 10) || 1,
3823
+ versionName: process.env.AURA_ANDROID_VERSION_NAME || identity.version,
3824
+ };
3825
+ }
3826
+
3827
+ function resolveIosBuildIdentity(identity) {
3828
+ const slug = sanitizeExecutableBaseName(identity.executableBaseName || 'game').toLowerCase();
3829
+ return {
3830
+ appName: identity.name,
3831
+ bundleId: process.env.AURA_IOS_BUNDLE_ID
3832
+ || `com.aurajs.${sanitizeMobileIdentifierFragment(slug)}`,
3833
+ version: identity.version,
3834
+ buildNumber: process.env.AURA_IOS_BUILD_NUMBER || '1',
3835
+ };
3836
+ }
3837
+
3838
+ function buildMobileSourceArtifacts({
3839
+ projectRoot,
3840
+ outDir,
3841
+ entryFile,
3842
+ identity,
3843
+ modules,
3844
+ windowConfig,
3845
+ }) {
3846
+ const sourceOutRoot = resolve(projectRoot, outDir, '.mobile-source');
3847
+ const bundle = runBuildStage('bundle', () => bundleProject({
3848
+ projectRoot,
3849
+ mode: 'build',
3850
+ entryFile,
3851
+ outFile: resolve(sourceOutRoot, 'js/game.bundle.js'),
3852
+ }));
3853
+ const assets = runBuildStage('asset-pack', () => packageAssets({
3854
+ projectRoot,
3855
+ outRoot: sourceOutRoot,
3856
+ mode: 'embed',
3857
+ }));
3858
+ const buildManifestPath = runBuildStage('build-contract', () => writeBuildManifest({
3859
+ outRoot: sourceOutRoot,
3860
+ bundlePath: bundle.outFile,
3861
+ assetsManifestPath: assets.manifestPath,
3862
+ executablePath: null,
3863
+ assetMode: assets.mode,
3864
+ identity: {
3865
+ name: identity.name,
3866
+ version: identity.version,
3867
+ windowTitle: identity.name,
3868
+ executableBaseName: identity.executableBaseName,
3869
+ executableFileName: null,
3870
+ },
3871
+ icon: null,
3872
+ }));
3873
+ const runtimeConfigPath = runBuildStage('runtime-config', () => {
3874
+ const capabilityDeclaration = readProjectCapabilityDeclaration({
3875
+ projectRoot,
3876
+ modules: modules || {},
3877
+ });
3878
+ const runtimeConfig = buildRuntimeConfig({
3879
+ windowConfig: windowConfig || {},
3880
+ modules: modules || {},
3881
+ capabilityDeclaration,
3882
+ });
3883
+ const outPath = resolve(sourceOutRoot, 'runtime-config.json');
3884
+ writeCanonicalJsonFile(outPath, runtimeConfig);
3885
+ return outPath;
3886
+ });
3887
+
3888
+ return {
3889
+ outRoot: sourceOutRoot,
3890
+ bundle,
3891
+ assets,
3892
+ buildManifestPath,
3893
+ runtimeConfigPath,
3894
+ };
3895
+ }
3896
+
3441
3897
  function resolveBuildIcon({ projectRoot, targetOutRoot, iconPath, assetDir = 'assets' }) {
3442
3898
  const configuredPath = typeof iconPath === 'string' && iconPath.trim().length > 0
3443
3899
  ? iconPath.trim()
@@ -4221,7 +4677,7 @@ async function cmdDevNative(devArgs = {}) {
4221
4677
 
4222
4678
  async function cmdDev(args) {
4223
4679
  const parsed = parseDevArgs(args);
4224
- const target = parsed.target || parseTarget(args);
4680
+ const target = parsed.target;
4225
4681
  if (target === 'web') {
4226
4682
  if (parsed.restoreKind || parsed.session !== null || parsed.sessionName || parsed.runtimeArgs.length > 0) {
4227
4683
  throw new StateExportError(
@@ -4238,7 +4694,7 @@ async function cmdDev(args) {
4238
4694
  async function cmdBuild(args, options = {}) {
4239
4695
  const target = parseTarget(args);
4240
4696
  if (target && !SUPPORTED_BUILD_TARGETS.has(target)) {
4241
- throw new Error(`Unsupported --target "${target}". Supported values: windows, mac, linux, all, web, gb, gbc, gba.`);
4697
+ throw new Error(`Unsupported --target "${target}". Supported values: windows, mac, linux, all, web, android, ios, mobile, gb, gbc, gba.`);
4242
4698
  }
4243
4699
 
4244
4700
  const requestedTarget = target || currentPlatformTarget();
@@ -4247,6 +4703,8 @@ async function cmdBuild(args, options = {}) {
4247
4703
  const entryFile = options.entryFile || config.build.entry;
4248
4704
  const logOutput = options.log !== false;
4249
4705
  const identity = resolveBuildIdentity(config);
4706
+ const cliAssetMode = parseAssetMode(args);
4707
+ const selectedAssetMode = args.some((a) => a === '--asset-mode') ? cliAssetMode : config.build.assetMode;
4250
4708
  if (isRetroBuildTarget(requestedTarget)) {
4251
4709
  return buildRetroProject({
4252
4710
  projectRoot,
@@ -4304,6 +4762,136 @@ async function cmdBuild(args, options = {}) {
4304
4762
  loaderPath: webContract.loaderPath,
4305
4763
  };
4306
4764
  }
4765
+ if (isMobileBuildTarget(requestedTarget)) {
4766
+ if (selectedAssetMode !== 'embed') {
4767
+ throw new Error('Mobile builds require `--asset-mode embed` in v1.');
4768
+ }
4769
+
4770
+ const mobileSource = buildMobileSourceArtifacts({
4771
+ projectRoot,
4772
+ outDir: config.build.outDir,
4773
+ entryFile,
4774
+ identity,
4775
+ modules: config.modules,
4776
+ windowConfig: config.window,
4777
+ });
4778
+
4779
+ const androidRequested = requestedTarget === 'android' || requestedTarget === 'mobile';
4780
+ const iosRequested = requestedTarget === 'ios' || requestedTarget === 'mobile';
4781
+
4782
+ const androidHostArtifact = androidRequested
4783
+ ? runBuildStage('android-host-artifact', () => produceAndroidHostArtifact({
4784
+ sourceLibraryPath: process.env.AURA_ANDROID_NATIVE_HOST_LIBRARY || null,
4785
+ env: process.env,
4786
+ }))
4787
+ : null;
4788
+ const androidStage = androidRequested
4789
+ ? runBuildStage('android-stage', () => stageAndroidProject({
4790
+ projectRoot,
4791
+ outRoot: resolve(projectRoot, config.build.outDir, 'android'),
4792
+ bundlePath: mobileSource.bundle.outFile,
4793
+ assetsPackPath: mobileSource.assets.packPath,
4794
+ sourceBuildManifestPath: mobileSource.buildManifestPath,
4795
+ runtimeConfigPath: mobileSource.runtimeConfigPath,
4796
+ nativeHostLibraryPath: androidHostArtifact?.ok ? androidHostArtifact.distArtifactPath : null,
4797
+ identity: resolveAndroidBuildIdentity(identity),
4798
+ }))
4799
+ : null;
4800
+ const androidBuild = androidRequested
4801
+ ? runBuildStage('android-build', () => buildAndroidArtifacts({
4802
+ outRoot: androidStage.outRoot,
4803
+ manifestPath: androidStage.manifestPath,
4804
+ env: process.env,
4805
+ }))
4806
+ : null;
4807
+ const iosHostArtifact = iosRequested
4808
+ ? runBuildStage('ios-host-artifact', () => produceIosHostArtifact({
4809
+ hostFrameworkPath: process.env.AURA_IOS_HOST_FRAMEWORK_PATH || null,
4810
+ environment: process.env,
4811
+ }))
4812
+ : null;
4813
+ const iosStage = iosRequested
4814
+ ? runBuildStage('ios-stage', () => stageIosProject({
4815
+ projectRoot,
4816
+ outRoot: resolve(projectRoot, config.build.outDir, 'ios'),
4817
+ sourceBuildRoot: mobileSource.outRoot,
4818
+ buildManifestPath: mobileSource.buildManifestPath,
4819
+ assetsManifestPath: mobileSource.assets.manifestPath,
4820
+ bundlePath: mobileSource.bundle.outFile,
4821
+ hostFrameworkPath: iosHostArtifact?.ok ? iosHostArtifact.artifactPath : null,
4822
+ identity: resolveIosBuildIdentity(identity),
4823
+ }))
4824
+ : null;
4825
+ const iosBuild = iosRequested
4826
+ ? runBuildStage('ios-build', () => buildIosArtifacts({
4827
+ outRoot: iosStage.outRoot,
4828
+ manifestPath: iosStage.manifestPath,
4829
+ environment: process.env,
4830
+ }))
4831
+ : null;
4832
+
4833
+ if (logOutput) {
4834
+ console.log('\n aura build: complete.');
4835
+ console.log(` Game: ${identity.name}`);
4836
+ console.log(` Version: ${identity.version}`);
4837
+ console.log(` Time: ${mobileSource.bundle.elapsedMs}ms`);
4838
+ console.log(` Assets: ${mobileSource.assets.assetCount} (${mobileSource.assets.mode})`);
4839
+ console.log(
4840
+ ` Asset updates: copied=${mobileSource.assets.incremental.copied} skipped=${mobileSource.assets.incremental.skipped} removed=${mobileSource.assets.incremental.removed}`,
4841
+ );
4842
+ console.log(` Output target: ${requestedTarget}`);
4843
+ if (androidStage) {
4844
+ const androidToolchain = androidStage.toolchain.ready
4845
+ ? 'ready'
4846
+ : (androidStage.toolchain.reasonCodes.join(', ') || 'missing');
4847
+ console.log(` Android stage: ${resolve(projectRoot, config.build.outDir, 'android')}`);
4848
+ console.log(` Android host artifact: ${androidHostArtifact?.reasonCode || 'not_requested'}`);
4849
+ console.log(` Android toolchain: ${androidToolchain}`);
4850
+ console.log(` Android build: ${androidBuild?.reasonCode || 'not_run'}`);
4851
+ }
4852
+ if (iosStage) {
4853
+ const iosToolchain = iosStage.toolchain.reasonCode;
4854
+ console.log(` iOS stage: ${resolve(projectRoot, config.build.outDir, 'ios')}`);
4855
+ console.log(` iOS host artifact: ${iosHostArtifact?.reasonCode || 'not_requested'}`);
4856
+ console.log(` iOS toolchain: ${iosToolchain}`);
4857
+ console.log(` iOS build: ${iosBuild?.reasonCode || 'not_run'}`);
4858
+ }
4859
+ console.log(' Note: mobile in v1 produces host artifacts, stages package roots, and runs Gradle/Xcode export when prerequisites are present; otherwise manifests stay reason-coded and staged.');
4860
+ console.log('');
4861
+ }
4862
+
4863
+ return {
4864
+ projectRoot,
4865
+ config,
4866
+ requestedTarget,
4867
+ platformTarget: currentPlatformTarget(),
4868
+ effectiveTarget: requestedTarget,
4869
+ targetKind: 'mobile',
4870
+ targetOutRoot: resolve(projectRoot, config.build.outDir),
4871
+ executablePath: null,
4872
+ launchExecutablePath: null,
4873
+ bundlePath: mobileSource.bundle.outFile,
4874
+ assetsManifestPath: mobileSource.assets.manifestPath,
4875
+ buildManifestPath: mobileSource.buildManifestPath,
4876
+ assetMode: mobileSource.assets.mode,
4877
+ identity,
4878
+ icon: null,
4879
+ androidBuildManifestPath: androidStage?.manifestPath || null,
4880
+ iosBuildManifestPath: iosStage?.manifestPath || null,
4881
+ mobileBuilds: {
4882
+ android: androidRequested ? {
4883
+ hostArtifact: androidHostArtifact,
4884
+ stage: androidStage,
4885
+ export: androidBuild,
4886
+ } : null,
4887
+ ios: iosRequested ? {
4888
+ hostArtifact: iosHostArtifact,
4889
+ stage: iosStage,
4890
+ export: iosBuild,
4891
+ } : null,
4892
+ },
4893
+ };
4894
+ }
4307
4895
 
4308
4896
  const platformTarget = currentPlatformTarget();
4309
4897
  const effectiveTarget = platformTarget;
@@ -4311,8 +4899,7 @@ async function cmdBuild(args, options = {}) {
4311
4899
  const executableFileName = executableNameForPlatform(effectiveTarget, identity.executableBaseName);
4312
4900
 
4313
4901
  // CLI --asset-mode flag overrides config if provided.
4314
- const cliAssetMode = parseAssetMode(args);
4315
- const assetMode = args.some((a) => a === '--asset-mode') ? cliAssetMode : config.build.assetMode;
4902
+ const assetMode = selectedAssetMode;
4316
4903
 
4317
4904
  const result = runBuildStage('bundle', () => bundleProject({
4318
4905
  projectRoot,
@@ -4700,6 +5287,9 @@ async function cmdRun(args) {
4700
5287
  if (built.targetKind === 'retro') {
4701
5288
  throw new Error('aura run does not support Aura Retro targets. Use `aura build --target <gb|gbc|gba>` and the generated preview/toolchain outputs instead.');
4702
5289
  }
5290
+ if (built.targetKind === 'mobile') {
5291
+ throw new Error('aura run does not support mobile package targets. Use `aura build --target android|ios|mobile` and continue with the staged mobile package lane.');
5292
+ }
4703
5293
  if (built.effectiveTarget === 'web') {
4704
5294
  throw new Error('aura run does not support --target web. Serve build/web in a browser and use the web loader contract.');
4705
5295
  }
@@ -4901,6 +5491,74 @@ async function cmdTest(args) {
4901
5491
  console.log('');
4902
5492
  }
4903
5493
 
5494
+ async function cmdEvidence(args) {
5495
+ const projectRoot = process.cwd();
5496
+ const parsed = parseEvidenceArgs(args);
5497
+ const config = await loadConfig({ projectRoot, mode: 'build' });
5498
+ const replayPayload = parsed.replayPath
5499
+ ? readJsonInputFile(parsed.replayPath, 'replay payload', 'replay_input')
5500
+ : null;
5501
+ const result = await captureHeadlessEvidenceBundle({
5502
+ projectRoot,
5503
+ file: parsed.file || config.build.entry,
5504
+ frames: parsed.frames,
5505
+ width: parsed.width ?? config.window.width,
5506
+ height: parsed.height ?? config.window.height,
5507
+ outDir: parsed.outDir,
5508
+ replayPayload,
5509
+ });
5510
+
5511
+ const manifestText = serializeEvidenceManifest(result.manifest, parsed.compact);
5512
+ writeStateJsonOutput(parsed.outputPath, manifestText, 'evidence_output_write_failed');
5513
+ }
5514
+
5515
+ async function cmdReplayRun(args) {
5516
+ const projectRoot = process.cwd();
5517
+ const parsed = parseReplayRunArgs(args);
5518
+ const config = await loadConfig({ projectRoot, mode: 'build' });
5519
+ const payload = readJsonInputFile(parsed.replayPath, 'replay payload', 'replay_input');
5520
+ const result = await runHeadlessReplay({
5521
+ projectRoot,
5522
+ file: parsed.file || config.build.entry,
5523
+ width: parsed.width ?? config.window.width,
5524
+ height: parsed.height ?? config.window.height,
5525
+ payload,
5526
+ });
5527
+
5528
+ if (parsed.stateOutputPath !== null) {
5529
+ writeStateJsonOutput(
5530
+ parsed.stateOutputPath,
5531
+ serializeCanonicalStatePayload(result.finalState, parsed.compact),
5532
+ 'replay_state_output_write_failed',
5533
+ );
5534
+ }
5535
+ writeStateJsonOutput(
5536
+ parsed.outputPath,
5537
+ serializeReplayReport(result.report, parsed.compact),
5538
+ 'replay_output_write_failed',
5539
+ );
5540
+ }
5541
+
5542
+ async function cmdReplay(args) {
5543
+ const subcommand = args[0];
5544
+ if (!subcommand) {
5545
+ throw new StateExportError(
5546
+ 'replay_subcommand_required',
5547
+ 'Missing replay subcommand. Usage: aura replay run <replay.json> [options]',
5548
+ );
5549
+ }
5550
+
5551
+ if (subcommand === 'run') {
5552
+ await cmdReplayRun(args.slice(1));
5553
+ return;
5554
+ }
5555
+
5556
+ throw new StateExportError(
5557
+ 'replay_subcommand_unsupported',
5558
+ `Unknown replay subcommand "${subcommand}". Supported subcommands: run.`,
5559
+ );
5560
+ }
5561
+
4904
5562
  async function runStateExportPayload(parsed) {
4905
5563
  const projectRoot = process.cwd();
4906
5564
  const config = await loadConfig({ projectRoot, mode: 'build' });
@@ -6257,6 +6915,8 @@ const HANDLERS = {
6257
6915
  create: (args) => cmdCreate(args, { error }),
6258
6916
  from: (args) => cmdFrom(args, { error }),
6259
6917
  make: (args) => cmdMake(args, { error }),
6918
+ vendor: (args) => cmdVendor(args, { error }),
6919
+ packs: (args) => cmdPacks(args, { error }),
6260
6920
  explain: (args) => cmdExplain(args, { error }),
6261
6921
  check: (args) => cmdCheck(args, { error }),
6262
6922
  dev: cmdDev,
@@ -6268,6 +6928,8 @@ const HANDLERS = {
6268
6928
  'external-assets': cmdExternalAssets,
6269
6929
  clean: cmdClean,
6270
6930
  test: cmdTest,
6931
+ evidence: cmdEvidence,
6932
+ replay: cmdReplay,
6271
6933
  conformance: cmdConformance,
6272
6934
  state: cmdState,
6273
6935
  inspect: cmdInspect,
@@ -6291,7 +6953,7 @@ async function main() {
6291
6953
  process.exit(0);
6292
6954
  }
6293
6955
 
6294
- if (argv.includes('--version') || argv.includes('-v')) {
6956
+ if (argv[0] === '--version' || argv[0] === '-v') {
6295
6957
  console.log(VERSION);
6296
6958
  process.exit(0);
6297
6959
  }
@@ -6335,6 +6997,12 @@ async function main() {
6335
6997
  if (err instanceof StateArtifactError) {
6336
6998
  error(`${err.message} (reasonCode=${err.reasonCode})`, 6);
6337
6999
  }
7000
+ if (err instanceof EvidenceBundleError) {
7001
+ error(`${err.message} (reasonCode=${err.reasonCode})`, 6);
7002
+ }
7003
+ if (err instanceof ReplayError) {
7004
+ error(`${err.message} (reasonCode=${err.reasonCode})`, 6);
7005
+ }
6338
7006
  if (err instanceof HeadlessTestError) {
6339
7007
  if (Array.isArray(err.details?.failures) && err.details.failures.length > 0) {
6340
7008
  console.error('\n aura test failures:');