mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
build(layer2+3): propagate .build/+dist/ to Docker/Electron/CI; codify light deploy; docs
Layer 3: Dockerfile COPY .next/standalone -> .build/next/standalone (+cache mount); electron stage -> .build/electron-standalone + extraResources; CI asserts dist/server.js; eslint ignores .build/**+dist/**. Layer 2: deploy-vps-* skills use build:release + rsync(dist) -> remote app/ + pm2 restart + BUILD_SHA verify (drop npm-i-g/legacy-peer-deps/manual-wreq). Docs (RELEASE_CHECKLIST/CONTRIBUTING/AGENTS/CHANGELOG) describe src/+.build/+dist/ layout. Verified: docker build exit 0 + health 200; electron stage OK; no stale build-output refs.
This commit is contained in:
@@ -5,36 +5,99 @@ description: Deploy the latest OmniRoute code to the Akamai VPS (69.164.221.35)
|
||||
|
||||
# Deploy to Akamai VPS Workflow
|
||||
|
||||
Deploy OmniRoute to the Akamai VPS using `npm pack + scp` + PM2.
|
||||
Deploy OmniRoute to the Akamai VPS using a clean `build:release` + `rsync` + PM2 restart.
|
||||
|
||||
**Akamai VPS:** `69.164.221.35`
|
||||
**Process manager:** PM2 (`omniroute`)
|
||||
**Port:** `20128`
|
||||
**Remote install dir:** `/usr/lib/node_modules/omniroute/app/` (VPS image dir — unchanged)
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **Build must run where `node_modules` is REAL.** A git worktree with symlinked
|
||||
> `node_modules` breaks the Next.js standalone assembly. Always build from the main
|
||||
> checkout (or an `npm ci`'d worktree), never from `OmniRoute-*` worktrees unless
|
||||
> you first ran `npm ci` inside them.
|
||||
|
||||
## Steps
|
||||
|
||||
### 1. Build + pack locally
|
||||
### 1. Clean build + sentinel
|
||||
|
||||
Run from a checkout with a **real** `node_modules`:
|
||||
|
||||
// turbo
|
||||
|
||||
```bash
|
||||
cd /home/diegosouzapw/dev/proxys/OmniRoute && rm -f omniroute-*.tgz && rm -rf .next/cache app/.next/cache && npm run build:cli && rm -rf app/logs app/coverage app/.git app/.app-build-backup* && npm pack --ignore-scripts
|
||||
cd /home/diegosouzapw/dev/proxys/OmniRoute && npm run build:release
|
||||
```
|
||||
|
||||
### 2. Copy to Akamai VPS and install
|
||||
`build:release` does:
|
||||
1. Deletes `.build/` and `dist/` (clean rebuild — no stale cache)
|
||||
2. Runs `next build` → `.build/next/` (intermediates)
|
||||
3. Assembles the shippable bundle into `dist/` via `assembleStandalone`
|
||||
4. Writes `dist/BUILD_SHA` = `git rev-parse --short HEAD` (deploy sentinel)
|
||||
|
||||
Verify the sentinel before shipping:
|
||||
|
||||
```bash
|
||||
cat /home/diegosouzapw/dev/proxys/OmniRoute/dist/BUILD_SHA && git -C /home/diegosouzapw/dev/proxys/OmniRoute rev-parse --short HEAD
|
||||
```
|
||||
|
||||
Both lines must match. If they differ, the build is stale — re-run `build:release`.
|
||||
|
||||
### 2. Back up the running bundle on the VPS
|
||||
|
||||
```bash
|
||||
OMNIROUTE_VERSION=$(node -p "require('/home/diegosouzapw/dev/proxys/OmniRoute/package.json').version")
|
||||
ssh root@69.164.221.35 "cp -a /usr/lib/node_modules/omniroute/app /usr/lib/node_modules/omniroute/app.bak-${OMNIROUTE_VERSION}"
|
||||
```
|
||||
|
||||
### 3. Rsync `dist/` → remote `app/`
|
||||
|
||||
The VPS image directory is still named `app/`; we ship the **contents** of our local `dist/`:
|
||||
|
||||
// turbo-all
|
||||
|
||||
```bash
|
||||
scp omniroute-*.tgz root@69.164.221.35:/tmp/
|
||||
rsync -az --delete /home/diegosouzapw/dev/proxys/OmniRoute/dist/ root@69.164.221.35:/usr/lib/node_modules/omniroute/app/
|
||||
```
|
||||
|
||||
### 4. Restart + health check
|
||||
|
||||
```bash
|
||||
ssh root@69.164.221.35 "pm2 restart omniroute --update-env && pm2 save"
|
||||
```
|
||||
|
||||
```bash
|
||||
ssh root@69.164.221.35 "npm install -g /tmp/omniroute-*.tgz --ignore-scripts --legacy-peer-deps && cd /usr/lib/node_modules/omniroute/app && npm rebuild better-sqlite3 && (pm2 delete omniroute 2>/dev/null || true) && pm2 start /root/.omniroute/ecosystem.config.cjs --update-env && pm2 save && echo '✅ Akamai done'"
|
||||
sleep 5 && curl -sf http://69.164.221.35:20128/api/monitoring/health | jq '{status:.status, version:.version}'
|
||||
```
|
||||
|
||||
### 3. Verify the deployment
|
||||
Expected: HTTP 200, `"status": "ok"` (or equivalent health payload).
|
||||
|
||||
### 5. Verify the deployment
|
||||
|
||||
```bash
|
||||
curl -s -o /dev/null -w 'AKAMAI HTTP %{http_code}\n' http://69.164.221.35:20128/
|
||||
```
|
||||
|
||||
## Rollback
|
||||
|
||||
If the health check fails, restore the backup:
|
||||
|
||||
```bash
|
||||
OMNIROUTE_VERSION=$(node -p "require('/home/diegosouzapw/dev/proxys/OmniRoute/package.json').version")
|
||||
ssh root@69.164.221.35 "rm -rf /usr/lib/node_modules/omniroute/app && cp -a /usr/lib/node_modules/omniroute/app.bak-${OMNIROUTE_VERSION} /usr/lib/node_modules/omniroute/app && pm2 restart omniroute --update-env"
|
||||
```
|
||||
|
||||
## Gotchas
|
||||
|
||||
- **Never build in a worktree with symlinked `node_modules`** — the standalone copy step
|
||||
follows symlinks and produces a broken bundle. Use the main checkout or run `npm ci`
|
||||
inside the worktree first.
|
||||
- **`pm2 restart --update-env` does NOT re-read `ecosystem.config.cjs`** — if you changed
|
||||
env vars in the ecosystem file, use `pm2 reload` or `pm2 delete + pm2 start` instead.
|
||||
- **Parallel dev + release builds**: to avoid clobbering the dev server's `.build/next`,
|
||||
pass `NEXT_DIST_DIR=.build/next-release` to `build:release` in CI or concurrent deploys:
|
||||
`NEXT_DIST_DIR=.build/next-release npm run build:release`
|
||||
then adjust the rsync source to `dist-release/` (or whatever `build:release` assembles into).
|
||||
- **`dist/BUILD_SHA` must match the deployed HEAD** — always confirm before rsyncing.
|
||||
A mismatch means the build ran from a dirty or incorrect commit.
|
||||
|
||||
@@ -5,46 +5,115 @@ description: Deploy the latest OmniRoute code to BOTH the Akamai VPS and the Loc
|
||||
|
||||
# Deploy to VPS (Both) Workflow
|
||||
|
||||
Deploy OmniRoute to the production VPSs using `npm pack + scp` + PM2.
|
||||
Deploy OmniRoute to both production VPSs using a clean `build:release` + `rsync` + PM2 restart.
|
||||
|
||||
**Akamai VPS:** `69.164.221.35`
|
||||
**Local VPS:** `192.168.0.15`
|
||||
**Process manager:** PM2 (`omniroute`)
|
||||
**Port:** `20128`
|
||||
**PM2 entry:** `/usr/lib/node_modules/omniroute/app/server.js`
|
||||
**Remote install dir:** `/usr/lib/node_modules/omniroute/app/` (VPS image dir — unchanged)
|
||||
|
||||
> [!IMPORTANT]
|
||||
> The npm registry rejects packages > 100MB, so deployment uses **npm pack + scp**.
|
||||
> **Build must run where `node_modules` is REAL.** A git worktree with symlinked
|
||||
> `node_modules` breaks the Next.js standalone assembly. Always build from the main
|
||||
> checkout (or an `npm ci`'d worktree), never from `OmniRoute-*` worktrees unless
|
||||
> you first ran `npm ci` inside them.
|
||||
|
||||
## Steps
|
||||
|
||||
### 1. Build + pack locally
|
||||
### 1. Clean build + sentinel
|
||||
|
||||
Build **once** locally — the same `dist/` is shipped to both VPSs:
|
||||
|
||||
// turbo
|
||||
|
||||
```bash
|
||||
cd /home/diegosouzapw/dev/proxys/OmniRoute && rm -f omniroute-*.tgz && rm -rf .next/cache app/.next/cache && npm run build:cli && rm -rf app/logs app/coverage app/.git app/.app-build-backup* && npm pack --ignore-scripts
|
||||
cd /home/diegosouzapw/dev/proxys/OmniRoute && npm run build:release
|
||||
```
|
||||
|
||||
### 2. Copy to both VPS and install
|
||||
`build:release` does:
|
||||
1. Deletes `.build/` and `dist/` (clean rebuild — no stale cache)
|
||||
2. Runs `next build` → `.build/next/` (intermediates)
|
||||
3. Assembles the shippable bundle into `dist/` via `assembleStandalone`
|
||||
4. Writes `dist/BUILD_SHA` = `git rev-parse --short HEAD` (deploy sentinel)
|
||||
|
||||
Verify the sentinel before shipping:
|
||||
|
||||
```bash
|
||||
cat /home/diegosouzapw/dev/proxys/OmniRoute/dist/BUILD_SHA && git -C /home/diegosouzapw/dev/proxys/OmniRoute rev-parse --short HEAD
|
||||
```
|
||||
|
||||
Both lines must match. If they differ, the build is stale — re-run `build:release`.
|
||||
|
||||
### 2. Back up the running bundle on both VPSs
|
||||
|
||||
```bash
|
||||
OMNIROUTE_VERSION=$(node -p "require('/home/diegosouzapw/dev/proxys/OmniRoute/package.json').version")
|
||||
ssh root@69.164.221.35 "cp -a /usr/lib/node_modules/omniroute/app /usr/lib/node_modules/omniroute/app.bak-${OMNIROUTE_VERSION}" &
|
||||
ssh root@192.168.0.15 "cp -a /usr/lib/node_modules/omniroute/app /usr/lib/node_modules/omniroute/app.bak-${OMNIROUTE_VERSION}" &
|
||||
wait
|
||||
```
|
||||
|
||||
### 3. Rsync `dist/` → remote `app/` on both VPSs
|
||||
|
||||
The VPS image directory is still named `app/`; we ship the **contents** of our local `dist/`:
|
||||
|
||||
// turbo-all
|
||||
|
||||
```bash
|
||||
scp omniroute-*.tgz root@69.164.221.35:/tmp/ && scp omniroute-*.tgz root@192.168.0.15:/tmp/
|
||||
rsync -az --delete /home/diegosouzapw/dev/proxys/OmniRoute/dist/ root@69.164.221.35:/usr/lib/node_modules/omniroute/app/ && rsync -az --delete /home/diegosouzapw/dev/proxys/OmniRoute/dist/ root@192.168.0.15:/usr/lib/node_modules/omniroute/app/
|
||||
```
|
||||
|
||||
### 4. Restart + health check on both VPSs
|
||||
|
||||
```bash
|
||||
ssh root@69.164.221.35 "pm2 restart omniroute --update-env && pm2 save"
|
||||
```
|
||||
|
||||
```bash
|
||||
ssh root@69.164.221.35 "npm install -g /tmp/omniroute-*.tgz --ignore-scripts --legacy-peer-deps && cd /usr/lib/node_modules/omniroute/app && npm rebuild better-sqlite3 && (pm2 delete omniroute 2>/dev/null || true) && pm2 start /root/.omniroute/ecosystem.config.cjs --update-env && pm2 save && echo '✅ Akamai done'"
|
||||
ssh root@192.168.0.15 "pm2 restart omniroute --update-env && pm2 save"
|
||||
```
|
||||
|
||||
```bash
|
||||
ssh root@192.168.0.15 "npm install -g /tmp/omniroute-*.tgz --ignore-scripts --legacy-peer-deps && cd /usr/lib/node_modules/omniroute/app && npm rebuild better-sqlite3 && (pm2 delete omniroute 2>/dev/null || true) && pm2 start /root/.omniroute/ecosystem.config.cjs --update-env && pm2 save && echo '✅ Local done'"
|
||||
sleep 5
|
||||
curl -sf http://69.164.221.35:20128/api/monitoring/health | jq '{status:.status, version:.version}'
|
||||
curl -sf http://192.168.0.15:20128/api/monitoring/health | jq '{status:.status, version:.version}'
|
||||
```
|
||||
|
||||
### 3. Verify the deployment
|
||||
Expected: HTTP 200, `"status": "ok"` on both.
|
||||
|
||||
### 5. Verify the deployment
|
||||
|
||||
```bash
|
||||
curl -s -o /dev/null -w 'AKAMAI HTTP %{http_code}\n' http://69.164.221.35:20128/
|
||||
curl -s -o /dev/null -w 'LOCAL HTTP %{http_code}\n' http://192.168.0.15:20128/
|
||||
```
|
||||
|
||||
## Rollback
|
||||
|
||||
If either VPS fails its health check, restore from backup (adjust host as needed):
|
||||
|
||||
```bash
|
||||
OMNIROUTE_VERSION=$(node -p "require('/home/diegosouzapw/dev/proxys/OmniRoute/package.json').version")
|
||||
# Akamai rollback:
|
||||
ssh root@69.164.221.35 "rm -rf /usr/lib/node_modules/omniroute/app && cp -a /usr/lib/node_modules/omniroute/app.bak-${OMNIROUTE_VERSION} /usr/lib/node_modules/omniroute/app && pm2 restart omniroute --update-env"
|
||||
# Local rollback:
|
||||
ssh root@192.168.0.15 "rm -rf /usr/lib/node_modules/omniroute/app && cp -a /usr/lib/node_modules/omniroute/app.bak-${OMNIROUTE_VERSION} /usr/lib/node_modules/omniroute/app && pm2 restart omniroute --update-env"
|
||||
```
|
||||
|
||||
## Gotchas
|
||||
|
||||
- **Never build in a worktree with symlinked `node_modules`** — the standalone copy step
|
||||
follows symlinks and produces a broken bundle. Use the main checkout or run `npm ci`
|
||||
inside the worktree first.
|
||||
- **`pm2 restart --update-env` does NOT re-read `ecosystem.config.cjs`** — if you changed
|
||||
env vars in the ecosystem file, use `pm2 reload` or `pm2 delete + pm2 start` instead.
|
||||
- **Parallel dev + release builds**: to avoid clobbering the dev server's `.build/next`,
|
||||
pass `NEXT_DIST_DIR=.build/next-release` to `build:release` in CI or concurrent deploys:
|
||||
`NEXT_DIST_DIR=.build/next-release npm run build:release`
|
||||
then adjust the rsync source to `dist-release/`.
|
||||
- **`dist/BUILD_SHA` must match the deployed HEAD** — always confirm before rsyncing.
|
||||
A mismatch means the build ran from a dirty or incorrect commit.
|
||||
- **Build once, deploy to both** — never run two separate builds for the two VPSs.
|
||||
The same `dist/` artifact (same `BUILD_SHA`) must land on both hosts to keep
|
||||
versions in sync.
|
||||
|
||||
@@ -5,36 +5,99 @@ description: Deploy the latest OmniRoute code to the Local VPS (192.168.0.15)
|
||||
|
||||
# Deploy to Local VPS Workflow
|
||||
|
||||
Deploy OmniRoute to the Local VPS using `npm pack + scp` + PM2.
|
||||
Deploy OmniRoute to the Local VPS using a clean `build:release` + `rsync` + PM2 restart.
|
||||
|
||||
**Local VPS:** `192.168.0.15`
|
||||
**Process manager:** PM2 (`omniroute`)
|
||||
**Port:** `20128`
|
||||
**Remote install dir:** `/usr/lib/node_modules/omniroute/app/` (VPS image dir — unchanged)
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **Build must run where `node_modules` is REAL.** A git worktree with symlinked
|
||||
> `node_modules` breaks the Next.js standalone assembly. Always build from the main
|
||||
> checkout (or an `npm ci`'d worktree), never from `OmniRoute-*` worktrees unless
|
||||
> you first ran `npm ci` inside them.
|
||||
|
||||
## Steps
|
||||
|
||||
### 1. Build + pack locally
|
||||
### 1. Clean build + sentinel
|
||||
|
||||
Run from a checkout with a **real** `node_modules`:
|
||||
|
||||
// turbo
|
||||
|
||||
```bash
|
||||
cd /home/diegosouzapw/dev/proxys/OmniRoute && rm -f omniroute-*.tgz && rm -rf .next/cache app/.next/cache && npm run build:cli && rm -rf app/logs app/coverage app/.git app/.app-build-backup* && npm pack --ignore-scripts
|
||||
cd /home/diegosouzapw/dev/proxys/OmniRoute && npm run build:release
|
||||
```
|
||||
|
||||
### 2. Copy to Local VPS and install
|
||||
`build:release` does:
|
||||
1. Deletes `.build/` and `dist/` (clean rebuild — no stale cache)
|
||||
2. Runs `next build` → `.build/next/` (intermediates)
|
||||
3. Assembles the shippable bundle into `dist/` via `assembleStandalone`
|
||||
4. Writes `dist/BUILD_SHA` = `git rev-parse --short HEAD` (deploy sentinel)
|
||||
|
||||
Verify the sentinel before shipping:
|
||||
|
||||
```bash
|
||||
cat /home/diegosouzapw/dev/proxys/OmniRoute/dist/BUILD_SHA && git -C /home/diegosouzapw/dev/proxys/OmniRoute rev-parse --short HEAD
|
||||
```
|
||||
|
||||
Both lines must match. If they differ, the build is stale — re-run `build:release`.
|
||||
|
||||
### 2. Back up the running bundle on the VPS
|
||||
|
||||
```bash
|
||||
OMNIROUTE_VERSION=$(node -p "require('/home/diegosouzapw/dev/proxys/OmniRoute/package.json').version")
|
||||
ssh root@192.168.0.15 "cp -a /usr/lib/node_modules/omniroute/app /usr/lib/node_modules/omniroute/app.bak-${OMNIROUTE_VERSION}"
|
||||
```
|
||||
|
||||
### 3. Rsync `dist/` → remote `app/`
|
||||
|
||||
The VPS image directory is still named `app/`; we ship the **contents** of our local `dist/`:
|
||||
|
||||
// turbo-all
|
||||
|
||||
```bash
|
||||
scp omniroute-*.tgz root@192.168.0.15:/tmp/
|
||||
rsync -az --delete /home/diegosouzapw/dev/proxys/OmniRoute/dist/ root@192.168.0.15:/usr/lib/node_modules/omniroute/app/
|
||||
```
|
||||
|
||||
### 4. Restart + health check
|
||||
|
||||
```bash
|
||||
ssh root@192.168.0.15 "pm2 restart omniroute --update-env && pm2 save"
|
||||
```
|
||||
|
||||
```bash
|
||||
ssh root@192.168.0.15 "npm install -g /tmp/omniroute-*.tgz --ignore-scripts --legacy-peer-deps && cd /usr/lib/node_modules/omniroute/app && npm rebuild better-sqlite3 && (pm2 delete omniroute 2>/dev/null || true) && pm2 start /root/.omniroute/ecosystem.config.cjs --update-env && pm2 save && echo '✅ Local done'"
|
||||
sleep 5 && curl -sf http://192.168.0.15:20128/api/monitoring/health | jq '{status:.status, version:.version}'
|
||||
```
|
||||
|
||||
### 3. Verify the deployment
|
||||
Expected: HTTP 200, `"status": "ok"` (or equivalent health payload).
|
||||
|
||||
### 5. Verify the deployment
|
||||
|
||||
```bash
|
||||
curl -s -o /dev/null -w 'LOCAL HTTP %{http_code}\n' http://192.168.0.15:20128/
|
||||
```
|
||||
|
||||
## Rollback
|
||||
|
||||
If the health check fails, restore the backup:
|
||||
|
||||
```bash
|
||||
OMNIROUTE_VERSION=$(node -p "require('/home/diegosouzapw/dev/proxys/OmniRoute/package.json').version")
|
||||
ssh root@192.168.0.15 "rm -rf /usr/lib/node_modules/omniroute/app && cp -a /usr/lib/node_modules/omniroute/app.bak-${OMNIROUTE_VERSION} /usr/lib/node_modules/omniroute/app && pm2 restart omniroute --update-env"
|
||||
```
|
||||
|
||||
## Gotchas
|
||||
|
||||
- **Never build in a worktree with symlinked `node_modules`** — the standalone copy step
|
||||
follows symlinks and produces a broken bundle. Use the main checkout or run `npm ci`
|
||||
inside the worktree first.
|
||||
- **`pm2 restart --update-env` does NOT re-read `ecosystem.config.cjs`** — if you changed
|
||||
env vars in the ecosystem file, use `pm2 reload` or `pm2 delete + pm2 start` instead.
|
||||
- **Parallel dev + release builds**: to avoid clobbering the dev server's `.build/next`,
|
||||
pass `NEXT_DIST_DIR=.build/next-release` to `build:release` in CI or concurrent deploys:
|
||||
`NEXT_DIST_DIR=.build/next-release npm run build:release`
|
||||
then adjust the rsync source to `dist-release/` (or whatever `build:release` assembles into).
|
||||
- **`dist/BUILD_SHA` must match the deployed HEAD** — always confirm before rsyncing.
|
||||
A mismatch means the build ran from a dirty or incorrect commit.
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
# Dependencies and build output
|
||||
node_modules
|
||||
.next
|
||||
.build
|
||||
out
|
||||
build
|
||||
dist
|
||||
|
||||
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@@ -159,7 +159,11 @@ jobs:
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- run: npm run check:node-runtime
|
||||
# build:cli runs a clean build into .build/next and assembles dist/
|
||||
# For release builds prefer: npm run build:release (clean rebuild + HEAD sentinel)
|
||||
- run: npm run build:cli
|
||||
- name: Assert dist/server.js exists
|
||||
run: test -f dist/server.js || (echo "dist/server.js missing — build:cli did not assemble correctly" && exit 1)
|
||||
- run: npm run check:pack-artifact
|
||||
|
||||
electron-package-smoke:
|
||||
|
||||
39
AGENTS.md
39
AGENTS.md
@@ -23,19 +23,32 @@ with **MCP Server** (37 tools), **A2A v0.3 Protocol**, and **Electron desktop ap
|
||||
|
||||
## Build, Lint, and Test Commands
|
||||
|
||||
| Command | Description |
|
||||
| ----------------------------------- | --------------------------------- |
|
||||
| `npm run dev` | Start Next.js dev server |
|
||||
| `npm run build` | Production build (isolated) |
|
||||
| `npm run start` | Run production build |
|
||||
| `npm run build:cli` | Build CLI package |
|
||||
| `npm run lint` | ESLint on all source files |
|
||||
| `npm run typecheck:core` | TypeScript core type checking |
|
||||
| `npm run typecheck:noimplicit:core` | Strict checking (no implicit any) |
|
||||
| `npm run check` | Run lint + test |
|
||||
| `npm run check:cycles` | Check for circular dependencies |
|
||||
| `npm run electron:dev` | Run Electron app in dev mode |
|
||||
| `npm run electron:build` | Build Electron app for current OS |
|
||||
| Command | Description |
|
||||
| ----------------------------------- | ---------------------------------------------------------------- |
|
||||
| `npm run dev` | Start Next.js dev server |
|
||||
| `npm run build` | Production build: `next build` → `.build/next/` + assemble `dist/` |
|
||||
| `npm run build:release` | Clean rebuild + HEAD sentinel (`dist/BUILD_SHA`) — use for deploy |
|
||||
| `npm run start` | Run production build |
|
||||
| `npm run build:cli` | Build CLI package |
|
||||
| `npm run lint` | ESLint on all source files |
|
||||
| `npm run typecheck:core` | TypeScript core type checking |
|
||||
| `npm run typecheck:noimplicit:core` | Strict checking (no implicit any) |
|
||||
| `npm run check` | Run lint + test |
|
||||
| `npm run check:cycles` | Check for circular dependencies |
|
||||
| `npm run electron:dev` | Run Electron app in dev mode |
|
||||
| `npm run electron:build` | Build Electron app for current OS |
|
||||
|
||||
**Build output layout:**
|
||||
|
||||
| Directory | Purpose | Gitignored |
|
||||
| --------- | --------------------------------------------------- | ---------- |
|
||||
| `src/` | Application source (TypeScript / TSX) | No |
|
||||
| `.build/` | Build intermediates (`distDir = .build/next`) | Yes |
|
||||
| `dist/` | Shippable bundle assembled by `assembleStandalone` | Yes |
|
||||
|
||||
The pipeline is a single `next build` pass — intermediates land in `.build/next/`, the
|
||||
assembled bundle in `dist/`. VPS deploys rsync `dist/` into the remote
|
||||
`/usr/lib/node_modules/omniroute/app/` directory (VPS image path is unchanged).
|
||||
|
||||
### Running Tests
|
||||
|
||||
|
||||
@@ -35,6 +35,10 @@ _Development cycle in progress — entries are added as work merges into `releas
|
||||
- **resilience:** a per-model subscription/permission `403` from a passthrough provider (e.g. Ollama Cloud `deepseek-v4-pro` → _"this model requires a subscription"_) now locks out **only that model** instead of cooling down the whole connection — the free models on the same key keep serving, and repeated paid-model 403s no longer escalate a connection-wide backoff. Generalizes the grok-web 403 precedent to all `hasPerModelQuota` providers; terminal/credential 403s (banned/deactivated key) still deactivate the connection. ([#3027](https://github.com/diegosouzapw/OmniRoute/issues/3027))
|
||||
- **cache:** preserve client-side `cache_control` breakpoints for Xiaomi MiMo — added `xiaomi-mimo` to the prompt-caching provider allowlist so Claude Code (via cc-switch) cache hints are no longer stripped by the OpenAI-format translator, restoring cache hits. ([#3088](https://github.com/diegosouzapw/OmniRoute/issues/3088))
|
||||
|
||||
### 🔧 Build
|
||||
|
||||
- **build-output-isolation:** unified standalone assembly into one shared `assembleStandalone` module; isolated build output into `.build/` (intermediates, gitignored) and `dist/` (shippable bundle, gitignored), replacing the old repo-root `app/` and `.next/` directories; dropped the duplicate `next build` that prepublish previously ran; added `build:release` script for a clean rebuild with a `dist/BUILD_SHA` HEAD sentinel that guards against deploying stale bundles. **Operators using custom `app/` paths:** the published bundle directory on the VPS image (`/usr/lib/node_modules/omniroute/app/`) is unchanged — only the in-repo build output path moved. Update any local scripts that reference the repo-local `app/` build output to `dist/` instead.
|
||||
|
||||
### 📦 Dependencies
|
||||
|
||||
- **electron:** bump to 42.3.2 (crash fix desktopCapturer, Chromium 148.0.7778.218, ThinLTO perf) (#3083)
|
||||
|
||||
@@ -59,13 +59,40 @@ These settings are stored in the database and persist across restarts, overridin
|
||||
npm run dev
|
||||
|
||||
# Production build
|
||||
npm run build
|
||||
npm run build # next build → .build/next/ then assembleStandalone → dist/
|
||||
npm run start
|
||||
|
||||
# Release build (clean rebuild + HEAD sentinel — required for deploy)
|
||||
npm run build:release # rm -rf .build dist && build + writes dist/BUILD_SHA
|
||||
|
||||
# Common port configuration
|
||||
PORT=20128 NEXT_PUBLIC_BASE_URL=http://localhost:20128 npm run dev
|
||||
```
|
||||
|
||||
### Build Output Layout
|
||||
|
||||
| Directory | Contents | Tracked |
|
||||
| ---------- | ------------------------------------------- | ------- |
|
||||
| `src/` | Application source (TypeScript / TSX) | Yes |
|
||||
| `.build/` | Intermediates — `next build` output (gitignored, `distDir = .build/next`) | No |
|
||||
| `dist/` | Shippable bundle — assembled by `assembleStandalone` (gitignored) | No |
|
||||
|
||||
The build pipeline is a single pass:
|
||||
|
||||
```
|
||||
npm run build
|
||||
└─ next build → .build/next/standalone (Next.js output)
|
||||
└─ assembleStandalone() (copies standalone + static + public + native assets)
|
||||
└─ output: dist/ (server.js, .next/static/, public/, node_modules/)
|
||||
```
|
||||
|
||||
`npm run build:release` additionally cleans both directories first and writes
|
||||
`dist/BUILD_SHA` (= `git rev-parse --short HEAD`) as a deploy integrity sentinel.
|
||||
|
||||
> **VPS deploy note:** the remote image directory `/usr/lib/node_modules/omniroute/app/`
|
||||
> is unchanged. The deploy skills rsync the contents of `dist/` into it.
|
||||
> Only the in-repo build output path moved (`app/` → `dist/`).
|
||||
|
||||
Default URLs:
|
||||
|
||||
- **Dashboard**: `http://localhost:20128/dashboard`
|
||||
@@ -314,6 +341,10 @@ Write unit tests in `tests/unit/` covering at minimum:
|
||||
|
||||
Releases are managed via the `/generate-release` workflow. When a new GitHub Release is created, the package is **automatically published to npm** via GitHub Actions.
|
||||
|
||||
For VPS deploys, use `npm run build:release` (not `npm run build`) — it performs a clean
|
||||
rebuild, assembles the bundle into `dist/`, and writes the `dist/BUILD_SHA` sentinel.
|
||||
Then use the `/deploy-vps-*-cc` skills which rsync `dist/` to the remote `app/` directory.
|
||||
|
||||
---
|
||||
|
||||
## Getting Help
|
||||
|
||||
@@ -42,7 +42,7 @@ RUN --mount=type=cache,target=/root/.npm \
|
||||
ENV OMNIROUTE_USE_TURBOPACK=1
|
||||
|
||||
COPY . ./
|
||||
RUN --mount=type=cache,target=/app/.next/cache \
|
||||
RUN --mount=type=cache,target=/app/.build/next/cache \
|
||||
mkdir -p /app/data && npm run build
|
||||
|
||||
# ── Runner base ────────────────────────────────────────────────────────────
|
||||
@@ -65,9 +65,9 @@ ENV DATA_DIR=/app/data
|
||||
RUN mkdir -p /app/data
|
||||
|
||||
# The standalone build + syncStandaloneExtraModules bundles all runtime files
|
||||
# (.next, node_modules, migrations, scripts, docs, etc.) into .next/standalone/.
|
||||
# (.next, node_modules, migrations, scripts, docs, etc.) into .build/next/standalone/.
|
||||
# Explicit overrides below cover modules that NFT tracing may miss.
|
||||
COPY --from=builder /app/.next/standalone ./
|
||||
COPY --from=builder /app/.build/next/standalone ./
|
||||
# Explicitly copy @swc/helpers — not always traced by standalone output but needed at runtime
|
||||
COPY --from=builder /app/node_modules/@swc/helpers ./node_modules/@swc/helpers
|
||||
# Explicitly copy better-sqlite3 — native bindings are not reliably traced by
|
||||
|
||||
@@ -147,11 +147,38 @@ If `electron/` changed:
|
||||
- [ ] `electron/package.json` version matches root `package.json`
|
||||
- [ ] Auto-update channel pointer updated if releasing to `stable`
|
||||
|
||||
### Build Layout
|
||||
|
||||
The repository uses three distinct output directories — never mix them up:
|
||||
|
||||
| Directory | Purpose | Tracked? |
|
||||
| ------------- | ------------------------------------------------------------- | -------- |
|
||||
| `src/` | Application source (TypeScript / TSX) | Yes |
|
||||
| `.build/` | Build intermediates — `next build` output (`distDir`) | No (gitignored) |
|
||||
| `dist/` | Shippable npm bundle — assembled by `assembleStandalone` | No (gitignored) |
|
||||
|
||||
> **Operator note:** the remote VPS image directory remains `/usr/lib/node_modules/omniroute/app/`.
|
||||
> Only the **in-repo** build output moved (`app/` → `dist/`). The deploy skills rsync
|
||||
> `dist/` contents into the remote `app/` dir — no VPS path changes required.
|
||||
|
||||
**Single-build flow:**
|
||||
|
||||
```
|
||||
npm run build:release
|
||||
└─ rm -rf .build dist (clean)
|
||||
└─ next build → .build/next/ (intermediates)
|
||||
└─ assembleStandalone (copies standalone + static + public + natives → dist/)
|
||||
└─ writes dist/BUILD_SHA (HEAD sentinel)
|
||||
```
|
||||
|
||||
Do NOT run `npm run build` followed by a separate `npm run build:cli` for deploy — use
|
||||
`npm run build:release` which does a clean rebuild + sentinel in one command.
|
||||
|
||||
### Artifact Validation
|
||||
|
||||
- [ ] `npm run build:cli` succeeds
|
||||
- [ ] `npm run build:release` succeeds and `dist/BUILD_SHA` == `git rev-parse --short HEAD`
|
||||
- [ ] `npm run check:pack-artifact` clean — no `app.__qa_backup`, `scripts/scratch`, `package-lock.json`, or other local residue
|
||||
- [ ] `npm run build` produces a working standalone Next.js bundle
|
||||
- [ ] `dist/server.js` exists after build
|
||||
|
||||
### Tagging & Release
|
||||
|
||||
@@ -169,10 +196,14 @@ If `electron/` changed:
|
||||
|
||||
### Deploy
|
||||
|
||||
Deploy skills use the light rsync flow — no `npm pack`, no `npm i -g`:
|
||||
|
||||
- [ ] Use deploy skill that matches target:
|
||||
- `/deploy-vps-local-cc` — local VPS (192.168.0.15)
|
||||
- `/deploy-vps-akamai-cc` — Akamai VPS (69.164.221.35)
|
||||
- `/deploy-vps-both-cc` — both
|
||||
- [ ] Before deploying, confirm `dist/BUILD_SHA` == `git rev-parse --short HEAD`
|
||||
- [ ] Build must run where `node_modules` is real (main checkout or `npm ci`'d worktree — NOT a symlinked worktree)
|
||||
- [ ] Smoke test deployed instance:
|
||||
- Open `/dashboard/health` → check version string matches release
|
||||
- Run a `/v1/chat/completions` request against a known provider
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
],
|
||||
"extraResources": [
|
||||
{
|
||||
"from": "../.next/electron-standalone",
|
||||
"from": "../.build/electron-standalone",
|
||||
"to": "app",
|
||||
"filter": [
|
||||
"**/*",
|
||||
@@ -65,7 +65,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"from": "../.next/electron-standalone/node_modules",
|
||||
"from": "../.build/electron-standalone/node_modules",
|
||||
"to": "app/node_modules",
|
||||
"filter": [
|
||||
"**/*"
|
||||
|
||||
@@ -38,11 +38,13 @@ const eslintConfig = [
|
||||
// Global ignores — keep ESLint scoped to source files only
|
||||
{
|
||||
ignores: [
|
||||
// Next.js build output
|
||||
// Next.js build output (distDir now .build/next; keep .next for legacy)
|
||||
".next/**",
|
||||
".build/**",
|
||||
"src/.next/**",
|
||||
"out/**",
|
||||
"build/**",
|
||||
"dist/**",
|
||||
"coverage/**",
|
||||
"next-env.d.ts",
|
||||
// Scripts and binaries
|
||||
@@ -65,12 +67,9 @@ const eslintConfig = [
|
||||
// Playwright test output
|
||||
"playwright-report/**",
|
||||
"test-results/**",
|
||||
// Subdirectory .next build output (app/ subdir)
|
||||
// Legacy app/ and QA backup dirs (renamed to dist/ in Layer 1)
|
||||
"app/**",
|
||||
"app/.next/**",
|
||||
"app/bin/**",
|
||||
"app.__qa_backup/**",
|
||||
"app/app.__qa_backup/**",
|
||||
// CLI package copy directory
|
||||
"clipr/**",
|
||||
],
|
||||
|
||||
@@ -9,10 +9,10 @@ const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
const ROOT = join(__dirname, "..", "..");
|
||||
|
||||
const NEXT_DIST_DIR = process.env.NEXT_DIST_DIR || ".next";
|
||||
const NEXT_DIST_DIR = process.env.NEXT_DIST_DIR || ".build/next";
|
||||
const DIST_DIR = join(ROOT, NEXT_DIST_DIR);
|
||||
const STANDALONE_DIR = join(DIST_DIR, "standalone");
|
||||
const ELECTRON_STANDALONE_DIR = join(ROOT, NEXT_DIST_DIR, "electron-standalone");
|
||||
const ELECTRON_STANDALONE_DIR = join(ROOT, ".build", "electron-standalone");
|
||||
|
||||
// --- Electron-UNIQUE: resolve the nested server.js location ----------------
|
||||
|
||||
|
||||
Reference in New Issue
Block a user