@tantawowa/hosanna-tools 2.22.0 → 2.23.0

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 (90) hide show
  1. package/README.md +95 -52
  2. package/dist/build-info.json +3 -3
  3. package/dist/cli.js +437 -157
  4. package/dist/cli.js.map +1 -1
  5. package/dist/device/device-cli.d.ts +2 -2
  6. package/dist/device/device-cli.js +7 -6
  7. package/dist/device/device-cli.js.map +1 -1
  8. package/dist/device/devices-file.js +9 -1
  9. package/dist/device/devices-file.js.map +1 -1
  10. package/dist/device/drivers/roku-driver.js +1 -1
  11. package/dist/device/drivers/roku-driver.js.map +1 -1
  12. package/dist/device/log-stream.d.ts +2 -0
  13. package/dist/device/log-stream.js +24 -14
  14. package/dist/device/log-stream.js.map +1 -1
  15. package/dist/device/types.d.ts +4 -0
  16. package/dist/index.d.ts +51 -2
  17. package/dist/index.js +44 -7
  18. package/dist/index.js.map +1 -1
  19. package/dist/lib/env-info.js +30 -24
  20. package/dist/lib/env-info.js.map +1 -1
  21. package/dist/lib/hosanna-config.d.ts +51 -0
  22. package/dist/lib/hosanna-config.js +323 -0
  23. package/dist/lib/hosanna-config.js.map +1 -0
  24. package/dist/run/android-emulator.d.ts +40 -0
  25. package/dist/run/android-emulator.js +590 -0
  26. package/dist/run/android-emulator.js.map +1 -0
  27. package/dist/run/apple-tv.d.ts +32 -0
  28. package/dist/run/apple-tv.js +367 -0
  29. package/dist/run/apple-tv.js.map +1 -0
  30. package/dist/run/ios-device.d.ts +17 -0
  31. package/dist/run/ios-device.js +229 -0
  32. package/dist/run/ios-device.js.map +1 -0
  33. package/dist/run/ios-simulator.d.ts +21 -0
  34. package/dist/run/ios-simulator.js +187 -0
  35. package/dist/run/ios-simulator.js.map +1 -0
  36. package/dist/run/process-utils.d.ts +2 -0
  37. package/dist/run/process-utils.js +31 -0
  38. package/dist/run/process-utils.js.map +1 -0
  39. package/dist/run/run-config.d.ts +20 -0
  40. package/dist/run/run-config.js +72 -0
  41. package/dist/run/run-config.js.map +1 -0
  42. package/dist/run/run-controller.d.ts +66 -0
  43. package/dist/run/run-controller.js +1017 -0
  44. package/dist/run/run-controller.js.map +1 -0
  45. package/dist/run/session-manager.d.ts +25 -0
  46. package/dist/run/session-manager.js +99 -0
  47. package/dist/run/session-manager.js.map +1 -0
  48. package/dist/run/targets.d.ts +60 -0
  49. package/dist/run/targets.js +210 -0
  50. package/dist/run/targets.js.map +1 -0
  51. package/dist/run/types.d.ts +60 -0
  52. package/dist/run/types.js +32 -0
  53. package/dist/run/types.js.map +1 -0
  54. package/dist/support-tools/framework-sync.d.ts +75 -0
  55. package/dist/support-tools/framework-sync.js +346 -0
  56. package/dist/support-tools/framework-sync.js.map +1 -0
  57. package/dist/support-tools/mcp-server/cli-test-recorder.js +1 -1
  58. package/dist/support-tools/mcp-server/cli-test-recorder.js.map +1 -1
  59. package/dist/support-tools/mcp-server/cli-test-runner.js +1 -1
  60. package/dist/support-tools/mcp-server/cli-test-runner.js.map +1 -1
  61. package/dist/support-tools/mcp-server/game-tools.js +1 -1
  62. package/dist/support-tools/mcp-server/game-tools.js.map +1 -1
  63. package/dist/support-tools/mcp-server/index.d.ts +1 -1
  64. package/dist/support-tools/mcp-server/instance-lock.js +1 -1
  65. package/dist/support-tools/mcp-server/tools.js +3 -3
  66. package/dist/support-tools/mcp-server/tools.js.map +1 -1
  67. package/dist/updater/compiler-installer.d.ts +17 -0
  68. package/dist/updater/compiler-installer.js +102 -0
  69. package/dist/updater/compiler-installer.js.map +1 -1
  70. package/dist/updater/framework-installer.d.ts +43 -0
  71. package/dist/updater/framework-installer.js +260 -0
  72. package/dist/updater/framework-installer.js.map +1 -0
  73. package/dist/updater/license-manager.d.ts +57 -0
  74. package/dist/updater/license-manager.js +265 -0
  75. package/dist/updater/license-manager.js.map +1 -0
  76. package/dist/updater/sdk-manager.d.ts +7 -1
  77. package/dist/updater/sdk-manager.js +15 -6
  78. package/dist/updater/sdk-manager.js.map +1 -1
  79. package/dist/updater/symlink-manager.js +25 -3
  80. package/dist/updater/symlink-manager.js.map +1 -1
  81. package/dist/updater/symlink-registry.d.ts +1 -0
  82. package/dist/updater/symlink-registry.js +6 -0
  83. package/dist/updater/symlink-registry.js.map +1 -1
  84. package/package.json +1 -1
  85. package/dist/lib/env-cloud-agent-setup-help.d.ts +0 -5
  86. package/dist/lib/env-cloud-agent-setup-help.js +0 -26
  87. package/dist/lib/env-cloud-agent-setup-help.js.map +0 -1
  88. package/dist/support-tools/cloud-agent-setup.d.ts +0 -41
  89. package/dist/support-tools/cloud-agent-setup.js +0 -132
  90. package/dist/support-tools/cloud-agent-setup.js.map +0 -1
package/dist/cli.js CHANGED
@@ -57,10 +57,9 @@ function parseGenerationMode(mode) {
57
57
  const compiler_installer_js_1 = require("./updater/compiler-installer.js");
58
58
  const generation_utils_js_2 = require("./generation/generation-utils.js");
59
59
  const env_info_js_1 = require("./lib/env-info.js");
60
- const cli_configure_hosanna_url_js_1 = require("./lib/cli.configure-hosanna-url.js");
61
60
  const hosanna_tools_utils_js_1 = require("./lib/hosanna-tools-utils.js");
62
- const env_cloud_agent_setup_help_js_1 = require("./lib/env-cloud-agent-setup-help.js");
63
61
  const cli_app_config_resolve_js_1 = require("./lib/cli.app-config-resolve.js");
62
+ const hosanna_config_js_1 = require("./lib/hosanna-config.js");
64
63
  // Read build date, version, and git hash embedded at build time
65
64
  let buildDate = 'unknown';
66
65
  let buildVersion = 'unknown';
@@ -87,18 +86,18 @@ catch (err) {
87
86
  // Let yargs handle all commands including env
88
87
  (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv))
89
88
  .version(`${buildVersion}\n git: ${buildGitHash}\n built: ${buildDate}`)
90
- .usage('$0 [command] [options]', `Hosanna Tools CLI\nVersion: ${buildVersion}\nBuild Date: ${buildDate}\n\nCommand Groups:\n run:* Development execution (dev, roku, debugger, mcp)\n stop:mcp Stop the Hosanna MCP server (PID lock; alias: run:stop)\n roku:* Roku deployment & packaging (run, package)\n generate:* Code generation (structs, clean)\n install:* Setup & installation (compiler, template)\n build-config Build/runtime configuration resolution\n ci:* Continuous integration (extract-pkg-key)\n secrets:* Portable .secrets (list, check, exec, init)\n test:* UI testing (replay recordings, visual regression)\n rasp:* RASP cert scripts (capture, export, validate)\n env:* Environment management (prepare-gitignore, cloud-agent-setup)`)
89
+ .usage('$0 [command] [options]', `Hosanna Tools CLI\nVersion: ${buildVersion}\nBuild Date: ${buildDate}\n\nCommand Groups:\n compiler:* Compiler lifecycle (install, status, list)\n config hosanna.json management (wizard, show, set)\n env Environment checks and repair (check, fix, prepare-gitignore)\n dev:* Local development execution\n debugger:* Command debugger control\n mcp:* Hosanna MCP server control\n run Cross-platform launch orchestration\n target:* Run target discovery and inventory\n roku:* Roku deployment & packaging (run, package, map-stack)\n device:* Device discovery and run workflows\n generate:* Code and asset generation\n sdk:* Hosanna SDK/framework installation\n build-config:* Build/runtime configuration resolution\n ci:* Continuous integration helpers\n framework:* Framework distribution helpers\n license:* Hosanna framework licensing\n secrets:* Portable .secrets files\n test:* UI testing\n rasp:* RASP cert scripts`)
91
90
  .option('verbose', {
92
91
  type: 'boolean',
93
92
  default: false,
94
93
  describe: 'Enable verbose logging for all operations',
95
94
  global: true
96
95
  })
97
- .command('game prep-level <input>', 'Normalize a Tiled JSON map into the Hs2d canonical runtime subset', yargs => yargs
96
+ .command('game:prep-level <input>', 'Normalize a Tiled JSON map into the Hs2d canonical runtime subset', yargs => yargs
98
97
  .positional('input', { type: 'string', demandOption: true, describe: 'Path to a Tiled .json/.tmj map export' })
99
98
  .option('out', { alias: 'o', type: 'string', describe: 'Output path (defaults to overwriting the input file)' })
100
99
  .option('pretty', { type: 'boolean', default: false, describe: 'Pretty-print the output JSON (device payloads default to compact)' })
101
- .example('$0 game prep-level levels/level-1.json -o asset-bundles/my-game/levels/level-1.json', 'Decode/normalize a Tiled export into an asset bundle')
100
+ .example('$0 game:prep-level levels/level-1.json -o asset-bundles/my-game/levels/level-1.json', 'Decode/normalize a Tiled export into an asset bundle')
102
101
  .epilog([
103
102
  'Converts what Tiled produces into what the Hs2d device parser reads:',
104
103
  ' - decodes base64 (+zlib/gzip) tile data into plain arrays',
@@ -128,7 +127,7 @@ catch (err) {
128
127
  process.exit(1);
129
128
  }
130
129
  })
131
- .command('build-config resolve', 'Resolve Hosanna build config overlays into the canonical runtime build-config.json', yargs => yargs
130
+ .command('build-config:resolve', 'Resolve Hosanna build config overlays into the canonical runtime build-config.json', yargs => yargs
132
131
  .option('env', { type: 'string', describe: 'Environment, e.g. dev, qa, or prod (or HS_ENV / HOSANNA_BUILD_ENV)' })
133
132
  .option('platform', { type: 'string', describe: 'Platform, e.g. web, roku, android, apple, or ios (or HS_PLATFORM / HOSANNA_BUILD_PLATFORM)' })
134
133
  .option('profile', { type: 'string', describe: 'Optional explicit local developer profile (or HS_BUILD_PROFILE)' })
@@ -137,9 +136,9 @@ catch (err) {
137
136
  .option('manifest-out', { type: 'string', describe: 'Write a sanitized source manifest to this file' })
138
137
  .option('explain', { type: 'boolean', default: false, describe: 'Print source summary without secret values' })
139
138
  .option('allow-profile-in-prod', { type: 'boolean', default: false, describe: 'Allow applying --profile with --env prod (or HS_ALLOW_PROFILE_IN_PROD=true)' })
140
- .example('$0 build-config resolve --env dev --platform web --format json', 'Resolve dev web config to stdout')
141
- .example('HS_ENV=dev HS_PLATFORM=roku $0 build-config resolve --out assets/meta/build-config.json', 'Resolve config from env-prefixed shell arguments')
142
- .example('$0 build-config resolve --env prod --platform roku --out assets/meta/build-config.json', 'Resolve prod Roku config to a runtime file')
139
+ .example('$0 build-config:resolve --env dev --platform web --format json', 'Resolve dev web config to stdout')
140
+ .example('HS_ENV=dev HS_PLATFORM=roku $0 build-config:resolve --out assets/meta/build-config.json', 'Resolve config from env-prefixed shell arguments')
141
+ .example('$0 build-config:resolve --env prod --platform roku --out assets/meta/build-config.json', 'Resolve prod Roku config to a runtime file')
143
142
  .epilog([
144
143
  'Environment fallbacks:',
145
144
  ' --env: HS_ENV or HOSANNA_BUILD_ENV',
@@ -211,7 +210,7 @@ catch (err) {
211
210
  process.exit(1);
212
211
  }
213
212
  })
214
- .command(['version', 'v'], 'Print the version number', yargs => yargs, () => {
213
+ .command('version', 'Print the version number', yargs => yargs, () => {
215
214
  console.log(buildVersion);
216
215
  console.log(` git: ${buildGitHash}`);
217
216
  console.log(` built: ${buildDate}`);
@@ -317,50 +316,6 @@ catch (err) {
317
316
  console.error(`❌ Failed to map Roku stack trace: ${msg}`);
318
317
  process.exit(1);
319
318
  }
320
- })
321
- .command('publish-roku', '[DEPRECATED] Use "roku:package" instead. Package and sign a Roku channel using roku-deploy. Flags fall back to env vars.', yargs => yargs
322
- .option('ip', { type: 'string', describe: 'Roku device IP (env: ROKU_IP)' })
323
- .option('installer-password', { type: 'string', describe: 'Dev Application Installer password (env: ROKU_DEVPASSWORD)' })
324
- .option('dev-id', { type: 'string', describe: 'Signing Developer ID (env: ROKU_DEV_ID)' })
325
- .option('signing-password', { type: 'string', describe: 'Signing password (env: ROKU_DEV_PASSWORD)' })
326
- .option('key-pkg', { type: 'string', describe: 'Path or filename of the signed key package (.pkg) (env: ROKU_PKG_NAME)' })
327
- .option('key-pkg-base64', { type: 'string', describe: 'Base64-encoded signed key package (.pkg) (env: ROKU_PKG_KEY_BASE64)' })
328
- .option('env', { type: 'string', choices: ['prod', 'dev'], default: 'prod', describe: 'Used in output naming; does not modify project config' })
329
- .option('rootDir', { type: 'string', describe: 'Roku project root (default: ./platforms/roku/src)' })
330
- .option('outDir', { type: 'string', describe: 'Output directory (default: ./build)' })
331
- .option('prebuild', { type: 'string', describe: 'Shell command to run before packaging (e.g. "npm run transpile:prod")' })
332
- .option('quiet', { type: 'boolean', default: false, describe: 'Reduce output; print only errors and success' })
333
- .example('$0 publish-roku --ip 192.168.1.10', '[DEPRECATED] Use "roku:package" instead'), async (args) => {
334
- console.warn('⚠️ DEPRECATED: The command "publish-roku" is deprecated. Use "roku:package" instead.');
335
- console.warn(' This command will be removed in a future version.');
336
- if (args.verbose) {
337
- process.env.HOSANNA_VERBOSE = '1';
338
- process.env.HOSANNA_UPDATER_VERBOSE = '1';
339
- }
340
- try {
341
- const mod = await Promise.resolve().then(() => __importStar(require('./support-tools/publish-roku.js')));
342
- const res = await mod.publishRoku({
343
- ip: args.ip,
344
- installerPassword: args['installer-password'],
345
- devId: args['dev-id'],
346
- signingPassword: args['signing-password'],
347
- keyPkg: args['key-pkg'],
348
- keyPkgBase64: args['key-pkg-base64'],
349
- env: args.env,
350
- rootDir: args.rootDir,
351
- outDir: args.outDir,
352
- prebuild: args.prebuild,
353
- quiet: Boolean(args.quiet),
354
- dotenv: true,
355
- });
356
- if (!args.quiet)
357
- console.info(`✅ Success: ${res.outPath}`);
358
- process.exit(0);
359
- }
360
- catch (err) {
361
- console.error('❌ Failed to publish Roku package:', err);
362
- process.exit(1);
363
- }
364
319
  })
365
320
  .command('ci:extract-pkg-key <input-pkg>', 'Extract signing key from an existing signed Roku package as base64 for CI use', yargs => yargs
366
321
  .positional('input-pkg', { type: 'string', describe: 'Path to existing signed Roku package (.pkg) file to extract key from' })
@@ -514,24 +469,46 @@ catch (err) {
514
469
  }
515
470
  })
516
471
  // remove old --env flag; env is now a subcommand
517
- .command('env', 'Print environment information and run checks. Use --fix to auto-correct SDK, compiler, and tools version mismatches.', yargs => yargs
518
- .option('fix', { type: 'boolean', default: false, describe: 'Auto-fix SDK, compiler, and tools version mismatches' }), async (args) => {
472
+ .command('config [action]', 'Show or update hosanna.json configuration from a curated catalog', yargs => yargs
473
+ .positional('action', {
474
+ type: 'string',
475
+ choices: ['show', 'set', 'wizard'],
476
+ describe: 'show prints current config, set writes values from flags, wizard runs the interactive setup',
477
+ })
478
+ .option('git-url', { type: 'string', describe: 'Set hosanna.json git-url' })
479
+ .option('sdk-version', { type: 'string', describe: 'Set hosanna.json sdk-version' })
480
+ .option('branch', { type: 'string', describe: 'Set hosanna.json branch; use an empty string to prefer sdk-version' })
481
+ .option('transpiler-version', { type: 'string', describe: 'Set hosanna.json transpiler-version' })
482
+ .option('config-catalog-url', { type: 'string', describe: 'Persist a remote catalog URL into hosanna.json' })
483
+ .option('catalog-url', { type: 'string', describe: 'Use a remote catalog URL for this run without writing it' })
484
+ .option('fix', { type: 'boolean', default: false, describe: 'Run hst env fix after updating config' }), async (args) => {
519
485
  if (args.verbose) {
520
486
  process.env.HOSANNA_VERBOSE = '1';
521
487
  process.env.HOSANNA_UPDATER_VERBOSE = '1';
522
488
  }
523
- const info = (0, env_info_js_1.collectEnvInfo)(process.cwd());
524
- const report = (0, env_info_js_1.formatEnvReportWithChecks)(info);
525
- console.info(report);
526
- if (args.fix) {
527
- // Handle comprehensive auto-fix
528
- const { fixAllEnvironmentIssues } = await Promise.resolve().then(() => __importStar(require('./lib/env-info.js')));
529
- const ok = (0, env_info_js_1.allEnvChecksPass)(info);
530
- if (ok) {
531
- console.info('\n✅ All environment checks passed - no fixes needed.');
532
- }
533
- else {
534
- console.info('');
489
+ const actionArg = typeof args.action === 'string' ? args.action : undefined;
490
+ const action = actionArg === 'show' || actionArg === 'set' ? actionArg : 'wizard';
491
+ const updates = {};
492
+ if (typeof args.gitUrl === 'string')
493
+ updates['git-url'] = args.gitUrl;
494
+ if (typeof args.sdkVersion === 'string')
495
+ updates['sdk-version'] = args.sdkVersion;
496
+ if (typeof args.branch === 'string')
497
+ updates.branch = args.branch;
498
+ if (typeof args.transpilerVersion === 'string')
499
+ updates['transpiler-version'] = args.transpilerVersion;
500
+ if (typeof args.configCatalogUrl === 'string')
501
+ updates['config-catalog-url'] = args.configCatalogUrl;
502
+ try {
503
+ const result = await (0, hosanna_config_js_1.runHosannaConfig)({
504
+ action,
505
+ cwd: process.cwd(),
506
+ catalogUrl: typeof args.catalogUrl === 'string' ? args.catalogUrl : undefined,
507
+ updates,
508
+ writeStdout: text => console.info(text),
509
+ });
510
+ if ((args.fix || result.runFix) && action !== 'show') {
511
+ const { fixAllEnvironmentIssues } = await Promise.resolve().then(() => __importStar(require('./lib/env-info.js')));
535
512
  const fixResult = await fixAllEnvironmentIssues(process.cwd());
536
513
  console.info(`\n${fixResult.message}`);
537
514
  if (fixResult.remainingIssues.length > 0) {
@@ -540,60 +517,77 @@ catch (err) {
540
517
  console.info(` • ${issue}`);
541
518
  }
542
519
  }
543
- // If fixes were successful with no remaining issues, exit with success
544
- // Don't rely on immediate re-check which may fail due to timing (npm install, PATH refresh, etc.)
545
- if (fixResult.success && fixResult.remainingIssues.length === 0) {
546
- // Fixes completed successfully - exit with 0 regardless of re-check
547
- if (!process.env.VITEST) {
548
- process.exit(0);
549
- }
550
- return; // Exit early since we handled the exit
551
- }
552
- // If there were remaining issues or fixes didn't complete successfully,
553
- // re-check to see current state and exit accordingly
554
- const updatedInfo = (0, env_info_js_1.collectEnvInfo)(process.cwd());
555
- const updatedOk = (0, env_info_js_1.allEnvChecksPass)(updatedInfo);
556
- if (!process.env.VITEST) {
557
- process.exit(updatedOk ? 0 : 1);
558
- }
559
- return; // Exit early since we handled the exit
520
+ if (!process.env.VITEST)
521
+ process.exit(fixResult.success ? 0 : 1);
522
+ return;
560
523
  }
524
+ if (!process.env.VITEST)
525
+ process.exit(0);
561
526
  }
562
- const ok = (0, env_info_js_1.allEnvChecksPass)(info);
563
- if (!process.env.VITEST) {
564
- process.exit(ok ? 0 : 1);
527
+ catch (err) {
528
+ console.error('❌ Failed to configure hosanna.json:', err instanceof Error ? err.message : err);
529
+ if (!process.env.VITEST)
530
+ process.exit(1);
565
531
  }
566
532
  })
567
- .command('env:cloud-agent-setup', 'Restore debug flags, patch remoteDebug for agents, optionally start debugger and npm dev (see --help for Cursor Cloud Agents)', yargs => yargs
568
- .option('output-dir', { type: 'string', describe: 'Directory for cloud-agent.json build config profile (default: build-config/profiles)' })
569
- .option('start-debugger', { type: 'boolean', default: false, describe: 'Start command debugger in-process (non-blocking)' })
570
- .option('start-app', { type: 'boolean', default: false, describe: 'Spawn npm run dev detached and wait for Vite port' })
571
- .option('vite-port', { type: 'number', default: 5173, describe: 'Port to wait on when --start-app is set' })
572
- .option('format', { type: 'string', choices: ['text', 'json'], default: 'text', describe: 'Summary format' })
573
- .example('$0 env:cloud-agent-setup --start-debugger --start-app', 'Full bootstrap for a cloud agent')
574
- .epilog(env_cloud_agent_setup_help_js_1.ENV_CLOUD_AGENT_SETUP_EPILOG), async (args) => {
533
+ .command('env <action>', 'Check or repair the local Hosanna environment', yargs => yargs
534
+ .positional('action', {
535
+ type: 'string',
536
+ choices: ['check', 'fix', 'prepare-gitignore'],
537
+ describe: 'check prints environment status, fix repairs version mismatches, prepare-gitignore updates .gitignore',
538
+ }), async (args) => {
575
539
  if (args.verbose) {
576
540
  process.env.HOSANNA_VERBOSE = '1';
577
541
  process.env.HOSANNA_UPDATER_VERBOSE = '1';
578
542
  }
579
- try {
580
- const mod = await Promise.resolve().then(() => __importStar(require('./support-tools/cloud-agent-setup.js')));
581
- await mod.runCloudAgentSetup({
582
- cwd: process.cwd(),
583
- outputDir: args['output-dir'],
584
- startDebugger: Boolean(args['start-debugger']),
585
- startApp: Boolean(args['start-app']),
586
- vitePort: args['vite-port'],
587
- format: args.format,
588
- });
589
- process.exit(0);
543
+ if (args.action === 'prepare-gitignore') {
544
+ console.info('Preparing .gitignore...');
545
+ (0, sdk_manager_js_1.prepareGitIgnore)();
546
+ console.info('.gitignore preparation completed.');
547
+ return;
590
548
  }
591
- catch (err) {
592
- console.error('❌ env:cloud-agent-setup failed:', err);
593
- process.exit(1);
549
+ const info = (0, env_info_js_1.collectEnvInfo)(process.cwd());
550
+ const report = (0, env_info_js_1.formatEnvReportWithChecks)(info);
551
+ console.info(report);
552
+ const ok = (0, env_info_js_1.allEnvChecksPass)(info);
553
+ if (args.action === 'check') {
554
+ if (!process.env.VITEST) {
555
+ process.exit(ok ? 0 : 1);
556
+ }
557
+ return;
558
+ }
559
+ if (ok) {
560
+ console.info('\n✅ All environment checks passed - no fixes needed.');
561
+ }
562
+ else {
563
+ const { fixAllEnvironmentIssues } = await Promise.resolve().then(() => __importStar(require('./lib/env-info.js')));
564
+ console.info('');
565
+ const fixResult = await fixAllEnvironmentIssues(process.cwd());
566
+ console.info(`\n${fixResult.message}`);
567
+ if (fixResult.remainingIssues.length > 0) {
568
+ console.info('\n❌ Remaining issues:');
569
+ for (const issue of fixResult.remainingIssues) {
570
+ console.info(` • ${issue}`);
571
+ }
572
+ }
573
+ if (fixResult.success && fixResult.remainingIssues.length === 0) {
574
+ if (!process.env.VITEST) {
575
+ process.exit(0);
576
+ }
577
+ return;
578
+ }
579
+ const updatedInfo = (0, env_info_js_1.collectEnvInfo)(process.cwd());
580
+ const updatedOk = (0, env_info_js_1.allEnvChecksPass)(updatedInfo);
581
+ if (!process.env.VITEST) {
582
+ process.exit(updatedOk ? 0 : 1);
583
+ }
584
+ return;
585
+ }
586
+ if (!process.env.VITEST) {
587
+ process.exit(0);
594
588
  }
595
589
  })
596
- .command('run:debugger', 'Start the command debugger WebSocket proxy', yargs => yargs
590
+ .command('debugger:start', 'Start the command debugger WebSocket proxy', yargs => yargs
597
591
  .option('webAppPort', { type: 'number', description: 'Port for web app client', default: 59151 })
598
592
  .option('deviceAppPort', { type: 'number', description: 'Port for device app client', default: 59152 })
599
593
  .option('extensionPort', { type: 'number', description: 'Port for extension client', default: 59153 })
@@ -611,14 +605,14 @@ catch (err) {
611
605
  const { checkExistingDebugger } = await Promise.resolve().then(() => __importStar(require('./lib/debugger-probe.js')));
612
606
  const { checkPortInUse } = await Promise.resolve().then(() => __importStar(require('./lib/port-utils.js')));
613
607
  if (await checkExistingDebugger(managementPort)) {
614
- console.log('[run:debugger] Command debugger is already running at http://localhost:' + managementPort);
615
- console.log('[run:debugger] Your apps can connect via the management API (/assign-port). No need to start another debugger.');
608
+ console.log('[debugger:start] Command debugger is already running at http://localhost:' + managementPort);
609
+ console.log('[debugger:start] Your apps can connect via the management API (/assign-port). No need to start another debugger.');
616
610
  process.exit(0);
617
611
  }
618
612
  // TCP fallback: if management port is in use (e.g. debugger still starting, HTTP not ready), assume debugger is running
619
613
  if (await checkPortInUse(managementPort)) {
620
- console.log('[run:debugger] Debugger port ' + managementPort + ' is in use — a debugger is already running.');
621
- console.log('[run:debugger] Not starting a debugger server. Your apps can connect via the management API (/assign-port).');
614
+ console.log('[debugger:start] Debugger port ' + managementPort + ' is in use — a debugger is already running.');
615
+ console.log('[debugger:start] Not starting a debugger server. Your apps can connect via the management API (/assign-port).');
622
616
  process.exit(0);
623
617
  }
624
618
  // Check if any required ports are already in use (e.g. another process on our ports)
@@ -635,11 +629,11 @@ catch (err) {
635
629
  }
636
630
  }
637
631
  if (portsInUse.length > 0) {
638
- console.error('[run:debugger] ❌ Error: One or more ports are already in use:');
632
+ console.error('[debugger:start] ❌ Error: One or more ports are already in use:');
639
633
  for (const { port, name } of portsInUse) {
640
- console.error(`[run:debugger] • Port ${port} (${name})`);
634
+ console.error(`[debugger:start] • Port ${port} (${name})`);
641
635
  }
642
- console.error('[run:debugger] Please free these ports or use different ports (e.g. --deviceAppPort 59156) and try again.');
636
+ console.error('[debugger:start] Please free these ports or use different ports (e.g. --deviceAppPort 59156) and try again.');
643
637
  process.exit(1);
644
638
  }
645
639
  const { startCommandDebugger } = await Promise.resolve().then(() => __importStar(require('./support-tools/command-debugger.js')));
@@ -651,8 +645,8 @@ catch (err) {
651
645
  defaultAppName: typeof args.appName === 'string' && args.appName.trim() !== '' ? args.appName.trim() : undefined,
652
646
  });
653
647
  })
654
- .command('install:compiler [version]', 'Install or update the Hosanna compiler (hsc) locally. Downloads to /tmp cache and installs to ./bin/hsc. If [version] is omitted, uses hosanna.json transpiler-version.', yargs => yargs
655
- .positional('version', { type: 'string', describe: 'hsc version (x.y.z). Overrides hosanna.json when provided.' })
648
+ .command('compiler:install [compilerVersion]', 'Install or update the Hosanna compiler (hsc) locally. Downloads to /tmp cache and installs to ./bin/hsc. If [compilerVersion] is omitted, uses hosanna.json transpiler-version.', yargs => yargs
649
+ .positional('compilerVersion', { type: 'string', describe: 'hsc version (x.y.z). Overrides hosanna.json when provided.' })
656
650
  .option('force', { type: 'boolean', default: false, describe: 'Force install even if version matches or compiler is npm-installed' })
657
651
  .option('sudo', { type: 'boolean', default: false, describe: 'If needed, use sudo for file operations (legacy option, rarely needed)' }), async (args) => {
658
652
  if (args.verbose) {
@@ -660,9 +654,9 @@ catch (err) {
660
654
  process.env.HOSANNA_UPDATER_VERBOSE = '1';
661
655
  }
662
656
  let version;
663
- const provided = args.version;
657
+ const provided = args.compilerVersion;
664
658
  if (typeof provided === 'string') {
665
- const trimmed = provided.trim();
659
+ const trimmed = provided.trim().replace(/^v(?=\d+\.\d+\.\d+)/, '');
666
660
  version = trimmed.length > 0 ? trimmed : undefined;
667
661
  }
668
662
  try {
@@ -699,17 +693,51 @@ catch (err) {
699
693
  process.exit(1);
700
694
  }
701
695
  })
702
- .command('generate', 'Generate structs and command handler maps', yargs => yargs
696
+ .command('compiler:status', 'Print local Hosanna compiler status for this project', yargs => yargs.option('format', { type: 'string', choices: ['text', 'json'], default: 'text', describe: 'Output format' }), args => {
697
+ var _a;
698
+ const status = (0, compiler_installer_js_1.getCompilerStatus)(process.cwd());
699
+ if (args.format === 'json') {
700
+ console.log(JSON.stringify(status, null, 2));
701
+ }
702
+ else {
703
+ console.info('Hosanna compiler status:');
704
+ console.info(` configured: ${(_a = status.configuredVersion) !== null && _a !== void 0 ? _a : '(not set)'}`);
705
+ console.info(` installed: ${status.installed ? status.installType : 'missing'}`);
706
+ if (status.installedVersion)
707
+ console.info(` version: ${status.installedVersion}`);
708
+ console.info(` path: ${status.installedPath}`);
709
+ console.info(` matches: ${status.matchesConfigured === undefined ? '(not applicable)' : status.matchesConfigured ? 'yes' : 'no'}`);
710
+ console.info(` cache: ${status.cacheDir}`);
711
+ console.info(` cached: ${status.cachedVersions.length ? status.cachedVersions.join(', ') : '(none)'}`);
712
+ }
713
+ process.exit(status.configuredVersion && status.matchesConfigured === false ? 1 : 0);
714
+ })
715
+ .command('compiler:list', 'List compiler versions known locally from hosanna.json, bin/hsc, and the local hsc cache', yargs => yargs.option('format', { type: 'string', choices: ['text', 'json'], default: 'text', describe: 'Output format' }), args => {
716
+ const entries = (0, compiler_installer_js_1.listCompilerVersions)(process.cwd());
717
+ if (args.format === 'json') {
718
+ console.log(JSON.stringify(entries, null, 2));
719
+ }
720
+ else if (entries.length === 0) {
721
+ console.info('No local compiler versions found.');
722
+ }
723
+ else {
724
+ for (const entry of entries) {
725
+ console.info(`${entry.version} (${entry.sources.join(', ')})`);
726
+ }
727
+ }
728
+ process.exit(0);
729
+ })
730
+ .command('generate:all', 'Generate structs and command handler maps', yargs => yargs
703
731
  .option('watch', { type: 'boolean', description: 'Watch for file changes' })
704
732
  .option('rootFolder', { type: 'string', description: 'Root folder for the operation (defaults to ./src)' })
705
733
  .option('generatedFolder', { type: 'string', description: 'Folder to output generated files (defaults to src-generated)' })
706
734
  .option('quiet', { type: 'boolean', description: 'Reduce output; print only errors and success', default: false })
707
735
  .option('mode', { type: 'string', choices: ['runtime', 'types', 'both'], default: 'runtime', description: 'Generate runtime .ts, types .d.ts+.map, or both' })
708
736
  .option('fileFilter', { type: 'string', description: 'Regex pattern to filter filenames (applied to relative paths from rootFolder)' })
709
- .example('$0 generate --rootFolder ./src', 'Generate structs and command handler maps for the specified root folder')
710
- .example('$0 generate --watch', 'Generate structs and command handler maps and watch for file changes')
711
- .example('$0 generate --generatedFolder ./gen', 'Generate structs and command handler maps in ./gen folder')
712
- .example('$0 generate --fileFilter "Button"', 'Generate structs only for files containing "Button" in their path'), args => {
737
+ .example('$0 generate:all --rootFolder ./src', 'Generate structs and command handler maps for the specified root folder')
738
+ .example('$0 generate:all --watch', 'Generate structs and command handler maps and watch for file changes')
739
+ .example('$0 generate:all --generatedFolder ./gen', 'Generate structs and command handler maps in ./gen folder')
740
+ .example('$0 generate:all --fileFilter "Button"', 'Generate structs only for files containing "Button" in their path'), args => {
713
741
  const rootFolder = args.rootFolder ? path.resolve(args.rootFolder) : path.resolve(process.cwd(), 'src');
714
742
  const generatedFolder = args.generatedFolder ? path.resolve(args.generatedFolder) : path.resolve(process.cwd(), 'src-generated');
715
743
  if (args.quiet) {
@@ -906,40 +934,154 @@ catch (err) {
906
934
  process.exit(1);
907
935
  }
908
936
  })
909
- .command('env:prepare-gitignore', 'Ensure .gitignore contains required entries', yargs => yargs, (args) => {
937
+ .command('sdk:install', 'Install the SDK. Uses git (hosanna.json git-url) or license-gated keygen artifacts (hosanna.json "distribution": "keygen")', yargs => yargs
938
+ .option('force', { type: 'boolean', default: false, describe: 'Reinstall even if the requested version is already installed (keygen mode)' }), async (args) => {
910
939
  if (args.verbose) {
911
940
  process.env.HOSANNA_VERBOSE = '1';
912
941
  process.env.HOSANNA_UPDATER_VERBOSE = '1';
913
942
  }
914
- console.info('Preparing .gitignore...');
915
- (0, sdk_manager_js_1.prepareGitIgnore)();
916
- console.info('.gitignore preparation completed.');
943
+ console.info('Running sdk:install command...');
944
+ try {
945
+ // Keygen-distributed projects bypass the git flow entirely.
946
+ const hosannaJsonPath = path.resolve(process.cwd(), 'hosanna.json');
947
+ let distribution;
948
+ if (fs.existsSync(hosannaJsonPath)) {
949
+ try {
950
+ distribution = JSON.parse(fs.readFileSync(hosannaJsonPath, 'utf-8')).distribution;
951
+ }
952
+ catch ( /* fall through to git flow which reports config errors */_a) { /* fall through to git flow which reports config errors */ }
953
+ }
954
+ if (distribution === 'keygen') {
955
+ const mod = await Promise.resolve().then(() => __importStar(require('./updater/framework-installer.js')));
956
+ await mod.installFrameworkFromKeygen({ force: Boolean(args.force) });
957
+ }
958
+ else {
959
+ await (0, sdk_manager_js_1.installSdk)(); // Ensure installSdk is awaited
960
+ }
961
+ console.info('sdk:install command completed.');
962
+ process.exit(0); // Exit after successful completion
963
+ }
964
+ catch (err) {
965
+ console.error('An error occurred during the install process:', err);
966
+ process.exit(1); // Exit with error code on failure
967
+ }
917
968
  })
918
- .command('install', 'Install the SDK by creating hosanna.json and setting up the environment', yargs => yargs, async (args) => {
969
+ .command('framework:update', 'Update the keygen-distributed hosanna-ui folder to the version in hosanna.json (no-op when already current)', yargs => yargs
970
+ .option('force', { type: 'boolean', default: false, describe: 'Reinstall even if the requested version is already installed' }), async (args) => {
919
971
  if (args.verbose) {
920
972
  process.env.HOSANNA_VERBOSE = '1';
921
973
  process.env.HOSANNA_UPDATER_VERBOSE = '1';
922
974
  }
923
- console.info('Running install-sdk command...');
924
975
  try {
925
- await (0, sdk_manager_js_1.installSdk)(); // Ensure installSdk is awaited
926
- console.info('install-sdk command completed.');
927
- process.exit(0); // Exit after successful completion
976
+ const mod = await Promise.resolve().then(() => __importStar(require('./updater/framework-installer.js')));
977
+ await mod.installFrameworkFromKeygen({ force: Boolean(args.force) });
978
+ process.exit(0);
928
979
  }
929
980
  catch (err) {
930
- console.error('An error occurred during the install process:', err);
931
- process.exit(1); // Exit with error code on failure
981
+ console.error(' framework:update failed:', err instanceof Error ? err.message : err);
982
+ process.exit(1);
983
+ }
984
+ })
985
+ .command('framework:sync', 'Maintainer: fast-forward tier-1 client forks (branches + tags) from upstream, driven by ~/.hosanna/sync-clients.json', yargs => yargs
986
+ .option('client', { type: 'string', describe: 'Sync a single named client from the registry (default: all clients)' })
987
+ .option('registry', { type: 'string', describe: 'Path to the sync registry (default: ~/.hosanna/sync-clients.json)' })
988
+ .option('dry-run', { type: 'boolean', default: false, describe: 'Show what would be pushed without pushing' })
989
+ .option('init', { type: 'boolean', default: false, describe: 'Create a template registry file and exit' })
990
+ .example('$0 framework:sync', 'Sync all registered client forks')
991
+ .example('$0 framework:sync --client ewtn --dry-run', 'Preview a sync for one client'), async (args) => {
992
+ try {
993
+ const mod = await Promise.resolve().then(() => __importStar(require('./support-tools/framework-sync.js')));
994
+ if (args.init) {
995
+ const filePath = mod.initRegistry(args.registry);
996
+ console.info(`✅ Created sync registry template: ${filePath}`);
997
+ console.info('Edit it to add your clients, then run: hst framework:sync');
998
+ process.exit(0);
999
+ }
1000
+ const results = await mod.syncFrameworkForks({
1001
+ registryPath: args.registry,
1002
+ client: args.client,
1003
+ dryRun: Boolean(args['dry-run']),
1004
+ });
1005
+ const ok = mod.printSyncSummary(results, Boolean(args['dry-run']));
1006
+ process.exit(ok ? 0 : 1);
1007
+ }
1008
+ catch (err) {
1009
+ console.error('❌ framework:sync failed:', err instanceof Error ? err.message : err);
1010
+ process.exit(1);
1011
+ }
1012
+ })
1013
+ .command('framework:doctor', 'Maintainer: validate tier-1 client forks (reachability, hosanna.json git-url/schema, sdk-version tags)', yargs => yargs
1014
+ .option('client', { type: 'string', describe: 'Check a single named client from the registry (default: all clients)' })
1015
+ .option('registry', { type: 'string', describe: 'Path to the sync registry (default: ~/.hosanna/sync-clients.json)' })
1016
+ .example('$0 framework:doctor', 'Validate all registered client forks'), async (args) => {
1017
+ try {
1018
+ const mod = await Promise.resolve().then(() => __importStar(require('./support-tools/framework-sync.js')));
1019
+ const issues = await mod.doctorFrameworkForks({
1020
+ registryPath: args.registry,
1021
+ client: args.client,
1022
+ });
1023
+ const ok = mod.printDoctorSummary(issues);
1024
+ process.exit(ok ? 0 : 1);
1025
+ }
1026
+ catch (err) {
1027
+ console.error('❌ framework:doctor failed:', err instanceof Error ? err.message : err);
1028
+ process.exit(1);
932
1029
  }
933
1030
  })
934
- .command('install:template', 'Create a new template app with the Hosanna SDK', yargs => yargs, async (args) => {
1031
+ .command('license:activate <key>', 'Store your Hosanna framework license key (~/.hosanna/license.key) after validating it', yargs => yargs
1032
+ .positional('key', { type: 'string', demandOption: true, describe: 'License key issued for your organization' }), async (args) => {
1033
+ try {
1034
+ const mod = await Promise.resolve().then(() => __importStar(require('./updater/license-manager.js')));
1035
+ const key = String(args.key).trim();
1036
+ const validation = await mod.validateLicense(key);
1037
+ if (!validation.valid) {
1038
+ console.error(`❌ License is not valid: ${validation.code} ${validation.detail}`.trim());
1039
+ process.exit(1);
1040
+ }
1041
+ const filePath = mod.storeLicenseKey(key);
1042
+ console.info(`✅ License validated and stored at ${filePath}`);
1043
+ if (validation.expiry)
1044
+ console.info(` Expires: ${validation.expiry}`);
1045
+ process.exit(0);
1046
+ }
1047
+ catch (err) {
1048
+ console.error('❌ license:activate failed:', err instanceof Error ? err.message : err);
1049
+ process.exit(1);
1050
+ }
1051
+ })
1052
+ .command('license:status', 'Validate the stored license and print its entitlements (flavour, platforms, tier)', yargs => yargs, async () => {
1053
+ try {
1054
+ const mod = await Promise.resolve().then(() => __importStar(require('./updater/license-manager.js')));
1055
+ const key = mod.requireLicenseKey();
1056
+ const info = await mod.getLicenseInfo(key);
1057
+ if (!info.validation.valid) {
1058
+ console.error(`❌ License is not valid: ${info.validation.code} ${info.validation.detail}`.trim());
1059
+ process.exit(1);
1060
+ }
1061
+ console.info('✅ License is valid');
1062
+ if (info.validation.expiry)
1063
+ console.info(` Expires: ${info.validation.expiry}`);
1064
+ const flavours = mod.entitledFlavours(info.entitlements);
1065
+ const platforms = mod.entitledPlatforms(info.entitlements);
1066
+ console.info(` Flavour: ${flavours.length ? flavours[flavours.length - 1] : '(none)'}`);
1067
+ console.info(` Platforms: ${platforms.length ? platforms.join(', ') : '(none)'}`);
1068
+ console.info(` Entitlements: ${info.entitlements.join(', ') || '(none)'}`);
1069
+ process.exit(0);
1070
+ }
1071
+ catch (err) {
1072
+ console.error('❌ license:status failed:', err instanceof Error ? err.message : err);
1073
+ process.exit(1);
1074
+ }
1075
+ })
1076
+ .command('template:create', 'Create a new template app with the Hosanna SDK', yargs => yargs, async (args) => {
935
1077
  if (args.verbose) {
936
1078
  process.env.HOSANNA_VERBOSE = '1';
937
1079
  process.env.HOSANNA_UPDATER_VERBOSE = '1';
938
1080
  }
939
- console.info('Running install:template command...');
1081
+ console.info('Running template:create command...');
940
1082
  try {
941
1083
  await (0, sdk_manager_js_1.createTemplateApp)();
942
- console.info('install:template command completed.');
1084
+ console.info('template:create command completed.');
943
1085
  process.exit(0);
944
1086
  }
945
1087
  catch (err) {
@@ -976,7 +1118,7 @@ catch (err) {
976
1118
  process.exit(1);
977
1119
  }
978
1120
  })
979
- .command('device:list', 'List devices discovered by hst, merged with .hs-devices.json preferences', yargs => yargs
1121
+ .command('device:list', 'List devices discovered by hst, merged with .hosanna-tools/devices.json preferences', yargs => yargs
980
1122
  .option('type', {
981
1123
  type: 'string',
982
1124
  choices: ['roku', 'ios', 'apple-tv', 'android', 'android-tv'],
@@ -1005,8 +1147,154 @@ catch (err) {
1005
1147
  console.error(err instanceof Error ? err.message : String(err));
1006
1148
  process.exit(1);
1007
1149
  }
1150
+ })
1151
+ .command('target:list', 'List run targets discovered by hst, including web previews, simulators, and physical devices', yargs => yargs
1152
+ .option('platform', {
1153
+ type: 'string',
1154
+ choices: ['web', 'roku', 'ios', 'apple-tv', 'android', 'android-tv'],
1155
+ describe: 'Filter by launch platform',
1156
+ })
1157
+ .option('target', {
1158
+ type: 'string',
1159
+ choices: ['web', 'device', 'simulator', 'emulator', 'simulated', 'emulated'],
1160
+ describe: 'Filter by target kind',
1161
+ })
1162
+ .option('form-factor', {
1163
+ type: 'string',
1164
+ choices: ['desktop', 'mobile', 'tablet', 'tv'],
1165
+ describe: 'Filter by form factor for broad agent inventory queries',
1166
+ })
1167
+ .option('json', {
1168
+ type: 'boolean',
1169
+ default: false,
1170
+ describe: 'Print machine-readable JSON',
1171
+ })
1172
+ .example('$0 target:list --platform roku', 'List Roku web/device targets')
1173
+ .example('$0 target:list --platform ios --target simulator --json', 'List iOS simulator targets as JSON')
1174
+ .example('$0 target:list --platform ios --target device --json', 'List physical iOS device targets as JSON')
1175
+ .example('$0 target:list --platform android --target emulator --json', 'List Android phone emulator targets as JSON')
1176
+ .example('$0 target:list --platform android --target device --json', 'List physical Android device targets as JSON')
1177
+ .example('$0 target:list --platform android-tv --target emulator --json', 'List Android TV emulator targets as JSON')
1178
+ .example('$0 target:list --platform apple-tv --target simulator --json', 'List Apple TV simulator targets as JSON')
1179
+ .example('$0 target:list --form-factor tv --json', 'List TV-class targets as JSON'), async (args) => {
1180
+ try {
1181
+ const mod = await Promise.resolve().then(() => __importStar(require('./run/targets.js')));
1182
+ const filters = mod.parseRunTargetFilters({
1183
+ platform: args.platform,
1184
+ target: args.target,
1185
+ formFactor: args['form-factor'],
1186
+ });
1187
+ const targets = await mod.collectRunTargets(Object.assign(Object.assign({ cwd: process.cwd() }, filters), { env: process.env }));
1188
+ if (args.json) {
1189
+ console.info(JSON.stringify(mod.formatRunTargetsJson(targets), null, 2));
1190
+ }
1191
+ else {
1192
+ console.info(mod.formatRunTargetsText(targets));
1193
+ }
1194
+ process.exit(0);
1195
+ }
1196
+ catch (err) {
1197
+ console.error('❌ target:list failed');
1198
+ console.error(err instanceof Error ? err.message : String(err));
1199
+ process.exit(1);
1200
+ }
1201
+ })
1202
+ .command('run', 'Launch a Hosanna app on a platform target (web, Roku, iOS, Apple TV, Android, and Android TV targets are supported)', yargs => yargs
1203
+ .parserConfiguration({ 'boolean-negation': false })
1204
+ .option('platform', {
1205
+ type: 'string',
1206
+ choices: ['web', 'roku', 'ios', 'apple-tv', 'android', 'android-tv'],
1207
+ demandOption: true,
1208
+ describe: 'Launch platform. Required to keep agent runs deterministic.',
1209
+ })
1210
+ .option('target', {
1211
+ type: 'string',
1212
+ choices: ['web', 'device', 'simulator', 'emulator', 'simulated', 'emulated'],
1213
+ describe: 'Target kind. Defaults to web for web/roku.',
1214
+ })
1215
+ .option('device', {
1216
+ type: 'string',
1217
+ describe: 'Device IP, serial/UDID, or case-insensitive name substring for device/simulator runs',
1218
+ })
1219
+ .option('appName', {
1220
+ type: 'string',
1221
+ describe: 'App name used for debugger/session identity',
1222
+ })
1223
+ .option('vitePort', { type: 'number', description: 'Port for Vite dev server', default: 5173 })
1224
+ .option('webAppPort', { type: 'number', description: 'Port for web app debugger client', default: 59151 })
1225
+ .option('deviceAppPort', { type: 'number', description: 'Port for device app debugger client', default: 59152 })
1226
+ .option('extensionPort', { type: 'number', description: 'Debugger extension WebSocket port', default: 59153 })
1227
+ .option('managementPort', { type: 'number', description: 'Debugger management HTTP port', default: 59150 })
1228
+ .option('reuse', { type: 'boolean', default: false, describe: 'Explicitly allow reuse of a matching hst session' })
1229
+ .option('replace', { type: 'boolean', default: false, describe: 'Replace conflicting web/device sessions or occupied device log resources' })
1230
+ .option('non-interactive', { type: 'boolean', default: false, describe: 'Fail instead of prompting when a decision is needed' })
1231
+ .option('json', { type: 'boolean', default: false, describe: 'Print run result as JSON when the command returns' })
1232
+ .option('no-build', { type: 'boolean', default: false, describe: 'Skip the platform build step for device runs' })
1233
+ .option('no-logs', { type: 'boolean', default: false, describe: 'Do not stream device logs after launch where supported' })
1234
+ .option('build-cmd', { type: 'string', describe: 'Override the platform build command for device runs' })
1235
+ .option('app-package', { type: 'string', describe: 'Roku app .zip or folder to deploy for device runs' })
1236
+ .option('source-root', { type: 'string', describe: 'Roku source root to deploy for device runs' })
1237
+ .option('source-map-root', { type: 'string', describe: 'Roku source map root for log resolution' })
1238
+ .option('files', { type: 'array', describe: 'Roku file globs when deploying a folder' })
1239
+ .option('installer-password', { type: 'string', describe: 'Roku installer password (env: ROKU_DEVPASSWORD)' })
1240
+ .option('logs', { type: 'boolean', default: false, describe: 'Enable verbose deploy logging where supported' })
1241
+ .option('applySourceMapToPaths', { type: 'boolean', default: true, describe: 'Resolve Roku stack trace paths with source maps during log streaming' })
1242
+ .example('$0 run --platform web', 'Start/reuse web dev services for this app')
1243
+ .example('$0 run --platform roku --target web', 'Start/reuse the Roku TV web preview')
1244
+ .example('$0 run --platform ios --target simulator --device "iPhone 17 Pro"', 'Build, install, and launch on an iOS simulator')
1245
+ .example('$0 run --platform ios --target device --device "George iPhone" --no-logs', 'Build, install, and launch on a physical iPhone')
1246
+ .example('$0 run --platform android --target emulator --device "Pixel_8" --no-logs', 'Build, install, and launch on an Android phone emulator')
1247
+ .example('$0 run --platform android --target device --device "RF8M62694QT" --no-logs', 'Build, install, and launch on a physical Android device')
1248
+ .example('$0 run --platform android-tv --target emulator --device "Television_1080p" --no-logs', 'Build, install, and launch on an Android TV emulator')
1249
+ .example('$0 run --platform apple-tv --target simulator --device "Apple TV 4K"', 'Build, install, and launch on an Apple TV simulator')
1250
+ .example('$0 run --platform roku --target device --device "living room"', 'Build, deploy, launch, and stream logs on a Roku device'), async (args) => {
1251
+ var _a, _b, _c;
1252
+ try {
1253
+ const mod = await Promise.resolve().then(() => __importStar(require('./run/run-controller.js')));
1254
+ const result = await mod.runHosanna({
1255
+ cwd: process.cwd(),
1256
+ platform: args.platform,
1257
+ target: args.target,
1258
+ device: args.device,
1259
+ appName: args.appName,
1260
+ vitePort: args.vitePort,
1261
+ webAppPort: args.webAppPort,
1262
+ deviceAppPort: args.deviceAppPort,
1263
+ extensionPort: args.extensionPort,
1264
+ managementPort: args.managementPort,
1265
+ reuse: Boolean(args.reuse),
1266
+ replace: Boolean(args.replace),
1267
+ nonInteractive: Boolean(args['non-interactive']),
1268
+ noBuild: Boolean(args['no-build']),
1269
+ noLogs: Boolean(args['no-logs']),
1270
+ buildCmd: args['build-cmd'],
1271
+ appPackage: args['app-package'],
1272
+ sourceRoot: args['source-root'],
1273
+ sourceMapRoot: args['source-map-root'],
1274
+ files: args.files,
1275
+ installerPassword: args['installer-password'],
1276
+ logs: Boolean(args.logs),
1277
+ applySourceMapToPaths: Boolean(args.applySourceMapToPaths),
1278
+ env: process.env,
1279
+ });
1280
+ if (args.json) {
1281
+ console.info(JSON.stringify(result, null, 2));
1282
+ }
1283
+ else if (result.status === 'reused') {
1284
+ console.info(`hst run: reusing ${(_a = result.appName) !== null && _a !== void 0 ? _a : 'app'} at ${(_b = result.url) !== null && _b !== void 0 ? _b : '(unknown URL)'}`);
1285
+ }
1286
+ else if (result.url) {
1287
+ console.info(`hst run: ${(_c = result.appName) !== null && _c !== void 0 ? _c : 'app'} available at ${result.url}`);
1288
+ }
1289
+ }
1290
+ catch (err) {
1291
+ console.error('❌ run failed');
1292
+ console.error(err instanceof Error ? err.message : String(err));
1293
+ process.exit(1);
1294
+ }
1008
1295
  })
1009
1296
  .command('device:run', 'Build, deploy, launch, and stream logs for a device', yargs => yargs
1297
+ .parserConfiguration({ 'boolean-negation': false })
1010
1298
  .option('type', {
1011
1299
  type: 'string',
1012
1300
  choices: ['roku', 'ios', 'apple-tv', 'android', 'android-tv'],
@@ -1165,12 +1453,12 @@ catch (err) {
1165
1453
  process.exit(1);
1166
1454
  }
1167
1455
  })
1168
- .command('run:mcp', 'Start the Hosanna MCP server for AI agent debugging', yargs => yargs
1456
+ .command('mcp:start', 'Start the Hosanna MCP server for AI agent debugging', yargs => yargs
1169
1457
  .option('managementPort', { type: 'number', description: 'Management server port', default: 59150 })
1170
1458
  .option('managementHost', { type: 'string', description: 'Management server host', default: 'localhost' })
1171
1459
  .option('extensionPort', {
1172
1460
  type: 'number',
1173
- description: 'Debugger extension WebSocket port (must match hst run:debugger)',
1461
+ description: 'Debugger extension WebSocket port (must match hst debugger:start)',
1174
1462
  default: 59153,
1175
1463
  }), async (args) => {
1176
1464
  if (args.verbose) {
@@ -1184,12 +1472,12 @@ catch (err) {
1184
1472
  extensionPort: args.extensionPort,
1185
1473
  });
1186
1474
  })
1187
- .command(['stop:mcp', 'run:stop'], 'Stop the Hosanna MCP server (reads PID from the same lock file as run:mcp). Alias: run:stop', yargs => {
1475
+ .command('mcp:stop', 'Stop the Hosanna MCP server (reads PID from the same lock file as mcp:start).', yargs => {
1188
1476
  var _a;
1189
1477
  return yargs
1190
1478
  .option('managementPort', {
1191
1479
  type: 'number',
1192
- description: 'Must match the running instance (same as run:mcp)',
1480
+ description: 'Must match the running instance (same as mcp:start)',
1193
1481
  default: parseInt((_a = process.env.HOSANNA_MANAGEMENT_PORT) !== null && _a !== void 0 ? _a : '59150', 10),
1194
1482
  })
1195
1483
  .option('force', {
@@ -1225,7 +1513,7 @@ catch (err) {
1225
1513
  process.exit(1);
1226
1514
  }
1227
1515
  })
1228
- .command('run:dev', 'Run dev processes: vite, generator watch, and optional debugger', yargs => yargs
1516
+ .command('dev:start', 'Run dev processes: vite, generator watch, and optional debugger', yargs => yargs
1229
1517
  .option('vitePort', { type: 'number', description: 'Port for Vite dev server', default: 5173 })
1230
1518
  .option('webAppPort', { type: 'number', description: 'Port for web app client', default: 59151 })
1231
1519
  .option('deviceAppPort', { type: 'number', description: 'Port for device app client', default: 59152 })
@@ -1245,17 +1533,9 @@ catch (err) {
1245
1533
  defaultClientType: args.defaultClientType || 'web',
1246
1534
  appName: args.appName,
1247
1535
  });
1248
- })
1249
- .command('ci:configure-hosanna-url [gitUrl]', 'Configure hosanna.json git-url from an argument or HOSANNA_UI_GIT_URL', yargs => yargs
1250
- .positional('gitUrl', { type: 'string', describe: 'Git URL to set in hosanna.json (defaults to env HOSANNA_UI_GIT_URL)' }), args => {
1251
- if (args.verbose) {
1252
- process.env.HOSANNA_VERBOSE = '1';
1253
- process.env.HOSANNA_UPDATER_VERBOSE = '1';
1254
- }
1255
- (0, cli_configure_hosanna_url_js_1.runConfigureHosannaUrl)(typeof args.gitUrl === 'string' ? args.gitUrl : undefined);
1256
1536
  })
1257
1537
  .command('test:ui [recordings..]', 'Replay UI test recordings against a running Hosanna app and exit with pass/fail. ' +
1258
- 'Requires a running debug proxy (hst run:debugger) and a connected app.', yargs => yargs
1538
+ 'Requires a running debug proxy (hst debugger:start) and a connected app.', yargs => yargs
1259
1539
  .positional('recordings', {
1260
1540
  describe: 'Recording IDs to replay (omit to run all)',
1261
1541
  type: 'string',
@@ -2020,5 +2300,5 @@ catch (err) {
2020
2300
  .demandCommand(0)
2021
2301
  .strict()
2022
2302
  .parse();
2023
- // Note: legacy --env flag removed; use `hst env`
2303
+ // Note: legacy --env flag removed; use `hst env check`
2024
2304
  //# sourceMappingURL=cli.js.map