@tantawowa/hosanna-tools 4.0.1 → 5.0.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 (54) hide show
  1. package/README.md +70 -66
  2. package/dist/build-info.json +3 -3
  3. package/dist/cli.js +22 -186
  4. package/dist/cli.js.map +1 -1
  5. package/dist/index.d.ts +4 -12
  6. package/dist/index.js +5 -13
  7. package/dist/index.js.map +1 -1
  8. package/dist/lib/env-info.d.ts +9 -16
  9. package/dist/lib/env-info.js +117 -386
  10. package/dist/lib/env-info.js.map +1 -1
  11. package/dist/lib/hosanna-config.d.ts +1 -31
  12. package/dist/lib/hosanna-config.js +8 -363
  13. package/dist/lib/hosanna-config.js.map +1 -1
  14. package/dist/testing/client.js +2 -3
  15. package/dist/testing/client.js.map +1 -1
  16. package/dist/testing/config.d.ts +1 -0
  17. package/dist/testing/config.js +8 -0
  18. package/dist/testing/config.js.map +1 -1
  19. package/dist/testing/launcher.js +26 -29
  20. package/dist/testing/launcher.js.map +1 -1
  21. package/dist/testing/types.d.ts +5 -0
  22. package/dist/updater/framework-script-migration.d.ts +6 -0
  23. package/dist/updater/framework-script-migration.js +55 -0
  24. package/dist/updater/framework-script-migration.js.map +1 -0
  25. package/dist/updater/license-manager.d.ts +1 -80
  26. package/dist/updater/license-manager.js +4 -329
  27. package/dist/updater/license-manager.js.map +1 -1
  28. package/dist/updater/npm-framework.d.ts +19 -0
  29. package/dist/updater/npm-framework.js +214 -0
  30. package/dist/updater/npm-framework.js.map +1 -0
  31. package/dist/updater/sdk-manager.d.ts +2 -31
  32. package/dist/updater/sdk-manager.js +53 -804
  33. package/dist/updater/sdk-manager.js.map +1 -1
  34. package/dist/updater/symlink-manager.js +2 -21
  35. package/dist/updater/symlink-manager.js.map +1 -1
  36. package/dist/vite.d.ts +8 -0
  37. package/dist/vite.js +22 -6
  38. package/dist/vite.js.map +1 -1
  39. package/package.json +1 -1
  40. package/dist/lib/cli.configure-hosanna-url.d.ts +0 -11
  41. package/dist/lib/cli.configure-hosanna-url.js +0 -51
  42. package/dist/lib/cli.configure-hosanna-url.js.map +0 -1
  43. package/dist/lib/local-source-override.d.ts +0 -22
  44. package/dist/lib/local-source-override.js +0 -141
  45. package/dist/lib/local-source-override.js.map +0 -1
  46. package/dist/support-tools/framework-sync.d.ts +0 -75
  47. package/dist/support-tools/framework-sync.js +0 -348
  48. package/dist/support-tools/framework-sync.js.map +0 -1
  49. package/dist/updater/framework-installer.d.ts +0 -57
  50. package/dist/updater/framework-installer.js +0 -492
  51. package/dist/updater/framework-installer.js.map +0 -1
  52. package/dist/updater/local-sdk-manager.d.ts +0 -16
  53. package/dist/updater/local-sdk-manager.js +0 -225
  54. package/dist/updater/local-sdk-manager.js.map +0 -1
package/README.md CHANGED
@@ -42,46 +42,68 @@ Note, if you are developing hosanna-tools, you will install the package locally.
42
42
  npm install . -g # path to the hosanna-tools directory
43
43
  ```
44
44
 
45
- ## Hosanna source install modes
45
+ ## Install the framework
46
46
 
47
- Client projects can switch between private Keygen source tarballs, an embedded
48
- copy, a shared local checkout, and the legacy git/local modes:
47
+ The framework is an npm dependency. Install it with the tools in each app:
49
48
 
50
49
  ```bash
51
- hst config source # show current mode and immutable install identity
52
- hst config source keygen --sdk-version latest --install # newest valid private publication across all lanes
53
- hst config source keygen --sdk-version v1.29.1 --install # pin an exact immutable production release
54
- hst config source embedded --source-path ../hosanna-ui --install # filtered, reproducible copy
55
- hst config source shared --source-path ../hosanna-ui --install # portable relative link for local development
56
- hst config source git --install # legacy git checkout
57
- hst config source local # caller-provided LOCAL_SDK checkout
58
- ```
59
-
60
- Keygen mode writes `distribution: "keygen"`, `flavour: "fullSource"`, and no
61
- platform list to `hosanna.json`. Bare `latest` means the newest valid published
62
- full-source release by Keygen publication time, whether it came from a nightly,
63
- manual, or tag run. `@latest` remains the stable-lane alias and `@nightly`
64
- remains the development-lane alias. Exact versions are immutable pins.
65
- The same `HSC_LICENSE_KEY` used by the compiler authorizes source installation;
66
- there is no separate Hosanna source-license environment variable.
67
-
68
- Local `shared`, `embedded`, and `local` selections are private developer
69
- overrides. They do not rewrite `hosanna.json`: Git projects store the selection
70
- in worktree-specific Git metadata, so activating a local checkout cannot dirty
71
- the project or leak a machine path into a commit. `hst config source` and
72
- `hst env check` prominently report the active override, resolved checkout, and
73
- the committed distribution/version it temporarily supersedes. Running
74
- `hst sdk:install` or `hst framework:update` while an override is active preserves
75
- the local checkout. Select `keygen` or `git` with `--install
76
- --replace-existing` to clear the override and return to the committed source.
77
-
78
- Use `--replace-existing` with `--install` when switching an existing project
79
- between modes. Replacement is refused for an unrecognized or modified source
80
- root; `--force` is required for a dirty git checkout. Embedded installs exclude
81
- repository/build/secret state, write a content-hash manifest, and never modify
82
- the source checkout. Shared installs validate the source and create a relative
83
- symlink. Granular flavours and platform subsets are deliberately rejected with
84
- `NOT SUPPORTED YET — use flavour "fullSource"`.
50
+ npm install --save-dev --save-exact @tantawowa/hosanna-tools@5 @tantawowa/hosanna-ui@2 @tantawowa/hosanna-eslint-plugin@2
51
+ npx hst sdk:install
52
+ ```
53
+
54
+ Commit `package.json` and `package-lock.json`. A fresh checkout runs `npm ci`
55
+ followed by `npx hst sdk:install`. The command validates the installed package
56
+ and maintains these app paths:
57
+
58
+ ```text
59
+ hosanna-ui -> node_modules/@tantawowa/hosanna-ui
60
+ src/hosanna-ui -> ../hosanna-ui/src/hosanna-ui
61
+ ```
62
+
63
+ The other framework source folders use the same links. Existing `@hs-src`
64
+ imports, generated app files and compiler paths continue to work. Update the
65
+ framework with npm, then run `hst sdk:install` again. The command also reconciles
66
+ the framework's declared app dependencies. Use `--skip-consumer-dependencies`
67
+ when the app package manager has already installed them. Tools 5 requires
68
+ Vitest `>=3.2.0 <5`; update an older app test dependency during the npm upgrade.
69
+
70
+ For an app using the previous framework installer, install the npm packages
71
+ with `--ignore-scripts` first so old install hooks cannot run removed commands,
72
+ then run:
73
+
74
+ ```bash
75
+ npm install --ignore-scripts --save-dev --save-exact @tantawowa/hosanna-tools@5 @tantawowa/hosanna-ui@2 @tantawowa/hosanna-eslint-plugin@2
76
+ npx hst sdk:migrate --replace-existing
77
+ ```
78
+
79
+ Migration removes the old framework version/source settings and private source
80
+ override, preserves compiler and app settings, and replaces the old SDK folder
81
+ with the npm compatibility link. The previous real SDK folder and its Git
82
+ history remain in `hosanna-ui.before-npm`. It requires explicit replacement
83
+ and refuses to discard changes in a Git checkout. Move or commit those changes first.
84
+ Migration also updates exact known install, update, generation and cleanup
85
+ scripts. Custom scripts that select a framework source or generate inside the
86
+ SDK must be updated manually before migration; the command names them before
87
+ changing files. A failed migration restores app configuration, dependency
88
+ manifests, links and the previous SDK folder. After migration, run `npm rebuild`
89
+ if dependencies need their install scripts.
90
+
91
+ For local framework development, use npm's normal local package support:
92
+
93
+ ```bash
94
+ npm install --save-dev ../hosanna-ui
95
+ npx hst sdk:install
96
+ ```
97
+
98
+ A declared dependency can also be redirected with `npm link`; the installer
99
+ resolves the linked package through the same npm path. To return to a release,
100
+ install the desired npm version and rerun `hst sdk:install`.
101
+
102
+ `hosanna.json` retains `transpiler-version` and app integration settings such as
103
+ `symlink-extra-folders`. Compiler installation and licensing are unchanged.
104
+ `config source`, `framework:update`, `framework:sync` and `framework:doctor`
105
+ were removed in tools 5. Framework delivery no longer uses Git clones, Keygen
106
+ source artifacts or private source overrides.
85
107
 
86
108
  ## Embedding as a library
87
109
 
@@ -309,17 +331,6 @@ central `monorepo-release.yml` workflow after this package's npm trusted publish
309
331
  has been migrated. See [RELEASE.md](RELEASE.md) for the configuration and validation
310
332
  requirements.
311
333
 
312
- ## CLI: configure-hosanna-url
313
-
314
- - **purpose**: Updates `hosanna.json` with a new Hosanna UI Git URL under the `git-url` key.
315
- - **usage**:
316
- - `hst configure-hosanna-url https://github.com/YourOrg/hosanna-ui.git`
317
- - `HOSANNA_UI_GIT_URL=https://github.com/YourOrg/hosanna-ui.git hst configure-hosanna-url`
318
- - **behavior**:
319
- - If no positional `gitUrl` is provided, the command falls back to the `HOSANNA_UI_GIT_URL` environment variable.
320
- - If neither is provided, it prints a message and leaves `hosanna.json` unchanged.
321
- - On success, it prints a sanitized URL (protocol + host + path) for logs.
322
-
323
334
  # Hosanna Tools CLI
324
335
 
325
336
  Hosanna Tools is a comprehensive CLI toolset for the Hosanna framework that provides commands for development, building, deployment, and CI/CD operations.
@@ -435,10 +446,9 @@ npm run roku:build
435
446
  ### CI Commands (`ci:*`) - Continuous Integration
436
447
  - `ci:project-actions --base <ref> [--head <ref>]` - Select `hst.json` `prScopeRules` actions for changed files; add `--run` to execute matched npm scripts
437
448
  - `ci:extract-pkg-key` - Extract signing key from an existing signed Roku package as base64
438
- - `config set --git-url` - Configure hosanna.json git-url from argument or environment
439
449
 
440
- Project-specific PR rules belong in `hst.json`; SDK source/version settings
441
- remain in `hosanna.json`:
450
+ Project-specific PR rules belong in `hst.json`. The framework dependency and
451
+ version belong in `package.json` and `package-lock.json`:
442
452
 
443
453
  ```json
444
454
  {
@@ -495,8 +505,8 @@ The installed SDK owns its app dependency contract in
495
505
  Both maps are required. `hst doctor` and `hst env check` validate the exact
496
506
  manifest, the app's direct `package.json` declarations, and the versions
497
507
  actually available through the app's Node resolution path (including a
498
- workspace-hoisted install). An older SDK without the manifest is reported as a
499
- supported legacy install with no requirements.
508
+ workspace-hoisted install). The npm SDK must include this manifest; an
509
+ incomplete package fails the environment check.
500
510
 
501
511
  `hst doctor --fix` and `hst env fix` add missing requirements and run one
502
512
  app-root `npm install`. The repair is additive to the app's dependency set: it
@@ -513,10 +523,8 @@ rollback snapshot; run the workspace's package manager from its root instead.
513
523
  If `npm install` fails, or the installed
514
524
  versions still fail the audit afterward, `package.json` and npm lockfiles
515
525
  (`package-lock.json` and `npm-shrinkwrap.json`) are restored to their original
516
- contents. SDK installs and updates—including
517
- Git, Keygen, local, embedded, and shared source modes—run the same reconciliation
518
- after the new SDK is ready, so a framework upgrade cannot leave new app
519
- requirements undeclared.
526
+ contents. `sdk:install` and `sdk:migrate` run the same reconciliation after
527
+ resolving the npm package, so framework requirements remain declared in the app.
520
528
 
521
529
  Library callers can inspect or run the same focused operation through
522
530
  `env.dependencies.audit({ projectRoot })` and
@@ -525,13 +533,12 @@ repair results without exiting the process.
525
533
 
526
534
  ### Framework Source Symlinks (semver-keyed registry)
527
535
 
528
- `hst` symlinks framework source folders from the `hosanna-ui/` checkout into the
536
+ `hst` symlinks framework source folders through the `hosanna-ui/` npm compatibility link into the
529
537
  project (`src/hosanna-ui -> ../hosanna-ui/src/hosanna-ui`, etc.). Which folders
530
538
  get linked is decided by a **semver-keyed registry**: an entry applies when the
531
539
  installed framework version is **>=** its key, and the highest applicable key
532
540
  wins. The installed version is read from `hosanna-ui/package.json` (prerelease
533
- suffixes are ignored, so `1.31.0-next` counts as `1.31.0`) — this works for
534
- tags, branches, `@latest`, and `LOCAL_SDK` alike.
541
+ suffixes are ignored, so `2.0.0-next` counts as `2.0.0`).
535
542
 
536
543
  **Built-in defaults** (`src/updater/symlink-registry.ts`):
537
544
 
@@ -539,10 +546,7 @@ tags, branches, `@latest`, and `LOCAL_SDK` alike.
539
546
  |---|---|
540
547
  | >= 1.0.0 | `hosanna-bridge-core`, `hosanna-bridge-http`, `hosanna-bridge-lib`, `hosanna-bridge-targets`, `hosanna-list`, `hosanna-ui` |
541
548
 
542
- Prefer folder existence over version gates when adding registry entries: a
543
- checkout's `package.json` version can lag the tag it was cut from (hosanna-ui
544
- `v1.28.3` reads `1.28.1-next`), and folders absent from a checkout are already
545
- skipped safely at sync time.
549
+ Folders absent from the installed package are skipped when syncing links.
546
550
 
547
551
  `hst env check` always prints the active set, the matched registry version, and the
548
552
  source (`built-in`, `hosanna.json override`, `+ N extra`). `hst env fix`
@@ -556,8 +560,6 @@ to the resolved defaults; cannot break them. Folder names are forgiving
556
560
 
557
561
  ```json
558
562
  {
559
- "git-url": "git@github.com:TantaWowa/hosanna-ui.git",
560
- "sdk-version": "v1.31.0",
561
563
  "symlink-extra-folders": ["hosanna-game/", "hosanna-game-examples/"]
562
564
  }
563
565
  ```
@@ -604,6 +606,8 @@ silently break an install.
604
606
 
605
607
  Applications own regular Vitest suites. Global setup comes from `@tantawowa/hosanna-tools/testing/vitest`; worker fixtures and matchers come from `@tantawowa/hosanna-tools/testing/vitest/fixture`; shared lifecycle, types, and helpers come from `@tantawowa/hosanna-tools/testing`. There is no HST test CLI or recording/replay format.
606
608
 
609
+ `timeoutMs` sets the limit for each launch/readiness stage and browser navigation during initial launch, runtime relaunch, and `hs.app.deepLink(...)`. Navigation still waits for the page's `load` event. The default is 60 seconds; a positive, finite `HS_TEST_TIMEOUT_MS` overrides the config. This is a per-stage limit, not a deadline for the whole test. Vitest's `testTimeout` and `hookTimeout`, and timeouts passed to individual assertion waits, remain separate.
610
+
607
611
  Add `createHosannaReporter(config)` from `@tantawowa/hosanna-tools/testing/vitest/reporter` to the application's Vitest reporters. Every run then writes a self-contained JSON and HTML report under `test-results/<testRunId>/`, updates `test-results/latest.json` and `test-results/latest.html`, and includes every Vitest result plus any named visual proofs captured by the tests.
608
612
 
609
613
  Tests can record a complete browser emulator or Roku device run:
@@ -1,6 +1,6 @@
1
1
  {
2
- "buildDate": "2026-11-12T21:23:14+00:00",
3
- "buildDateISO": "2026-11-12T21:23:14.234Z",
2
+ "buildDate": "2026-11-12T23:40:49+00:00",
3
+ "buildDateISO": "2026-11-12T23:40:49.615Z",
4
4
  "timeZone": "UTC",
5
- "gitHash": "39818dc12"
5
+ "gitHash": "08a7d43e7"
6
6
  }
package/dist/cli.js CHANGED
@@ -243,14 +243,6 @@ const RUN_PLATFORM_CHOICES = ['browser', 'web', 'roku'];
243
243
  function stringArg(value) {
244
244
  return typeof value === 'string' && value.trim() ? value.trim() : undefined;
245
245
  }
246
- function stringArrayArg(value) {
247
- if (typeof value === 'string')
248
- return [value];
249
- if (Array.isArray(value)) {
250
- return value.filter((item) => typeof item === 'string');
251
- }
252
- return undefined;
253
- }
254
246
  function parseVideoCaptureRect(value) {
255
247
  if (value === undefined)
256
248
  return undefined;
@@ -391,7 +383,7 @@ async function runEnvironmentCommand(action, verbose) {
391
383
  // Let yargs handle all commands including env
392
384
  (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv))
393
385
  .version(`${buildVersion}\n git: ${buildGitHash}\n built: ${buildDate}`)
394
- .usage(`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 doctor Friendly environment/package check (use --fix to repair)\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 build Roku and Roku browser emulator builds\n run Roku and Roku browser emulator launches\n capture Standalone scenario video capture\n target:* Run target discovery and inventory\n roku:* Roku deployment, commands & packaging (run, command, 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 rasp:* RASP cert scripts`)
386
+ .usage(`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 doctor Friendly environment/package check (use --fix to repair)\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 build Roku and Roku browser emulator builds\n run Roku and Roku browser emulator launches\n capture Standalone scenario video capture\n target:* Run target discovery and inventory\n roku:* Roku deployment, commands & packaging (run, command, 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 license:* Hosanna compiler licensing\n secrets:* Portable .secrets files\n rasp:* RASP cert scripts`)
395
387
  .option('verbose', {
396
388
  type: 'boolean',
397
389
  default: false,
@@ -962,84 +954,23 @@ async function runEnvironmentCommand(action, verbose) {
962
954
  }
963
955
  })
964
956
  // remove old --env flag; env is now a subcommand
965
- .command('config [action] [mode]', 'Show or update hosanna.json configuration from a curated catalog', yargs => yargs
957
+ .command('config [action]', 'Show or update hosanna.json configuration from a curated catalog', yargs => yargs
966
958
  .positional('action', {
967
959
  type: 'string',
968
- choices: ['show', 'set', 'wizard', 'source'],
969
- describe: 'show prints current config, set writes values from flags, wizard runs the interactive setup, source switches install mode',
960
+ choices: ['show', 'set', 'wizard'],
961
+ describe: 'show prints compiler config, set writes values from flags, wizard runs compiler setup',
970
962
  })
971
- .positional('mode', {
972
- type: 'string',
973
- choices: ['show', 'keygen', 'git', 'local', 'embedded', 'shared'],
974
- describe: 'Source install mode when action is source',
975
- })
976
- .option('git-url', { type: 'string', describe: 'Set hosanna.json git-url' })
977
- .option('sdk-version', { type: 'string', describe: 'Set hosanna.json sdk-version' })
978
- .option('branch', { type: 'string', describe: 'Set hosanna.json branch; use an empty string to prefer sdk-version' })
979
963
  .option('transpiler-version', { type: 'string', describe: 'Set hosanna.json transpiler-version' })
980
964
  .option('config-catalog-url', { type: 'string', describe: 'Persist a remote catalog URL into hosanna.json' })
981
965
  .option('catalog-url', { type: 'string', describe: 'Use a remote catalog URL for this run without writing it' })
982
- .option('flavour', { type: 'string', choices: ['fullSource', 'core', 'game', 'full'], describe: 'Keygen source flavour when action is source (supported: fullSource)' })
983
- .option('platforms', { type: 'string', array: true, describe: 'Future split-platform selection; omit for fullSource' })
984
- .option('source-path', { type: 'string', describe: 'Hosanna UI checkout to copy for embedded mode or link for shared mode' })
985
- .option('install', { type: 'boolean', default: false, describe: 'Install the selected source mode atomically after updating config' })
986
- .option('replace-existing', { type: 'boolean', default: false, describe: 'Allow --install to replace an existing hosanna-ui source root when switching modes' })
987
- .option('force', { type: 'boolean', default: false, describe: 'Allow --install to replace a dirty git checkout when used with --replace-existing' })
988
966
  .option('fix', { type: 'boolean', default: false, describe: 'Run hst env fix after updating config' }), async (args) => {
989
967
  if (args.verbose) {
990
968
  process.env.HOSANNA_VERBOSE = '1';
991
969
  process.env.HOSANNA_UPDATER_VERBOSE = '1';
992
970
  }
993
971
  const actionArg = typeof args.action === 'string' ? args.action : undefined;
994
- if (actionArg === 'source') {
995
- try {
996
- const result = await (0, hosanna_config_js_1.runHosannaSourceConfig)({
997
- cwd: process.cwd(),
998
- mode: (typeof args.mode === 'string' ? args.mode : 'show'),
999
- flavour: args.flavour,
1000
- platforms: (0, hosanna_config_js_1.parsePlatformList)(stringArrayArg(args.platforms)),
1001
- sourcePath: typeof args.sourcePath === 'string' ? args.sourcePath : undefined,
1002
- sdkVersion: typeof args.sdkVersion === 'string' ? args.sdkVersion : undefined,
1003
- gitUrl: typeof args.gitUrl === 'string' ? args.gitUrl : undefined,
1004
- branch: typeof args.branch === 'string' ? args.branch : undefined,
1005
- install: Boolean(args.install),
1006
- replaceExisting: Boolean(args.replaceExisting),
1007
- force: Boolean(args.force),
1008
- writeStdout: text => console.info(text),
1009
- });
1010
- if (args.fix && result.updated) {
1011
- const { fixAllEnvironmentIssues } = await Promise.resolve().then(() => __importStar(require('./lib/env-info.js')));
1012
- const fixResult = await fixAllEnvironmentIssues(process.cwd());
1013
- console.info(`\n${fixResult.message}`);
1014
- if (fixResult.remainingIssues.length > 0) {
1015
- console.info('\n❌ Remaining issues:');
1016
- for (const issue of fixResult.remainingIssues) {
1017
- console.info(` • ${issue}`);
1018
- }
1019
- }
1020
- if (!process.env.VITEST)
1021
- process.exit(fixResult.success ? 0 : 1);
1022
- return;
1023
- }
1024
- if (!process.env.VITEST)
1025
- process.exit(0);
1026
- return;
1027
- }
1028
- catch (err) {
1029
- console.error('❌ Failed to configure source install mode:', err instanceof Error ? err.message : err);
1030
- if (!process.env.VITEST)
1031
- process.exit(1);
1032
- return;
1033
- }
1034
- }
1035
972
  const action = actionArg === 'show' || actionArg === 'set' ? actionArg : 'wizard';
1036
973
  const updates = {};
1037
- if (typeof args.gitUrl === 'string')
1038
- updates['git-url'] = args.gitUrl;
1039
- if (typeof args.sdkVersion === 'string')
1040
- updates['sdk-version'] = args.sdkVersion;
1041
- if (typeof args.branch === 'string')
1042
- updates.branch = args.branch;
1043
974
  if (typeof args.transpilerVersion === 'string')
1044
975
  updates['transpiler-version'] = args.transpilerVersion;
1045
976
  if (typeof args.configCatalogUrl === 'string')
@@ -1447,124 +1378,33 @@ async function runEnvironmentCommand(action, verbose) {
1447
1378
  process.exit(1);
1448
1379
  }
1449
1380
  })
1450
- .command('sdk:install', 'Install the SDK. Uses git (hosanna.json git-url) or license-gated keygen artifacts (hosanna.json "distribution": "keygen")', yargs => yargs
1451
- .option('force', { type: 'boolean', default: false, describe: 'Reinstall even if the requested version is already installed (keygen mode)' })
1452
- .option('skip-consumer-dependencies', { type: 'boolean', default: false, describe: 'Install the SDK without changing the app dependency manifest or installing app dependencies' }), async (args) => {
1453
- if (args.verbose) {
1454
- process.env.HOSANNA_VERBOSE = '1';
1455
- process.env.HOSANNA_UPDATER_VERBOSE = '1';
1456
- }
1457
- console.info('Running sdk:install command...');
1458
- try {
1459
- const skipConsumerDependencyReconciliation = Boolean(args['skip-consumer-dependencies']);
1460
- const { assertLocalSourceOverrideInstalled, readLocalSourceOverride } = await Promise.resolve().then(() => __importStar(require('./lib/local-source-override.js')));
1461
- const localOverride = readLocalSourceOverride(process.cwd());
1462
- if (localOverride) {
1463
- assertLocalSourceOverrideInstalled(process.cwd(), localOverride);
1464
- const { prepareLocalSdkIntegration } = await Promise.resolve().then(() => __importStar(require('./updater/local-sdk-manager.js')));
1465
- prepareLocalSdkIntegration(process.cwd(), { skipConsumerDependencyReconciliation });
1466
- console.info(`Private ${localOverride.mode} Hosanna source override is active; preserved the local SDK and skipped the committed distribution install.`);
1467
- console.info('sdk:install command completed.');
1468
- process.exit(0);
1469
- return;
1470
- }
1471
- // Keygen-distributed projects bypass the git flow entirely.
1472
- const hosannaJsonPath = path.resolve(process.cwd(), 'hosanna.json');
1473
- let distribution;
1474
- if (fs.existsSync(hosannaJsonPath)) {
1475
- try {
1476
- distribution = JSON.parse(fs.readFileSync(hosannaJsonPath, 'utf-8')).distribution;
1477
- }
1478
- catch ( /* fall through to git flow which reports config errors */_a) { /* fall through to git flow which reports config errors */ }
1479
- }
1480
- if (distribution === 'keygen') {
1481
- const mod = await Promise.resolve().then(() => __importStar(require('./updater/framework-installer.js')));
1482
- await mod.installFrameworkFromKeygen({ force: Boolean(args.force), skipConsumerDependencyReconciliation });
1483
- }
1484
- else {
1485
- await (0, sdk_manager_js_1.installSdk)(false, false, true, { skipConsumerDependencyReconciliation });
1486
- }
1487
- console.info('sdk:install command completed.');
1488
- process.exit(0); // Exit after successful completion
1489
- }
1490
- catch (err) {
1491
- console.error('An error occurred during the install process:', err);
1492
- process.exit(1); // Exit with error code on failure
1493
- }
1494
- })
1495
- .command('framework:update', 'Update the keygen-distributed hosanna-ui folder to the version in hosanna.json (no-op when already current)', yargs => yargs
1496
- .option('force', { type: 'boolean', default: false, describe: 'Reinstall even if the requested version is already installed' }), async (args) => {
1497
- if (args.verbose) {
1498
- process.env.HOSANNA_VERBOSE = '1';
1499
- process.env.HOSANNA_UPDATER_VERBOSE = '1';
1500
- }
1381
+ .command('sdk:install', 'Integrate the npm-installed @tantawowa/hosanna-ui package and repair its source links', yargs => yargs.option('skip-consumer-dependencies', { type: 'boolean', default: false, describe: 'Repair links without changing app dependency declarations' }), async (args) => {
1501
1382
  try {
1502
- const { assertLocalSourceOverrideInstalled, readLocalSourceOverride } = await Promise.resolve().then(() => __importStar(require('./lib/local-source-override.js')));
1503
- const localOverride = readLocalSourceOverride(process.cwd());
1504
- if (localOverride) {
1505
- assertLocalSourceOverrideInstalled(process.cwd(), localOverride);
1506
- const { prepareLocalSdkIntegration } = await Promise.resolve().then(() => __importStar(require('./updater/local-sdk-manager.js')));
1507
- prepareLocalSdkIntegration(process.cwd());
1508
- console.info(`Private ${localOverride.mode} Hosanna source override is active; preserved the local SDK and skipped framework:update.`);
1383
+ await (0, sdk_manager_js_1.installSdk)({ skipConsumerDependencyReconciliation: Boolean(args['skip-consumer-dependencies']) });
1384
+ if (!process.env.VITEST)
1509
1385
  process.exit(0);
1510
- return;
1511
- }
1512
- const mod = await Promise.resolve().then(() => __importStar(require('./updater/framework-installer.js')));
1513
- await mod.installFrameworkFromKeygen({ force: Boolean(args.force) });
1514
- process.exit(0);
1515
1386
  }
1516
- catch (err) {
1517
- console.error('❌ framework:update failed:', err instanceof Error ? err.message : err);
1518
- process.exit(1);
1387
+ catch (error) {
1388
+ console.error(error instanceof Error ? error.message : error);
1389
+ if (!process.env.VITEST)
1390
+ process.exit(1);
1519
1391
  }
1520
1392
  })
1521
- .command('framework:sync', 'Maintainer: fast-forward tier-1 client forks (branches + tags) from upstream, driven by ~/.hosanna/sync-clients.json', yargs => yargs
1522
- .option('client', { type: 'string', describe: 'Sync a single named client from the registry (default: all clients)' })
1523
- .option('registry', { type: 'string', describe: 'Path to the sync registry (default: ~/.hosanna/sync-clients.json)' })
1524
- .option('dry-run', { type: 'boolean', default: false, describe: 'Show what would be pushed without pushing' })
1525
- .option('init', { type: 'boolean', default: false, describe: 'Create a template registry file and exit' })
1526
- .example('$0 framework:sync', 'Sync all registered client forks')
1527
- .example('$0 framework:sync --client client1 --dry-run', 'Preview a sync for one client'), async (args) => {
1393
+ .command('sdk:migrate', 'Migrate an existing app to its npm-installed framework, preserving compiler configuration', yargs => yargs
1394
+ .option('replace-existing', { type: 'boolean', default: false, describe: 'Replace the old SDK folder or link; dirty Git checkouts are always protected' })
1395
+ .option('skip-consumer-dependencies', { type: 'boolean', default: false, describe: 'Migrate config and links without changing app dependency declarations' }), async (args) => {
1528
1396
  try {
1529
- const mod = await Promise.resolve().then(() => __importStar(require('./support-tools/framework-sync.js')));
1530
- if (args.init) {
1531
- const filePath = mod.initRegistry(args.registry);
1532
- console.info(`✅ Created sync registry template: ${filePath}`);
1533
- console.info('Edit it to add your clients, then run: hst framework:sync');
1397
+ await (0, sdk_manager_js_1.migrateSdk)({ replaceExisting: Boolean(args['replace-existing']), skipConsumerDependencyReconciliation: Boolean(args['skip-consumer-dependencies']) });
1398
+ if (!process.env.VITEST)
1534
1399
  process.exit(0);
1535
- }
1536
- const results = await mod.syncFrameworkForks({
1537
- registryPath: args.registry,
1538
- client: args.client,
1539
- dryRun: Boolean(args['dry-run']),
1540
- });
1541
- const ok = mod.printSyncSummary(results, Boolean(args['dry-run']));
1542
- process.exit(ok ? 0 : 1);
1543
- }
1544
- catch (err) {
1545
- console.error('❌ framework:sync failed:', err instanceof Error ? err.message : err);
1546
- process.exit(1);
1547
- }
1548
- })
1549
- .command('framework:doctor', 'Maintainer: validate tier-1 client forks (reachability, hosanna.json git-url/schema, sdk-version tags)', yargs => yargs
1550
- .option('client', { type: 'string', describe: 'Check a single named client from the registry (default: all clients)' })
1551
- .option('registry', { type: 'string', describe: 'Path to the sync registry (default: ~/.hosanna/sync-clients.json)' })
1552
- .example('$0 framework:doctor', 'Validate all registered client forks'), async (args) => {
1553
- try {
1554
- const mod = await Promise.resolve().then(() => __importStar(require('./support-tools/framework-sync.js')));
1555
- const issues = await mod.doctorFrameworkForks({
1556
- registryPath: args.registry,
1557
- client: args.client,
1558
- });
1559
- const ok = mod.printDoctorSummary(issues);
1560
- process.exit(ok ? 0 : 1);
1561
1400
  }
1562
- catch (err) {
1563
- console.error('❌ framework:doctor failed:', err instanceof Error ? err.message : err);
1564
- process.exit(1);
1401
+ catch (error) {
1402
+ console.error(error instanceof Error ? error.message : error);
1403
+ if (!process.env.VITEST)
1404
+ process.exit(1);
1565
1405
  }
1566
1406
  })
1567
- .command('license:activate <key>', 'Store your Hosanna framework license key (~/.hosanna/license.key) after validating it', yargs => yargs
1407
+ .command('license:activate <key>', 'Store your Hosanna compiler license key (~/.hosanna/license.key) after validating it', yargs => yargs
1568
1408
  .positional('key', { type: 'string', demandOption: true, describe: 'License key issued for your organization' }), async (args) => {
1569
1409
  try {
1570
1410
  const mod = await Promise.resolve().then(() => __importStar(require('./updater/license-manager.js')));
@@ -1591,7 +1431,7 @@ async function runEnvironmentCommand(action, verbose) {
1591
1431
  process.exit(1);
1592
1432
  }
1593
1433
  })
1594
- .command('license:status', 'Validate the stored license and print its entitlements (flavour, platforms, tier)', yargs => yargs, async () => {
1434
+ .command('license:status', 'Validate the stored compiler license and print its entitlements', yargs => yargs, async () => {
1595
1435
  try {
1596
1436
  const mod = await Promise.resolve().then(() => __importStar(require('./updater/license-manager.js')));
1597
1437
  const credential = mod.requireLicenseCredential();
@@ -1607,10 +1447,6 @@ async function runEnvironmentCommand(action, verbose) {
1607
1447
  if (info.validation.expiry)
1608
1448
  console.info(` Expires: ${info.validation.expiry}`);
1609
1449
  console.info(` Credential: ${credential.source === 'environment' ? 'HSC_LICENSE_KEY' : credential.filePath}`);
1610
- const flavours = mod.entitledFlavours(info.entitlements);
1611
- const platforms = mod.entitledPlatforms(info.entitlements);
1612
- console.info(` Flavour: ${flavours.length ? flavours[flavours.length - 1] : '(none)'}`);
1613
- console.info(` Platforms: ${platforms.length ? platforms.join(', ') : '(none)'}`);
1614
1450
  console.info(` Entitlements: ${info.entitlements.join(', ') || '(none)'}`);
1615
1451
  for (const warning of mod.getLicenseConfigurationWarnings()) {
1616
1452
  console.warn(` ⚠ ${warning}`);