feat(mosaic): IUV-M02 — CORS/FQDN UX polish + skill installer rework #444

Merged
jason.woltje merged 1 commits from feat/install-ux-polish into main 2026-04-05 23:44:08 +00:00
Owner

Summary

Closes #437

IUV-02-01: Replace CORS origin prompt with FQDN/hostname input

Problem: The installer prompted users for a raw CORS origin string (http://localhost:3000). Users don't know what CORS is.

Fix:

  • New deriveCorsOrigin(hostname, webUiPort, useHttps?) pure function
  • Prompt now asks "Web UI hostname" (default: localhost)
  • Auto-detects protocol: localhost/127.0.0.1 always http; remote hosts default to https
  • For remote hosts, a follow-up confirm asks "Is HTTPS enabled?" (defaults to yes)
  • Headless: MOSAIC_HOSTNAME env var as a friendly alternative to MOSAIC_CORS_ORIGIN
  • GatewayState gains optional hostname field to track the raw user input
  • Fallback paths now read GATEWAY_CORS_ORIGIN from existing .env instead of hardcoding

IUV-02-02: Skill installer failure modes diagnosed

  1. Selection → installation gap: syncSkills() in finalize.ts completely ignored state.selectedSkills. The multiselect UI was a no-op — ALL skills were synced regardless of what the user picked.
  2. Silent failure: If the skills sync script exited non-zero, the error was swallowed in a bare catch {}. If the script was missing, nothing happened with no message to the user.
  3. No per-skill granularity: mosaic-sync-skills was all-or-nothing rsync with no whitelist concept. There was no mechanism to link only a subset of skills.

IUV-02-03: Skill installer rework

  • syncSkills() now accepts selectedSkills[] and passes MOSAIC_INSTALL_SKILLS (colon-separated) to the bash script
  • mosaic-sync-skills filters the linking phase to only the whitelisted skills when MOSAIC_INSTALL_SKILLS is set (empty/unset = all skills, preserving legacy mosaic sync behavior)
  • Script not found → clear error message instead of silent no-op
  • Non-zero exit code → stderr captured and shown to the user
  • Post-install summary reports "N installed" or failure reason with recovery hint

IUV-02-04: Tests + gates

  • 13 unit tests for deriveCorsOrigin (localhost, loopback, remote, https override)
  • 5 integration tests for finalize skill installer (selection, skip, failure modes, missing script)
  • 237 tests passing across 26 test files
  • pnpm typecheck && pnpm lint && pnpm format:check all green

Test plan

  • Unit tests: pnpm --filter @mosaicstack/mosaic test
  • Typecheck: pnpm typecheck
  • Lint: pnpm lint
  • Format: pnpm format:check
  • CI green on Woodpecker

🤖 Generated with Claude Code

## Summary Closes #437 ### IUV-02-01: Replace CORS origin prompt with FQDN/hostname input **Problem:** The installer prompted users for a raw CORS origin string (`http://localhost:3000`). Users don't know what CORS is. **Fix:** - New `deriveCorsOrigin(hostname, webUiPort, useHttps?)` pure function - Prompt now asks **"Web UI hostname"** (default: `localhost`) - Auto-detects protocol: `localhost`/`127.0.0.1` always http; remote hosts default to https - For remote hosts, a follow-up confirm asks "Is HTTPS enabled?" (defaults to yes) - Headless: `MOSAIC_HOSTNAME` env var as a friendly alternative to `MOSAIC_CORS_ORIGIN` - `GatewayState` gains optional `hostname` field to track the raw user input - Fallback paths now read `GATEWAY_CORS_ORIGIN` from existing .env instead of hardcoding ### IUV-02-02: Skill installer failure modes diagnosed 1. **Selection → installation gap:** `syncSkills()` in `finalize.ts` completely ignored `state.selectedSkills`. The multiselect UI was a no-op — ALL skills were synced regardless of what the user picked. 2. **Silent failure:** If the skills sync script exited non-zero, the error was swallowed in a bare `catch {}`. If the script was missing, nothing happened with no message to the user. 3. **No per-skill granularity:** `mosaic-sync-skills` was all-or-nothing rsync with no whitelist concept. There was no mechanism to link only a subset of skills. ### IUV-02-03: Skill installer rework - `syncSkills()` now accepts `selectedSkills[]` and passes `MOSAIC_INSTALL_SKILLS` (colon-separated) to the bash script - `mosaic-sync-skills` filters the linking phase to only the whitelisted skills when `MOSAIC_INSTALL_SKILLS` is set (empty/unset = all skills, preserving legacy `mosaic sync` behavior) - Script not found → clear error message instead of silent no-op - Non-zero exit code → stderr captured and shown to the user - Post-install summary reports "N installed" or failure reason with recovery hint ### IUV-02-04: Tests + gates - 13 unit tests for `deriveCorsOrigin` (localhost, loopback, remote, https override) - 5 integration tests for finalize skill installer (selection, skip, failure modes, missing script) - 237 tests passing across 26 test files - `pnpm typecheck && pnpm lint && pnpm format:check` all green ## Test plan - [ ] Unit tests: `pnpm --filter @mosaicstack/mosaic test` - [ ] Typecheck: `pnpm typecheck` - [ ] Lint: `pnpm lint` - [ ] Format: `pnpm format:check` - [ ] CI green on Woodpecker 🤖 Generated with [Claude Code](https://claude.com/claude-code)
jason.woltje added 1 commit 2026-04-05 23:40:32 +00:00
feat(mosaic): IUV-M02 — CORS/FQDN UX polish + skill installer rework (#437)
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
ci/woodpecker/pr/ci Pipeline was successful
26c1042a76
IUV-02-01: Replace raw CORS origin prompt with a friendly hostname input
- Add `deriveCorsOrigin(hostname, webUiPort, useHttps?)` pure function
- Prompt asks "Web UI hostname" (default: localhost) instead of "CORS origin"
- Auto-detects http vs https: localhost/127.0.0.1 always http, remote defaults to https
- For remote hosts, asks "Is HTTPS enabled?" (defaults to yes)
- Headless: MOSAIC_HOSTNAME env var as friendly alternative to MOSAIC_CORS_ORIGIN
- GatewayState gains optional `hostname` field to track the raw input
- Fallback paths now read GATEWAY_CORS_ORIGIN from .env instead of hardcoding

IUV-02-02: Diagnose skill installer failure modes (documented in PR body)
- Selection → installation gap: syncSkills() ignored state.selectedSkills entirely
- Silent failure: missing catalog directory had no user-visible error
- No per-skill granularity: all-or-nothing rsync with no whitelist concept

IUV-02-03: Rework skill installer end-to-end
- syncSkills() now accepts selectedSkills[] and passes MOSAIC_INSTALL_SKILLS
  (colon-separated) to the bash script
- Script filters linking to only the whitelisted skills when MOSAIC_INSTALL_SKILLS is set
- Missing script surfaced clearly instead of silently swallowed
- Non-zero exit captured from stderr and shown to the user
- Post-install summary reports "N installed" or failure reason

IUV-02-04: Tests + gates
- 13 unit tests for deriveCorsOrigin covering localhost, remote, https override
- 5 integration tests for finalize skill installer (selection, skip, failure, missing script)
- pnpm typecheck + lint + format:check all green
- 237 tests passing (26 test files)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
jason.woltje merged commit 172bacb30f into main 2026-04-05 23:44:08 +00:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: mosaicstack/stack#444