stack/turbo.json
BilalG1 526ce69ab9
feat(cli): fetch RDE dashboard from GitHub Releases instead of bundling (#1682)
## Summary

The RDE dashboard was bundled into the `@hexclave/cli` npm tarball
(~165MB). That made the `npx @latest` auto-update re-exec pull a huge
tarball, which tripped download firewalls (Socket Firewall
false-positives on Replit → `Lock compromised`).

This decouples the dashboard from the CLI: it's published as a **GitHub
Release artifact** (a zip + `manifest.json` on a floating
`dashboard-latest` release) and fetched at runtime, cached on disk. The
dashboard now rolls forward **independently of the CLI version**
(assuming newest dashboards stay compatible with older CLIs — no compat
gate yet, by design for now).

## How it works

- **Runtime** (`packages/cli/src/lib/dashboard-release.ts`): on
`hexclave dev`, fetch `manifest.json` (`{version, sha256, url}`) from a
stable, no-API GitHub URL → cache under
`<dirname(devEnvStatePath())>/dashboards/<version>/` → streamed
download, **sha256 verify**, extract via the existing `extract-zip` dep,
atomic publish. Offline → fall back to the newest cached build.
- **Wiring** (`dev.ts`): `startDashboardIfNeeded` resolves/launches the
cached release; the restart decision now compares the **dashboard
release** version (not the CLI version). `HEXCLAVE_DASHBOARD_DIR` runs a
local build with zero network; a custom dev dashboard command bypasses
releases entirely.
- **No more npx self-update**: the `npx @latest` re-exec in `hexclave
dev` is removed entirely (along with `self-update.ts`, the
`--no-auto-update` flag, and the binName plumbing that only built the
npx call). It existed to keep the bundled dashboard fresh; now the
dashboard self-updates from GitHub, so the re-exec only added an npx
download on every run — the firewall surface. Users update the CLI via
npm/npx themselves.
- **Publishing** (`scripts/package-dashboard-release.mjs` +
`.github/workflows/dashboard-release.yaml`): build standalone → zip →
write manifest → publish immutable `dashboard-v<version>` release +
clobber the `dashboard-latest` manifest. **Fails loudly** if an existing
tag's asset sha differs from a fresh build (dashboard changed without a
version bump → would otherwise advertise a hash the served zip doesn't
match).
- **Build**: CLI build is now `tsdown`-only (no bundled dashboard);
`turbo.json` drops the dashboard standalone build from the CLI build
graph.

## Testing

- **Unit**: 135 CLI tests pass (manifest parsing incl. path-safe version
validation, URL resolution, version picking, dir override).
- **End-to-end (no GitHub)**: a localhost HTTP server + fixture zip
exercised the full fetch path — download, sha256 verify, cache-hit,
roll-forward, **corrupt-sha rejection**, offline→cache fallback (9/9).
- **Real dashboard, no GitHub**: built the standalone, served the real
76MB zip over `python -m http.server`, and confirmed `hexclave dev`
downloads → verifies → extracts → **boots** the dashboard and registers
an RDE session against the hosted backend. Also validated
`HEXCLAVE_DASHBOARD_DIR` boot.

## Review

Three review agents (runtime correctness, packaging/workflow, code
quality) ran; valid findings fixed: path-traversal hardening on
`version`, a concurrent cache rm/rename race, the workflow
sha-mismatch/atomicity gap, plus `errorMessage` dedup and added tests.

## Notes / follow-ups

- No CLI↔dashboard compatibility gate yet (assumes always-compatible);
easy to add later via a min-CLI field in the manifest. This matters more
now that the CLI no longer auto-updates.
- The publish workflow has not run yet, so the first push to `main` will
create the `dashboard-latest` release.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added automated, immutable versioned releases for the standalone
dashboard plus a continuously updated “latest” manifest.
* CLI now fetches, verifies, caches, and supports offline fallback for
the dashboard runtime.
* **Bug Fixes**
* Improved dashboard restart behavior to update only when a strictly
newer published version is available.
* **Chores**
* Removed the CLI’s automatic update/re-launch behavior for more
predictable startup.
* **Tests**
* Added unit tests for dashboard manifest validation and latest-version
selection.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-30 16:12:13 -07:00

175 lines
3.2 KiB
JSON

{
"$schema": "https://turbo.build/schema.json",
"globalEnv": [
"STACK_*",
"HEXCLAVE_*",
"CRON_SECRET",
"CRON_SECRET_OLD",
"BLOB_READ_WRITE_TOKEN",
"NEXT_PUBLIC_*",
"VITE_*",
"NEXT_PUBLIC_SENTRY_*",
"SENTRY_*",
"VERCEL_GIT_COMMIT_SHA",
"NEXT_PUBLIC_POSTHOG_*",
"POSTHOG_*",
"NODE_ENV",
"QUETZAL_*",
"OTEL_*",
"DISCORD_WEBHOOK_URL",
"DISCORD_CHANNEL_ID",
"DISCORD_BOT_TOKEN",
"ENABLE_EXPERIMENTAL_COREPACK"
],
"tasks": {
"build": {
"inputs": [
"$TURBO_DEFAULT$",
".env",
".env.local",
".env.development",
".env.development.local",
".env.production",
".env.production.local",
"tsdown.config.ts"
],
"dependsOn": [
"^build"
],
"outputs": [
".next",
"dist/**",
"lib/**",
"*.js",
"*.d.ts",
"*.d.ts.map"
],
"outputLogs": "new-only"
},
"build:rde-standalone": {
"inputs": [
"$TURBO_DEFAULT$",
".env",
".env.local",
".env.development",
".env.development.local",
".env.production",
".env.production.local"
],
"dependsOn": [
"build"
],
"outputs": [
".next/**",
"src/generated/bundled-type-definitions.ts"
],
"outputLogs": "new-only"
},
"docker-build": {
"inputs": [
"$TURBO_DEFAULT$",
".env",
".env.local",
".env.development",
".env.development.local",
".env.production",
".env.production.local"
],
"dependsOn": [
"^build"
],
"outputs": [
".next",
"dist/**",
"lib/**",
"*.js",
"*.d.ts",
"*.d.ts.map"
],
"outputLogs": "new-only"
},
"@hexclave/backend#build": {
"dependsOn": [
"codegen"
]
},
"@hexclave/cli#build": {
"dependsOn": [
"^build"
],
"outputs": [
"dist/**"
]
},
"@hexclave/next#build": {
"dependsOn": [
"^build",
"@hexclave/template#build"
]
},
"@hexclave/react#build": {
"dependsOn": [
"^build",
"@hexclave/template#build"
]
},
"@hexclave/tanstack-start#build": {
"dependsOn": [
"^build",
"@hexclave/template#build"
]
},
"@hexclave/js#build": {
"dependsOn": [
"^build",
"@hexclave/template#build"
]
},
"clean": {
"cache": false
},
"dev": {
"persistent": true,
"cache": false
},
"dev:tui": {
"persistent": true,
"cache": false
},
"start": {
"persistent": true,
"cache": false
},
"lint": {
"dependsOn": []
},
"codegen": {
"dependsOn": [
"^build"
],
"cache": false
},
"typecheck": {
"dependsOn": []
},
"generate-keys": {
"cache": false
},
"db:migration-gen": {
"cache": false
},
"db:reset": {
"cache": false
},
"db:seed": {
"cache": false
},
"db:init": {
"cache": false
},
"db:migrate": {
"cache": false
}
}
}