refactor(scripts): organize into build/dev/check/docs/i18n/ad-hoc subfolders

Reorganizes the 29 active scripts under scripts/ into purpose-driven
subfolders:

- scripts/build/    (11) — Build, install, publish, runtime env
- scripts/dev/      (13) — Dev servers, test runners, healthchecks
- scripts/check/    (10) — Lint/validation/coverage checks
- scripts/docs/      (2) — Docs index and provider reference generation
- scripts/i18n/     (+3) — Adds Python translation utilities (check/validate/autotranslate)
- scripts/ad-hoc/    (4) — One-shot maintenance utilities

Updates all references in package.json, electron/package.json,
.husky/pre-commit, .github/workflows/ci.yml, Dockerfile, src/,
tests/, scripts/ internal cross-imports, playwright.config.ts,
and English docs (CODEBASE_DOCUMENTATION, ENVIRONMENT, FEATURES,
RELEASE_CHECKLIST, COVERAGE_PLAN, ELECTRON_GUIDE, I18N, GEMINI).

Also patches scripts/build/pack-artifact-policy.ts so the npm pack
allowlist mirrors the new layout.

Validates with:
- npm run lint            (exit 0 — pre-existing minified-bundle errors only)
- npm run typecheck:core  (exit 0)
- npm run check:docs-all  (exit 0)
- unit tests for moved scripts (57 tests pass)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
diegosouzapw
2026-05-13 10:14:25 -03:00
parent ca6916a867
commit f3b944a55a
78 changed files with 225 additions and 213 deletions

View File

@@ -71,7 +71,7 @@ jobs:
- name: Validate ${{ matrix.lang }}
run: |
python3 scripts/validate_translation.py quick -l '${{ matrix.lang }}' > result.txt
python3 scripts/i18n/validate_translation.py quick -l '${{ matrix.lang }}' > result.txt
- name: Upload result
if: always()
@@ -94,7 +94,7 @@ jobs:
- name: Fetch base branch
run: git fetch --no-tags origin "${GITHUB_BASE_REF}" --depth=1
- name: Validate source changes include tests
run: node scripts/check-pr-test-policy.mjs --summary-file .artifacts/pr-test-policy.md
run: node scripts/check/check-pr-test-policy.mjs --summary-file .artifacts/pr-test-policy.md
- name: Publish PR test policy summary
if: always()
run: |
@@ -255,7 +255,7 @@ jobs:
run: |
mkdir -p coverage
if [ -f coverage/coverage-summary.json ]; then
node scripts/test-report-summary.mjs \
node scripts/check/test-report-summary.mjs \
--input coverage/coverage-summary.json \
--output coverage/coverage-report.md \
--threshold 60

View File

@@ -6,5 +6,5 @@ if ! command -v npx >/dev/null 2>&1; then
fi
npx lint-staged
node scripts/check-docs-sync.mjs
node scripts/check/check-docs-sync.mjs
npm run check:any-budget:t11

View File

@@ -6,9 +6,9 @@ RUN apt-get update \
&& rm -rf /var/lib/apt/lists/*
COPY package*.json ./
COPY scripts/postinstall.mjs ./scripts/postinstall.mjs
COPY scripts/postinstallSupport.mjs ./scripts/postinstallSupport.mjs
COPY scripts/native-binary-compat.mjs ./scripts/native-binary-compat.mjs
COPY scripts/build/postinstall.mjs ./scripts/build/postinstall.mjs
COPY scripts/build/postinstallSupport.mjs ./scripts/build/postinstallSupport.mjs
COPY scripts/build/native-binary-compat.mjs ./scripts/build/native-binary-compat.mjs
ENV NPM_CONFIG_LEGACY_PEER_DEPS=true
RUN if [ -f package-lock.json ]; then \
npm ci --no-audit --no-fund --legacy-peer-deps; \
@@ -60,17 +60,17 @@ COPY --from=builder /app/src/mitm/server.cjs ./src/mitm/server.cjs
# Next.js standalone tracing does not include them.
COPY --from=builder /app/docs ./docs
COPY --from=builder /app/scripts/run-standalone.mjs ./run-standalone.mjs
COPY --from=builder /app/scripts/runtime-env.mjs ./runtime-env.mjs
COPY --from=builder /app/scripts/bootstrap-env.mjs ./bootstrap-env.mjs
COPY --from=builder /app/scripts/healthcheck.mjs ./healthcheck.mjs
COPY --from=builder /app/scripts/dev/run-standalone.mjs ./dev/run-standalone.mjs
COPY --from=builder /app/scripts/build/runtime-env.mjs ./build/runtime-env.mjs
COPY --from=builder /app/scripts/build/bootstrap-env.mjs ./build/bootstrap-env.mjs
COPY --from=builder /app/scripts/dev/healthcheck.mjs ./healthcheck.mjs
EXPOSE 20128
HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \
CMD ["node", "healthcheck.mjs"]
CMD ["node", "run-standalone.mjs"]
CMD ["node", "dev/run-standalone.mjs"]
FROM runner-base AS runner-cli

View File

@@ -5,7 +5,7 @@
## 1. File Placement & Organization
- **Test Files**: ALL unit tests, integration tests, ecosystem tests, or Vitest files MUST strictly be placed within the `tests/` directory (e.g., `tests/unit/`, `tests/integration/`). NEVER create test files in the project root (`/`).
- **Scripts and Utilities**: ALL maintenance, debugging, generation, or experimental scripts (`.cjs`, `.mjs`, `.js`, `.ts`) MUST be placed strictly inside the `scripts/` directory or `scripts/scratch/` for temporary one-offs. NEVER dump loose scripts in the project root (`/`).
- **Scripts and Utilities**: ALL maintenance, debugging, generation, or experimental scripts (`.cjs`, `.mjs`, `.js`, `.ts`) MUST be placed strictly inside one of the `scripts/` subfolders (`build/`, `dev/`, `check/`, `docs/`, `i18n/`, `ad-hoc/`). One-shot or experimental code goes under `scripts/ad-hoc/`. NEVER dump loose scripts in the project root (`/`) or the top-level `scripts/` folder.
**The Project Root MUST ONLY CONTAIN:**
@@ -14,7 +14,7 @@
- Documentation files (`README.md`, `CHANGELOG.md`, `LICENSE`, `AGENTS.md`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`, `llm.txt`, `Tuto_Qdrant.md`)
- CI/CD files and ignore definitions (`.gitignore`, `.dockerignore`, `.npmignore`, `.npmrc`, `.node-version`, `.nvmrc`, `.env.example`)
When creating _any_ validation tests or one-off logic scripts, default to using `scripts/scratch/` or the `tests/unit/` directories according to your goals. Do not pollute the `/` root context.
When creating _any_ validation tests or one-off logic scripts, default to using `scripts/ad-hoc/` or the `tests/unit/` directories according to your goals. Do not pollute the `/` root context.
## 2. Hard Rules (mirror of `CLAUDE.md`)

View File

@@ -25,7 +25,7 @@ without inventing new modules.
| Database | **SQLite** via `better-sqlite3` (singleton, WAL journaling) |
| Desktop | **Electron 41** + `electron-builder` 26.10 (separate workspace at `electron/`) |
| Tests | **Node native test runner** (unit/integration), **Vitest** (MCP, autoCombo, cache), **Playwright** (e2e + protocols-e2e) |
| Build | Next.js standalone via `scripts/build-next-isolated.mjs` |
| Build | Next.js standalone via `scripts/build/build-next-isolated.mjs` |
| Lint/format | ESLint flat config + Prettier (`lint-staged` via Husky pre-commit) |
| Module system | ESM everywhere (`"type": "module"`) |
| Workspaces | npm workspace — `open-sse` is the only sub-workspace |
@@ -613,26 +613,31 @@ Common commands:
## 8. `scripts/`
42 scripts. Highlights:
Organized into 6 subfolders by purpose.
- **Build / runtime**: `build-next-isolated.mjs`, `prepare-electron-standalone.mjs`,
`run-next.mjs`, `run-next-playwright.mjs`, `run-standalone.mjs`,
`standalone-server-ws.mjs`, `responses-ws-proxy.mjs`, `v1-ws-bridge.mjs`,
`runtime-env.mjs`, `bootstrap-env.mjs`, `smoke-electron-packaged.mjs`.
- **Checks**: `check-cycles.mjs`, `check-docs-sync.mjs`,
`check-route-validation.mjs`, `check-t11-any-budget.mjs`,
`check-pr-test-policy.mjs`, `check-supported-node-runtime.ts`,
- **`scripts/build/`** — `build-next-isolated.mjs`, `prepublish.ts`,
`prepare-electron-standalone.mjs`, `pack-artifact-policy.ts`,
`validate-pack-artifact.ts`, `postinstall.mjs`, `postinstallSupport.mjs`,
`uninstall.mjs`, `bootstrap-env.mjs`, `runtime-env.mjs`,
`native-binary-compat.mjs`.
- **Generators / sync**: `generate-docs-index.mjs`, `sync-env.mjs`,
`sync-cursor-models.mjs`, `migrate-env.mjs`.
- **Install / publish**: `postinstall.mjs`, `postinstallSupport.mjs`,
`prepublish.ts`, `pack-artifact-policy.ts`, `validate-pack-artifact.ts`,
`uninstall.mjs`.
- **Test runners**: `run-playwright-tests.mjs`, `run-ecosystem-tests.mjs`,
`run-protocol-clients-tests.mjs`, `test-report-summary.mjs`.
- **Misc**: `healthcheck.mjs`, `dbsetup.js`, `system-info.mjs`,
`cursor-tap.cjs`, `scratch.mjs`, `i18n_autotranslate.py`,
`check_translations.py`, `validate_translation.py`.
- **`scripts/dev/`** — `run-next.mjs`, `run-next-playwright.mjs`,
`run-standalone.mjs`, `standalone-server-ws.mjs`, `responses-ws-proxy.mjs`,
`v1-ws-bridge.mjs`, `smoke-electron-packaged.mjs`,
`run-playwright-tests.mjs`, `run-ecosystem-tests.mjs`,
`run-protocol-clients-tests.mjs`, `sync-env.mjs`, `healthcheck.mjs`,
`system-info.mjs`.
- **`scripts/check/`** — `check-cycles.mjs`, `check-docs-sync.mjs`,
`check-docs-counts-sync.mjs`, `check-env-doc-sync.mjs`,
`check-deprecated-versions.mjs`, `check-route-validation.mjs`,
`check-t11-any-budget.mjs`, `check-pr-test-policy.mjs`,
`check-supported-node-runtime.ts`, `test-report-summary.mjs`.
- **`scripts/docs/`** — `generate-docs-index.mjs`, `gen-provider-reference.ts`.
- **`scripts/i18n/`** — `generate-multilang.mjs`, `run-visual-qa.mjs`,
`generate-qa-checklist.mjs`, `apply-priority-overrides.mjs`,
`validate_translation.py`, `check_translations.py`, `i18n_autotranslate.py`,
`untranslatable-keys.json`.
- **`scripts/ad-hoc/`** — `cursor-tap.cjs`, `sync-cursor-models.mjs`,
`migrate-env.mjs`, `dbsetup.js`.
---

View File

@@ -156,7 +156,7 @@ Update `npm run test:coverage` thresholds only after the project actually exceed
For ad-hoc threshold checks against the latest report use:
```bash
node scripts/test-report-summary.mjs --threshold 75
node scripts/check/test-report-summary.mjs --threshold 75
```
Recommended ratchet sequence (order is `statements-lines / branches / functions`):

View File

@@ -56,7 +56,7 @@ Confirmed from `electron/package.json`:
The `electron/` workspace also exposes:
- `npm run prepare:bundle` — runs `scripts/prepare-electron-standalone.mjs`
- `npm run prepare:bundle` — runs `scripts/build/prepare-electron-standalone.mjs`
- `npm run build:mac-x64` / `build:mac-arm64` — single-arch macOS builds
- `npm run pack` — directory-only build for local testing (no installer)
@@ -73,8 +73,10 @@ electron/
└── dist-electron/ # electron-builder output (gitignored)
scripts/
├── prepare-electron-standalone.mjs # Stages .next/electron-standalone bundle
└── smoke-electron-packaged.mjs # Post-build smoke test
├── build/
│ └── prepare-electron-standalone.mjs # Stages .next/electron-standalone bundle
└── dev/
└── smoke-electron-packaged.mjs # Post-build smoke test
```
Both `main.js` and `preload.js` are **CommonJS `.js` files**, not TypeScript. The
@@ -202,7 +204,7 @@ NSIS settings: `oneClick: false`, lets the user choose the install directory, cr
npm run electron:smoke:packaged
```
`scripts/smoke-electron-packaged.mjs`:
`scripts/dev/smoke-electron-packaged.mjs`:
- Auto-discovers the packaged binary in `electron/dist-electron/` for the current platform.
- Launches with isolated `HOME`/`APPDATA`/`XDG_*` directories so it doesn't touch developer data.
@@ -266,4 +268,4 @@ Releases are published to GitHub Releases (`diegosouzapw/OmniRoute`), which is a
- [SETUP_GUIDE.md](./SETUP_GUIDE.md)
- [RELEASE_CHECKLIST.md](./RELEASE_CHECKLIST.md)
- Source: `electron/main.js`, `electron/preload.js`, `electron/package.json`
- Helpers: `scripts/prepare-electron-standalone.mjs`, `scripts/smoke-electron-packaged.mjs`
- Helpers: `scripts/build/prepare-electron-standalone.mjs`, `scripts/dev/smoke-electron-packaged.mjs`

View File

@@ -66,19 +66,19 @@ echo "OMNIROUTE_WS_BRIDGE_SECRET=$(openssl rand -base64 32)"
OmniRoute uses **SQLite** (via `better-sqlite3`) for all persistence. These variables control data location, encryption, and lifecycle.
| Variable | Default | Source File | Description |
| -------------------------------------- | -------------------- | ----------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `DATA_DIR` | `~/.omniroute/` | `src/lib/db/core.ts` | Root directory for SQLite DB, backups, and data files. Override for Docker volumes or custom paths. |
| `STORAGE_ENCRYPTION_KEY` | _(empty = disabled)_ | `src/lib/db/encryption.ts` | AES key for full SQLite database encryption at rest. Generate with `openssl rand -hex 32`. |
| `STORAGE_ENCRYPTION_KEY_VERSION` | `v1` | `scripts/bootstrap-env.mjs`, `electron/main.js` | Version label for the encryption key. Increment when performing key rotation to support decryption of old backups. |
| `DISABLE_SQLITE_AUTO_BACKUP` | `false` | `src/lib/db/backup.ts` | When `true`, skips the automatic database backup that runs before migrations on every startup. |
| `OMNIROUTE_CRYPT_KEY` | _(unset)_ | `src/lib/db/encryption.ts` | **Legacy alias** for `STORAGE_ENCRYPTION_KEY`. Accepted as a fallback when the primary variable is absent. |
| `OMNIROUTE_API_KEY_BASE64` | _(unset)_ | `src/lib/db/encryption.ts` | **Legacy alias** (Base64-encoded form) accepted as a fallback. Decoded automatically before use. |
| `OMNIROUTE_DB_HEALTHCHECK_INTERVAL_MS` | _(unset)_ | `src/lib/db/core.ts` | Override the periodic SQLite healthcheck interval (ms). When unset, defaults are derived from `NODE_ENV`. |
| `OMNIROUTE_FORCE_DB_HEALTHCHECK` | `0` | `src/lib/db/core.ts` | Set to `1` to force the DB healthcheck loop on, even when it would normally be skipped (e.g., short-lived tasks). |
| `OMNIROUTE_MIGRATIONS_DIR` | _(auto-detect)_ | `src/lib/db/migrationRunner.ts` | Override the directory that the migration runner scans. Useful when shipping bundled migrations in custom builds. |
| `OMNIROUTE_SPEND_FLUSH_INTERVAL_MS` | _(default in code)_ | `src/lib/spend/batchWriter.ts` | Flush interval (ms) for the batched spend/cost writer. Lower values reduce write coalescing; higher values reduce DB contention. |
| `OMNIROUTE_SPEND_MAX_BUFFER_SIZE` | _(default in code)_ | `src/lib/spend/batchWriter.ts` | Max buffered spend entries before a forced flush. Raise on high-QPS deployments; lower when bounded memory matters more. |
| Variable | Default | Source File | Description |
| -------------------------------------- | -------------------- | ----------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `DATA_DIR` | `~/.omniroute/` | `src/lib/db/core.ts` | Root directory for SQLite DB, backups, and data files. Override for Docker volumes or custom paths. |
| `STORAGE_ENCRYPTION_KEY` | _(empty = disabled)_ | `src/lib/db/encryption.ts` | AES key for full SQLite database encryption at rest. Generate with `openssl rand -hex 32`. |
| `STORAGE_ENCRYPTION_KEY_VERSION` | `v1` | `scripts/build/bootstrap-env.mjs`, `electron/main.js` | Version label for the encryption key. Increment when performing key rotation to support decryption of old backups. |
| `DISABLE_SQLITE_AUTO_BACKUP` | `false` | `src/lib/db/backup.ts` | When `true`, skips the automatic database backup that runs before migrations on every startup. |
| `OMNIROUTE_CRYPT_KEY` | _(unset)_ | `src/lib/db/encryption.ts` | **Legacy alias** for `STORAGE_ENCRYPTION_KEY`. Accepted as a fallback when the primary variable is absent. |
| `OMNIROUTE_API_KEY_BASE64` | _(unset)_ | `src/lib/db/encryption.ts` | **Legacy alias** (Base64-encoded form) accepted as a fallback. Decoded automatically before use. |
| `OMNIROUTE_DB_HEALTHCHECK_INTERVAL_MS` | _(unset)_ | `src/lib/db/core.ts` | Override the periodic SQLite healthcheck interval (ms). When unset, defaults are derived from `NODE_ENV`. |
| `OMNIROUTE_FORCE_DB_HEALTHCHECK` | `0` | `src/lib/db/core.ts` | Set to `1` to force the DB healthcheck loop on, even when it would normally be skipped (e.g., short-lived tasks). |
| `OMNIROUTE_MIGRATIONS_DIR` | _(auto-detect)_ | `src/lib/db/migrationRunner.ts` | Override the directory that the migration runner scans. Useful when shipping bundled migrations in custom builds. |
| `OMNIROUTE_SPEND_FLUSH_INTERVAL_MS` | _(default in code)_ | `src/lib/spend/batchWriter.ts` | Flush interval (ms) for the batched spend/cost writer. Lower values reduce write coalescing; higher values reduce DB contention. |
| `OMNIROUTE_SPEND_MAX_BUFFER_SIZE` | _(default in code)_ | `src/lib/spend/batchWriter.ts` | Max buffered spend entries before a forced flush. Raise on high-QPS deployments; lower when bounded memory matters more. |
### Scenarios

View File

@@ -269,7 +269,7 @@ Key features:
## 🌐 V1 WebSocket Bridge _(v3.6.6+)_
OmniRoute now supports **OpenAI-compatible WebSocket clients** via the `/v1/ws` upgrade endpoint. The custom `scripts/v1-ws-bridge.mjs` server wraps Next.js and upgrades WS connections to full bidirectional streaming sessions. Authentication uses the same API key or session cookie as HTTP requests.
OmniRoute now supports **OpenAI-compatible WebSocket clients** via the `/v1/ws` upgrade endpoint. The custom `scripts/dev/v1-ws-bridge.mjs` server wraps Next.js and upgrades WS connections to full bidirectional streaming sessions. Authentication uses the same API key or session cookie as HTTP requests.
Key behaviours:

View File

@@ -6,14 +6,14 @@ OmniRoute supports **30 languages** with full dashboard UI translation, translat
## Quick Reference
| Task | Command |
| ---------------------- | --------------------------------------------------------------------------------------- |
| Generate translations | `node scripts/i18n/generate-multilang.mjs messages` |
| Translate docs (LLM) | `python3 scripts/i18n_autotranslate.py --api-url <url> --api-key <key> --model <model>` |
| Validate a locale | `python3 scripts/validate_translation.py quick -l cs` |
| Check code keys | `python3 scripts/check_translations.py` |
| Generate QA report | `node scripts/i18n/generate-qa-checklist.mjs` |
| Visual QA (Playwright) | `node scripts/i18n/run-visual-qa.mjs` |
| Task | Command |
| ---------------------- | -------------------------------------------------------------------------------------------- |
| Generate translations | `node scripts/i18n/generate-multilang.mjs messages` |
| Translate docs (LLM) | `python3 scripts/i18n/i18n_autotranslate.py --api-url <url> --api-key <key> --model <model>` |
| Validate a locale | `python3 scripts/i18n/validate_translation.py quick -l cs` |
| Check code keys | `python3 scripts/i18n/check_translations.py` |
| Generate QA report | `node scripts/i18n/generate-qa-checklist.mjs` |
| Visual QA (Playwright) | `node scripts/i18n/run-visual-qa.mjs` |
## Architecture
@@ -115,8 +115,8 @@ Auto-translations are a starting point. Review manually for:
### 5. Validate
```bash
python3 scripts/validate_translation.py quick -l xx
python3 scripts/validate_translation.py diff common -l xx
python3 scripts/i18n/validate_translation.py quick -l xx
python3 scripts/i18n/validate_translation.py diff common -l xx
```
### 6. Generate Translated Documentation
@@ -162,7 +162,7 @@ node scripts/i18n/generate-multilang.mjs [messages|readme|docs|all]
**Secondary translator** — uses any OpenAI-compatible LLM API (including OmniRoute itself) to translate existing `docs/i18n/` markdown files. Best for polishing or re-translating docs with better quality than Google Translate.
```bash
python3 scripts/i18n_autotranslate.py \
python3 scripts/i18n/i18n_autotranslate.py \
--api-url http://localhost:20128/v1 \
--api-key sk-your-key \
--model gpt-4o
@@ -183,24 +183,24 @@ python3 scripts/i18n_autotranslate.py \
```bash
# Quick check (counts only)
python3 scripts/validate_translation.py quick -l cs
python3 scripts/i18n/validate_translation.py quick -l cs
# Output:
# Missing: 0
# Untranslated: 0
# Ignored (UNTRANSLATABLE_KEYS): 236
# Detailed diff by category
python3 scripts/validate_translation.py diff common -l cs
python3 scripts/validate_translation.py diff settings -l cs
python3 scripts/i18n/validate_translation.py diff common -l cs
python3 scripts/i18n/validate_translation.py diff settings -l cs
# Export to CSV
python3 scripts/validate_translation.py csv -l cs > report.csv
python3 scripts/i18n/validate_translation.py csv -l cs > report.csv
# Export to Markdown
python3 scripts/validate_translation.py md -l cs > report.md
python3 scripts/i18n/validate_translation.py md -l cs > report.md
# Full report (default)
python3 scripts/validate_translation.py -l cs
python3 scripts/i18n/validate_translation.py -l cs
```
**Detects:**
@@ -226,13 +226,13 @@ python3 scripts/validate_translation.py -l cs
```bash
# Basic check
python3 scripts/check_translations.py
python3 scripts/i18n/check_translations.py
# Verbose output
python3 scripts/check_translations.py --verbose
python3 scripts/i18n/check_translations.py --verbose
# Auto-fix (adds missing keys to en.json)
python3 scripts/check_translations.py --fix
python3 scripts/i18n/check_translations.py --fix
```
### generate-qa-checklist.mjs
@@ -322,7 +322,7 @@ The CI pipeline validates all locales on every push and PR:
LANGS=$(ls src/i18n/messages/*.json | xargs -n1 basename | sed 's/.json$//' | grep -v '^en$')
# i18n: validates each language
python3 scripts/validate_translation.py quick -l '${{ matrix.lang }}'
python3 scripts/i18n/validate_translation.py quick -l '${{ matrix.lang }}'
```
**Dashboard output:**
@@ -384,7 +384,7 @@ docs/
1. **Always edit `en.json` first** — it's the source of truth
2. **Run `generate-multilang.mjs messages`** to propagate new keys to all locales
3. **Review auto-translations** — Google Translate is a starting point, not final
4. **Validate before committing**`python3 scripts/validate_translation.py quick -l <lang>`
4. **Validate before committing**`python3 scripts/i18n/validate_translation.py quick -l <lang>`
5. **Update `untranslatable-keys.json`** if a key should remain in English
### Placeholder Safety
@@ -401,7 +401,7 @@ const t = useTranslations("settings");
t("cacheSettings"); // maps to settings.cacheSettings in JSON
// Run check_translations.py to verify keys exist
python3 scripts/check_translations.py --verbose
python3 scripts/i18n/check_translations.py --verbose
```
### RTL Considerations

View File

@@ -72,7 +72,7 @@ npm run test:e2e # optional but recommended
Husky hooks live in `.husky/` and run automatically on git operations.
- **pre-commit:** `npx lint-staged + node scripts/check-docs-sync.mjs + npm run check:any-budget:t11`
- **pre-commit:** `npx lint-staged + node scripts/check/check-docs-sync.mjs + npm run check:any-budget:t11`
- **pre-push:** currently disabled (commented out). When re-enabled, runs `npm run test:unit`.
- Run `npm run test:unit` manually before pushing release branches.

View File

@@ -12,7 +12,7 @@
"scripts": {
"start": "electron .",
"dev": "electron . --no-sandbox",
"prepare:bundle": "node ../scripts/prepare-electron-standalone.mjs",
"prepare:bundle": "node ../scripts/build/prepare-electron-standalone.mjs",
"build": "npm run prepare:bundle && electron-builder",
"build:win": "npm run prepare:bundle && electron-builder --win",
"build:mac": "npm run prepare:bundle && electron-builder --mac",
@@ -26,7 +26,7 @@
"electron-updater": "^6.8.5"
},
"devDependencies": {
"electron": "^42.0.1",
"electron": "^42.0.1",
"electron-builder": "^26.10.0"
},
"overrides": {

View File

@@ -22,13 +22,13 @@
"src/shared/contracts/",
"src/shared/utils/nodeRuntimeSupport.ts",
".env.example",
"scripts/postinstall.mjs",
"scripts/postinstallSupport.mjs",
"scripts/responses-ws-proxy.mjs",
"scripts/check-supported-node-runtime.ts",
"scripts/sync-env.mjs",
"scripts/native-binary-compat.mjs",
"scripts/build-next-isolated.mjs",
"scripts/build/postinstall.mjs",
"scripts/build/postinstallSupport.mjs",
"scripts/dev/responses-ws-proxy.mjs",
"scripts/check/check-supported-node-runtime.ts",
"scripts/dev/sync-env.mjs",
"scripts/build/native-binary-compat.mjs",
"scripts/build/build-next-isolated.mjs",
"README.md",
"LICENSE"
],
@@ -61,59 +61,59 @@
},
"homepage": "https://omniroute.online",
"scripts": {
"dev": "node scripts/run-next.mjs dev",
"prebuild:docs": "node scripts/generate-docs-index.mjs",
"gen:provider-reference": "node --import tsx/esm scripts/gen-provider-reference.ts",
"build": "node scripts/build-next-isolated.mjs",
"build:cli": "node --import tsx/esm scripts/prepublish.ts",
"start": "node scripts/run-next.mjs start",
"dev": "node scripts/dev/run-next.mjs dev",
"prebuild:docs": "node scripts/docs/generate-docs-index.mjs",
"gen:provider-reference": "node --import tsx/esm scripts/docs/gen-provider-reference.ts",
"build": "node scripts/build/build-next-isolated.mjs",
"build:cli": "node --import tsx/esm scripts/build/prepublish.ts",
"start": "node scripts/dev/run-next.mjs start",
"lint": "eslint .",
"electron:dev": "concurrently \"npm run dev\" \"wait-on http://localhost:20128 && cd electron && npm run dev\"",
"electron:build": "npm run build && cd electron && npm run build",
"electron:build:win": "npm run build && cd electron && npm run build:win",
"electron:build:mac": "npm run build && cd electron && npm run build:mac",
"electron:build:linux": "npm run build && cd electron && npm run build:linux",
"electron:smoke:packaged": "node scripts/smoke-electron-packaged.mjs",
"electron:smoke:packaged": "node scripts/dev/smoke-electron-packaged.mjs",
"test": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --import tsx/esm --test --test-concurrency=10 tests/unit/*.test.ts",
"test:unit": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --import tsx/esm --test --test-concurrency=10 tests/unit/*.test.ts",
"test:plan3": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --import tsx/esm --test tests/unit/plan3-p0.test.ts",
"test:fixes": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --import tsx/esm --test tests/unit/fixes-p1.test.ts",
"test:security": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --import tsx/esm --test tests/unit/security-fase01.test.ts",
"check:cycles": "node scripts/check-cycles.mjs",
"check:route-validation:t06": "node scripts/check-route-validation.mjs",
"check:any-budget:t11": "node scripts/check-t11-any-budget.mjs",
"check:docs-sync": "node scripts/check-docs-sync.mjs",
"check:env-doc-sync": "node scripts/check-env-doc-sync.mjs",
"check:docs-counts": "node scripts/check-docs-counts-sync.mjs",
"check:deprecated-versions": "node scripts/check-deprecated-versions.mjs",
"check:cycles": "node scripts/check/check-cycles.mjs",
"check:route-validation:t06": "node scripts/check/check-route-validation.mjs",
"check:any-budget:t11": "node scripts/check/check-t11-any-budget.mjs",
"check:docs-sync": "node scripts/check/check-docs-sync.mjs",
"check:env-doc-sync": "node scripts/check/check-env-doc-sync.mjs",
"check:docs-counts": "node scripts/check/check-docs-counts-sync.mjs",
"check:deprecated-versions": "node scripts/check/check-deprecated-versions.mjs",
"check:docs-all": "npm run check:docs-sync && npm run check:docs-counts && npm run check:env-doc-sync && npm run check:deprecated-versions",
"check:node-runtime": "node --import tsx/esm scripts/check-supported-node-runtime.ts",
"check:pack-artifact": "node --import tsx/esm scripts/validate-pack-artifact.ts",
"check:node-runtime": "node --import tsx/esm scripts/check/check-supported-node-runtime.ts",
"check:pack-artifact": "node --import tsx/esm scripts/build/validate-pack-artifact.ts",
"audit:deps": "npm audit --audit-level=moderate && npm run audit:electron",
"audit:electron": "npm --prefix electron audit --audit-level=moderate",
"typecheck:core": "tsc --pretty false -p tsconfig.typecheck-core.json",
"typecheck:noimplicit:core": "tsc --pretty false -p tsconfig.typecheck-noimplicit-core.json",
"backfill-aggregation": "node --import tsx/esm src/scripts/backfillAggregation.ts",
"env:sync": "node scripts/sync-env.mjs",
"env:sync": "node scripts/dev/sync-env.mjs",
"test:integration": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --import tsx/esm --test --test-force-exit --test-concurrency=1 tests/integration/*.test.ts",
"test:e2e": "node scripts/run-playwright-tests.mjs test tests/e2e/*.spec.ts",
"test:protocols:e2e": "node scripts/run-protocol-clients-tests.mjs",
"test:e2e": "node scripts/dev/run-playwright-tests.mjs test tests/e2e/*.spec.ts",
"test:protocols:e2e": "node scripts/dev/run-protocol-clients-tests.mjs",
"test:vitest": "vitest run --config vitest.mcp.config.ts",
"test:ecosystem": "node scripts/run-ecosystem-tests.mjs",
"test:ecosystem": "node scripts/dev/run-ecosystem-tests.mjs",
"test:coverage": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true c8 --output-dir coverage --exclude=tests/** --exclude=**/*.test.* --reporter=text-summary --reporter=html --reporter=json-summary --reporter=lcov --check-coverage --statements 75 --lines 75 --functions 75 --branches 70 node --import tsx/esm --test --test-concurrency=1 tests/unit/*.test.ts",
"test:coverage:legacy": "c8 --output-dir coverage --exclude=open-sse --check-coverage --lines 50 --functions 50 --branches 50 node --import tsx/esm --test tests/unit/*.test.ts",
"coverage:report": "c8 report --output-dir coverage --exclude=tests/** --exclude=**/*.test.* --reporter=text --reporter=text-summary --reporter=html --reporter=json-summary --reporter=lcov",
"coverage:summary": "node scripts/test-report-summary.mjs --input coverage/coverage-summary.json --output coverage/coverage-report.md --threshold 60",
"check:pr-test-policy": "node scripts/check-pr-test-policy.mjs",
"coverage:summary": "node scripts/check/test-report-summary.mjs --input coverage/coverage-summary.json --output coverage/coverage-report.md --threshold 60",
"check:pr-test-policy": "node scripts/check/check-pr-test-policy.mjs",
"coverage:report:legacy": "c8 report --output-dir coverage --exclude=open-sse --reporter=text --reporter=text-summary",
"test:all": "npm run test:unit && npm run test:vitest && npm run test:ecosystem && npm run test:e2e",
"check": "npm run lint && npm run test",
"prepublishOnly": "npm run build:cli && npm run check:pack-artifact",
"postinstall": "node scripts/postinstall.mjs",
"uninstall": "node scripts/uninstall.mjs",
"uninstall:full": "node scripts/uninstall.mjs --full",
"postinstall": "node scripts/build/postinstall.mjs",
"uninstall": "node scripts/build/uninstall.mjs",
"uninstall:full": "node scripts/build/uninstall.mjs --full",
"prepare": "husky",
"system-info": "node scripts/system-info.mjs"
"system-info": "node scripts/dev/system-info.mjs"
},
"dependencies": {
"@lobehub/icons": "^5.8.0",
@@ -128,7 +128,7 @@
"express": "^5.2.1",
"fetch-socks": "^1.3.3",
"fuse.js": "^7.3.0",
"gray-matter": "^4.0.3",
"gray-matter": "^4.0.3",
"http-proxy-middleware": "^4.0.0",
"https-proxy-agent": "^9.0.0",
"ioredis": "^5.10.1",

View File

@@ -34,7 +34,7 @@ export default defineConfig({
},
],
webServer: {
command: `${JSON.stringify(process.execPath)} scripts/run-next-playwright.mjs ${playwrightServerMode}`,
command: `${JSON.stringify(process.execPath)} scripts/dev/run-next-playwright.mjs ${playwrightServerMode}`,
url: webServerReadyUrl,
reuseExistingServer: !process.env.CI,
timeout: Number.isFinite(playwrightWebServerTimeout) ? playwrightWebServerTimeout : 900_000,

View File

@@ -142,7 +142,7 @@ req.on("data", (chunk) => {
});
req.on("end", () => {
const raw = Buffer.concat(collected);
const outDir = path.join(__dirname, "..", "tests", "fixtures", "cursor");
const outDir = path.join(__dirname, "..", "..", "tests", "fixtures", "cursor");
fs.mkdirSync(outDir, { recursive: true });
const outFile = path.join(outDir, `${fixtureName}.bin`);
fs.writeFileSync(outFile, raw);
@@ -168,7 +168,7 @@ setTimeout(() => {
} catch {}
const raw = Buffer.concat(collected);
if (raw.length > 0) {
const outDir = path.join(__dirname, "..", "tests", "fixtures", "cursor");
const outDir = path.join(__dirname, "..", "..", "tests", "fixtures", "cursor");
fs.mkdirSync(outDir, { recursive: true });
fs.writeFileSync(path.join(outDir, `${fixtureName}.bin`), raw);
}

View File

@@ -4,9 +4,9 @@
// invocation so cursor-agent prints "Available models: ..." on stderr.
//
// Usage:
// node scripts/sync-cursor-models.mjs # spawn cursor-agent and apply
// node scripts/sync-cursor-models.mjs --dry-run # print proposed block, don't write
// node scripts/sync-cursor-models.mjs --from-stdin # read the error message from stdin
// node scripts/ad-hoc/sync-cursor-models.mjs # spawn cursor-agent and apply
// node scripts/ad-hoc/sync-cursor-models.mjs --dry-run # print proposed block, don't write
// node scripts/ad-hoc/sync-cursor-models.mjs --from-stdin # read the error message from stdin
import { spawnSync } from "node:child_process";
import { readFileSync, writeFileSync } from "node:fs";

View File

@@ -312,7 +312,7 @@ export function bootstrapEnv({ dataDirOverride, quiet = false } = {}) {
return merged;
}
// ── CLI usage: node scripts/bootstrap-env.mjs ──────────────────────────────
// ── CLI usage: node scripts/build/bootstrap-env.mjs ──────────────────────────────
if (process.argv[1] && process.argv[1].endsWith("bootstrap-env.mjs")) {
const env = bootstrapEnv();
process.stderr.write(`[bootstrap] Done. DATA_DIR resolved to: ${resolveDataDir()}\n`);

View File

@@ -187,7 +187,7 @@ export async function main() {
console.log("[build-next-isolated] Generating docs index...");
try {
const { execSync } = await import("node:child_process");
execSync("node scripts/generate-docs-index.mjs", { cwd: projectRoot, stdio: "inherit" });
execSync("node scripts/docs/generate-docs-index.mjs", { cwd: projectRoot, stdio: "inherit" });
} catch (docGenErr) {
console.warn(
"[build-next-isolated] Docs index generation failed (non-fatal):",

View File

@@ -33,7 +33,7 @@ export const APP_STAGING_ALLOWED_EXACT_PATHS: string[] = [
"open-sse/mcp-server/server.js",
"package.json",
"responses-ws-proxy.mjs",
"scripts/sync-env.mjs",
"scripts/dev/sync-env.mjs",
"server.js",
"server-ws.mjs",
];
@@ -74,13 +74,13 @@ export const PACK_ARTIFACT_ROOT_ALLOWED_EXACT_PATHS: string[] = [
"open-sse/mcp-server/scopeEnforcement.ts",
"open-sse/mcp-server/server.ts",
"package.json",
"scripts/build-next-isolated.mjs",
"scripts/check-supported-node-runtime.ts",
"scripts/native-binary-compat.mjs",
"scripts/postinstall.mjs",
"scripts/postinstallSupport.mjs",
"scripts/responses-ws-proxy.mjs",
"scripts/sync-env.mjs",
"scripts/build/build-next-isolated.mjs",
"scripts/check/check-supported-node-runtime.ts",
"scripts/build/native-binary-compat.mjs",
"scripts/build/postinstall.mjs",
"scripts/build/postinstallSupport.mjs",
"scripts/dev/responses-ws-proxy.mjs",
"scripts/dev/sync-env.mjs",
"src/shared/utils/nodeRuntimeSupport.ts",
];
@@ -103,9 +103,9 @@ export const PACK_ARTIFACT_REQUIRED_PATHS: string[] = [
"bin/nodeRuntimeSupport.mjs",
"bin/omniroute.mjs",
"package.json",
"scripts/native-binary-compat.mjs",
"scripts/postinstall.mjs",
"scripts/postinstallSupport.mjs",
"scripts/build/native-binary-compat.mjs",
"scripts/build/postinstall.mjs",
"scripts/build/postinstallSupport.mjs",
"src/shared/utils/nodeRuntimeSupport.ts",
];

View File

@@ -31,7 +31,7 @@ import { hasStandaloneAppBundle } from "./postinstallSupport.mjs";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const ROOT = join(__dirname, "..");
const ROOT = join(__dirname, "..", "..");
const appBinary = join(
ROOT,

View File

@@ -15,7 +15,7 @@ import { fileURLToPath } from "node:url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const ROOT = join(__dirname, "..");
const ROOT = join(__dirname, "..", "..");
const STANDALONE_DIR = join(ROOT, ".next", "standalone");
const ELECTRON_STANDALONE_DIR = join(ROOT, ".next", "electron-standalone");

View File

@@ -6,7 +6,7 @@
* Builds the Next.js app in standalone mode and copies output
* into the `app/` directory that gets published to npm.
*
* Run with: node scripts/prepublish.mjs
* Run with: node scripts/build/prepublish.ts
*/
import { execFileSync } from "node:child_process";
@@ -33,7 +33,7 @@ import {
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const ROOT = join(__dirname, "..");
const ROOT = join(__dirname, "..", "..");
const NPM_BIN = process.platform === "win32" ? "npm.cmd" : "npm";
const NPX_BIN = process.platform === "win32" ? "npx.cmd" : "npx";
@@ -200,14 +200,14 @@ console.log(" 📋 Copying standalone build to app/...");
mkdirSync(APP_DIR, { recursive: true });
cpSync(standaloneDir, APP_DIR, { recursive: true });
const standaloneWsSrc = join(ROOT, "scripts", "standalone-server-ws.mjs");
const responsesWsProxySrc = join(ROOT, "scripts", "responses-ws-proxy.mjs");
const standaloneWsSrc = join(ROOT, "scripts", "dev", "standalone-server-ws.mjs");
const responsesWsProxySrc = join(ROOT, "scripts", "dev", "responses-ws-proxy.mjs");
if (existsSync(standaloneWsSrc) && existsSync(responsesWsProxySrc)) {
console.log(" 📋 Adding Responses WebSocket standalone wrapper...");
cpSync(standaloneWsSrc, join(APP_DIR, "server-ws.mjs"));
writeFileSync(
join(APP_DIR, "responses-ws-proxy.mjs"),
'export * from "../scripts/responses-ws-proxy.mjs";\n'
'export * from "../scripts/dev/responses-ws-proxy.mjs";\n'
);
}

View File

@@ -14,7 +14,7 @@ import {
const __filename: string = fileURLToPath(import.meta.url);
const __dirname: string = dirname(__filename);
const ROOT: string = join(__dirname, "..");
const ROOT: string = join(__dirname, "..", "..");
const npmCommand: string = process.platform === "win32" ? "npm.cmd" : "npm";
function runPackDryRun(): any {

View File

@@ -3,15 +3,15 @@
// Uses hardcoded regexes to avoid dynamic RegExp() (ReDoS concern flagged by semgrep).
// Exits 0 if clean, 1 (in --strict mode) if drift detected.
//
// Run: node scripts/check-deprecated-versions.mjs
// Strict: node scripts/check-deprecated-versions.mjs --strict
// Run: node scripts/check/check-deprecated-versions.mjs
// Strict: node scripts/check/check-deprecated-versions.mjs --strict
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const ROOT = path.resolve(__dirname, "..");
const ROOT = path.resolve(__dirname, "..", "..");
const DOCS_DIR = path.join(ROOT, "docs");
const PKG_JSON = path.join(ROOT, "package.json");
const STRICT = process.argv.includes("--strict");

View File

@@ -8,14 +8,14 @@
// - Cloud agents in src/lib/cloudAgent/agents/
//
// Exits 0 on success, 1 on detected drift.
// Run: node scripts/check-docs-counts-sync.mjs
// Run: node scripts/check/check-docs-counts-sync.mjs
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const ROOT = path.resolve(__dirname, "..");
const ROOT = path.resolve(__dirname, "..", "..");
const COMMON_NON_IMPL_BASENAMES = new Set([
"index.ts",

View File

@@ -2,8 +2,8 @@
// Validates that env vars referenced in code appear in .env.example AND in docs/ENVIRONMENT.md.
// Exits 0 on success, 1 on missing entries. Designed for use in pre-commit / CI.
//
// Run: node scripts/check-env-doc-sync.mjs
// Strict mode: node scripts/check-env-doc-sync.mjs --strict
// Run: node scripts/check/check-env-doc-sync.mjs
// Strict mode: node scripts/check/check-env-doc-sync.mjs --strict
// In strict mode, missing entries cause failure. In default mode, only summary is printed.
import fs from "node:fs";
@@ -12,7 +12,7 @@ import { fileURLToPath } from "node:url";
import { execSync } from "node:child_process";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const ROOT = path.resolve(__dirname, "..");
const ROOT = path.resolve(__dirname, "..", "..");
const ENV_EXAMPLE = path.join(ROOT, ".env.example");
const ENV_DOC = path.join(ROOT, "docs", "ENVIRONMENT.md");

View File

@@ -6,14 +6,17 @@ const SOURCE_ROOTS = ["src/", "open-sse/", "electron/", "bin/"];
const TEST_PATTERNS = [/^tests\//, /(?:^|\/)__tests__\//, /\.(?:test|spec)\.[cm]?[jt]sx?$/];
// Test files for specific source types (e.g., Python validation scripts for i18n)
const TEST_FILE_PATTERNS = {
"src/i18n/messages/": [/\/scripts\/validate_translation\.py$/, /\/scripts\/check_translations\.py$/],
"src/i18n/messages/": [
/\/scripts\/validate_translation\.py$/,
/\/scripts\/check_translations\.py$/,
],
};
// Exclude directories that don't require tests (i18n has Python validation, docs, config)
const EXCLUDED_PATTERNS = [
/\/i18n\/messages\//, // i18n files have their own Python test scripts
/\.md$/, // Documentation
/\.yaml$/, // Config files
/\.yml$/, // Config files
/\/i18n\/messages\//, // i18n files have their own Python test scripts
/\.md$/, // Documentation
/\.yaml$/, // Config files
/\.yml$/, // Config files
];
function getArg(name, fallbackValue = "") {
@@ -30,7 +33,7 @@ function runGit(args) {
function isSourceFile(filePath) {
// Exclude patterns that don't require tests
if (EXCLUDED_PATTERNS.some(pattern => pattern.test(filePath))) {
if (EXCLUDED_PATTERNS.some((pattern) => pattern.test(filePath))) {
return false;
}
return SOURCE_ROOTS.some((root) => filePath.startsWith(root));

View File

@@ -3,7 +3,7 @@
import {
getNodeRuntimeSupport,
getNodeRuntimeWarning,
} from "../src/shared/utils/nodeRuntimeSupport.ts";
} from "../../src/shared/utils/nodeRuntimeSupport.ts";
const support = getNodeRuntimeSupport();

View File

@@ -3,7 +3,7 @@
import { spawn } from "node:child_process";
import { join } from "node:path";
import { setTimeout as delay } from "node:timers/promises";
import { sanitizeColorEnv } from "./runtime-env.mjs";
import { sanitizeColorEnv } from "../build/runtime-env.mjs";
function parsePort(value, fallback) {
const parsed = Number.parseInt(String(value), 10);

View File

@@ -9,8 +9,8 @@ import {
sanitizeColorEnv,
spawnWithForwardedSignals,
withRuntimePortEnv,
} from "./runtime-env.mjs";
import { bootstrapEnv } from "./bootstrap-env.mjs";
} from "../build/runtime-env.mjs";
import { bootstrapEnv } from "../build/bootstrap-env.mjs";
const mode = process.argv[2] === "start" ? "start" : "dev";
const cwd = process.cwd();

View File

@@ -4,8 +4,8 @@ import fs from "node:fs";
import http from "node:http";
import path from "node:path";
import next from "next";
import { bootstrapEnv } from "./bootstrap-env.mjs";
import { resolveRuntimePorts, withRuntimePortEnv } from "./runtime-env.mjs";
import { bootstrapEnv } from "../build/bootstrap-env.mjs";
import { resolveRuntimePorts, withRuntimePortEnv } from "../build/runtime-env.mjs";
import { createOmnirouteWsBridge } from "./v1-ws-bridge.mjs";
import { createResponsesWsProxy } from "./responses-ws-proxy.mjs";
import { randomUUID } from "node:crypto";

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env node
import { spawn } from "node:child_process";
import { sanitizeColorEnv } from "./runtime-env.mjs";
import { sanitizeColorEnv } from "../build/runtime-env.mjs";
const defaultArgs = ["test", "tests/e2e/*.spec.ts"];
const forwardedArgs = process.argv.slice(2);

View File

@@ -3,7 +3,7 @@
import { spawn } from "node:child_process";
import { join } from "node:path";
import { setTimeout as delay } from "node:timers/promises";
import { sanitizeColorEnv } from "./runtime-env.mjs";
import { sanitizeColorEnv } from "../build/runtime-env.mjs";
function parsePort(value, fallback) {
const parsed = Number.parseInt(String(value), 10);

View File

@@ -4,8 +4,8 @@ import {
resolveRuntimePorts,
withRuntimePortEnv,
spawnWithForwardedSignals,
} from "./runtime-env.mjs";
import { bootstrapEnv } from "./bootstrap-env.mjs";
} from "../build/runtime-env.mjs";
import { bootstrapEnv } from "../build/bootstrap-env.mjs";
const env = bootstrapEnv();
const runtimePorts = resolveRuntimePorts(env);

View File

@@ -9,7 +9,7 @@ import { fileURLToPath, pathToFileURL } from "node:url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const ROOT = join(__dirname, "..");
const ROOT = join(__dirname, "..", "..");
const DEFAULT_TIMEOUT_MS = 45_000;
const DEFAULT_SETTLE_MS = 2_000;

View File

@@ -3,7 +3,7 @@
* system-info.mjs OmniRoute System Information Reporter (#280)
*
* Collects system/environment info for bug reports.
* Usage: node scripts/system-info.mjs [--output system-info.txt]
* Usage: node scripts/dev/system-info.mjs [--output system-info.txt]
*
* Output includes:
* - Node.js version
@@ -21,7 +21,7 @@ import { fileURLToPath } from "url";
import os from "os";
const __dirname = dirname(fileURLToPath(import.meta.url));
const ROOT = join(__dirname, "..");
const ROOT = join(__dirname, "..", "..");
// ── Helpers ────────────────────────────────────────────────────────────────

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env node
// Generates docs/PROVIDER_REFERENCE.md from src/shared/constants/providers.ts.
// Run: node --import tsx/esm scripts/gen-provider-reference.ts
// Run: node --import tsx/esm scripts/docs/gen-provider-reference.ts
import fs from "node:fs";
import path from "node:path";
@@ -22,10 +22,10 @@ import {
VIDEO_PROVIDER_IDS,
EMBEDDING_RERANK_PROVIDER_IDS,
SELF_HOSTED_CHAT_PROVIDER_IDS,
} from "../src/shared/constants/providers.ts";
} from "../../src/shared/constants/providers.ts";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const ROOT = path.resolve(__dirname, "..");
const ROOT = path.resolve(__dirname, "..", "..");
const OUT_FILE = path.join(ROOT, "docs", "PROVIDER_REFERENCE.md");
type ProviderRecord = {

View File

@@ -5,7 +5,7 @@
* src/app/docs/lib/docs-auto-generated.ts with static navigation + search data.
*
* This file is imported by both client and server components NO fs/path imports.
* Run via: node scripts/generate-docs-index.mjs
* Run via: node scripts/docs/generate-docs-index.mjs
* Automatically runs as prebuild step.
*/
@@ -15,7 +15,7 @@ import matter from "gray-matter";
import { fileURLToPath } from "node:url";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const ROOT = path.resolve(__dirname, "..");
const ROOT = path.resolve(__dirname, "..", "..");
const DOCS_DIR = path.join(ROOT, "docs");
const OUT_FILE = path.join(ROOT, "src", "app", "docs", "lib", "docs-auto-generated.ts");
@@ -127,8 +127,8 @@ if (!fs.existsSync(DOCS_DIR)) {
fs.mkdirSync(path.dirname(OUT_FILE), { recursive: true });
fs.writeFileSync(
OUT_FILE,
`// AUTO-GENERATED by scripts/generate-docs-index.mjs — DO NOT EDIT MANUALLY
// Regenerate with: node scripts/generate-docs-index.mjs
`// AUTO-GENERATED by scripts/docs/generate-docs-index.mjs — DO NOT EDIT MANUALLY
// Regenerate with: node scripts/docs/generate-docs-index.mjs
export interface AutoGenDocItem {
slug: string;
@@ -238,8 +238,8 @@ if (searchIndex.some((item) => item.slug === "api-reference")) {
// ---------- Write output ----------
const output = `// AUTO-GENERATED by scripts/generate-docs-index.mjs — DO NOT EDIT MANUALLY
// Regenerate with: node scripts/generate-docs-index.mjs
const output = `// AUTO-GENERATED by scripts/docs/generate-docs-index.mjs — DO NOT EDIT MANUALLY
// Regenerate with: node scripts/docs/generate-docs-index.mjs
export interface AutoGenDocItem {
slug: string;

View File

@@ -4,9 +4,9 @@ Translation check script for OmniRoute.
Checks if all translation keys used in code exist in en.json.
Usage:
python scripts/check_translations.py
python scripts/check_translations.py --verbose
python scripts/check_translations.py --fix
python scripts/i18n/check_translations.py
python scripts/i18n/check_translations.py --verbose
python scripts/i18n/check_translations.py --fix
"""
import json

View File

@@ -6,7 +6,7 @@ API (like OmniRoute itself) to translate any English paragraphs into the
target language.
Usage:
python3 scripts/i18n_autotranslate.py --api-url http://localhost:20128/v1 --api-key sk-your-omniroute-key --model cx/gpt-5.4
python3 scripts/i18n/i18n_autotranslate.py --api-url http://localhost:20128/v1 --api-key sk-your-omniroute-key --model cx/gpt-5.4
"""
import os

View File

@@ -30,8 +30,10 @@ NC = "\033[0m"
# Configuration - find repo root relative to this script
_script_dir = Path(__file__).parent.resolve()
# If script is in scripts/ subfolder, go up one level to repo root
if _script_dir.name == "scripts":
# Walk up out of scripts/<group>/, scripts/, or stay at cwd
if _script_dir.name == "i18n" and _script_dir.parent.name == "scripts":
SCRIPT_DIR = _script_dir.parent.parent
elif _script_dir.name == "scripts":
SCRIPT_DIR = _script_dir.parent
else:
SCRIPT_DIR = _script_dir

View File

@@ -10,7 +10,7 @@ import { join } from "node:path";
import { NextResponse } from "next/server";
import { isAuthenticated } from "@/shared/utils/apiAuth";
// @ts-expect-error - .mjs without types
import { getEnvSyncPlan, syncEnv } from "../../../../../../scripts/sync-env.mjs";
import { getEnvSyncPlan, syncEnv } from "../../../../../../scripts/dev/sync-env.mjs";
async function loadSyncHelpers() {
return { getEnvSyncPlan, syncEnv };

View File

@@ -234,7 +234,7 @@ export async function POST(req: NextRequest) {
send({ step: "rebuild", status: "done", message: "Dependencies installed" });
try {
await execFileAsync("node", ["scripts/sync-env.mjs"], {
await execFileAsync("node", ["scripts/dev/sync-env.mjs"], {
timeout: 15_000,
cwd: process.cwd(),
});

View File

@@ -1,5 +1,5 @@
// AUTO-GENERATED by scripts/generate-docs-index.mjs — DO NOT EDIT MANUALLY
// Regenerate with: node scripts/generate-docs-index.mjs
// AUTO-GENERATED by scripts/docs/generate-docs-index.mjs — DO NOT EDIT MANUALLY
// Regenerate with: node scripts/docs/generate-docs-index.mjs
export interface AutoGenDocItem {
slug: string;

View File

@@ -16,7 +16,7 @@ import { buildGitLabOAuthEndpoints, GITLAB_DUO_DEFAULT_BASE_URL } from "../gitla
*
* All credentials are read exclusively from environment variables.
* Default values match the public CLI client IDs from .env.example
* (auto-populated by scripts/sync-env.mjs on install).
* (auto-populated by scripts/dev/sync-env.mjs on install).
*
* These are public OAuth client credentials for desktop/CLI applications
* that rely on PKCE for security (RFC 8252), not on secret confidentiality.

View File

@@ -235,7 +235,7 @@ export function buildSourceUpdateScript(latest: string, gitRemote = "origin"): s
'git branch "$backup_branch" 2>/dev/null || true',
`git checkout "${targetTag}"`,
"npm install --legacy-peer-deps",
"node scripts/sync-env.mjs 2>/dev/null || true",
"node scripts/dev/sync-env.mjs 2>/dev/null || true",
"npm run build",
"if command -v pm2 >/dev/null 2>&1; then",
" pm2 restart omniroute --update-env || true",

View File

@@ -1,4 +1,4 @@
# Captured cursor wire bytes from scripts/cursor-tap.cjs.
# Captured cursor wire bytes from scripts/ad-hoc/cursor-tap.cjs.
# By default these are local-only — uncomment a specific fixture to
# include it in the repo as a regression baseline.
*.bin

View File

@@ -111,7 +111,7 @@ function createServerProcess() {
const stderrLines: string[] = [];
let exitInfo: { code: number | null; signal: NodeJS.Signals | null } | null = null;
const child = spawn(process.execPath, ["scripts/run-next-playwright.mjs", "dev"], {
const child = spawn(process.execPath, ["scripts/dev/run-next-playwright.mjs", "dev"], {
cwd: REPO_ROOT,
env: {
...process.env,

View File

@@ -16,7 +16,7 @@
* node --import tsx/esm --test tests/integration/cursor-e2e.test.ts
*
* Capturing wire fixtures (separate workflow):
* CURSOR_TOKEN=... node scripts/cursor-tap.cjs single-turn-chat "say PING"
* CURSOR_TOKEN=... node scripts/ad-hoc/cursor-tap.cjs single-turn-chat "say PING"
*/
import test from "node:test";

View File

@@ -184,7 +184,7 @@ function createServerProcess(dataDir: string, port: number) {
const stdoutLines: string[] = [];
const stderrLines: string[] = [];
let exitInfo: { code: number | null; signal: NodeJS.Signals | null } | null = null;
const child = spawn(process.execPath, ["scripts/run-next-playwright.mjs", "dev"], {
const child = spawn(process.execPath, ["scripts/dev/run-next-playwright.mjs", "dev"], {
cwd: REPO_ROOT,
env: {
...process.env,

View File

@@ -5,7 +5,7 @@ import os from "node:os";
import path from "node:path";
import Database from "better-sqlite3";
import { bootstrapEnv } from "../../scripts/bootstrap-env.mjs";
import { bootstrapEnv } from "../../scripts/build/bootstrap-env.mjs";
function withTempEnv(fn) {
const originalCwd = process.cwd();

View File

@@ -11,7 +11,7 @@ const {
pruneStandaloneArtifacts,
resolveNextBuildEnv,
syncStandaloneNativeAssets,
} = await import("../../scripts/build-next-isolated.mjs");
} = await import("../../scripts/build/build-next-isolated.mjs");
async function withTempDir(fn) {
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "omniroute-build-next-isolated-"));

View File

@@ -5,7 +5,7 @@ import {
buildSmokeEnv,
FATAL_LOG_PATTERNS,
LINUX_EXECUTABLE_NAMES,
} from "../../scripts/smoke-electron-packaged.mjs";
} from "../../scripts/dev/smoke-electron-packaged.mjs";
test("electron smoke discovers the default Linux executable name", () => {
assert.ok(LINUX_EXECUTABLE_NAMES.includes("omniroute-desktop"));

View File

@@ -7,7 +7,7 @@ import { tmpdir } from "node:os";
import {
detectNativeBinaryTarget,
isNativeBinaryCompatible,
} from "../../scripts/native-binary-compat.mjs";
} from "../../scripts/build/native-binary-compat.mjs";
function makeElfBinary(machine) {
const buffer = Buffer.alloc(64);

View File

@@ -10,12 +10,12 @@ import {
findMissingArtifactPaths,
findUnexpectedArtifactPaths,
normalizeArtifactPath,
} from "../../scripts/pack-artifact-policy.ts";
} from "../../scripts/build/pack-artifact-policy.ts";
test("normalizeArtifactPath normalizes slashes and leading relative markers", () => {
assert.equal(
normalizeArtifactPath("./app\\scripts\\scratch\\test.js"),
"app/scripts/scratch/test.js"
normalizeArtifactPath("./app\\scripts\\ad-hoc\\test.js"),
"app/scripts/ad-hoc/test.js"
);
});
@@ -25,7 +25,7 @@ test("findUnexpectedArtifactPaths flags staged app files outside the allowlist",
"open-sse/services/compression/engines/rtk/filters/generic-output.json",
"open-sse/services/compression/rules/en/filler.json",
"package-lock.json",
"scripts/sync-env.mjs",
"scripts/dev/sync-env.mjs",
"server.js",
],
{
@@ -43,8 +43,8 @@ test("findUnexpectedArtifactPaths flags app pack files outside the allowlist", (
"app/open-sse/services/compression/engines/rtk/filters/generic-output.json",
"app/open-sse/services/compression/rules/en/filler.json",
"app/server.js",
"app/scripts/sync-env.mjs",
"app/scripts/prepublish.mjs",
"app/scripts/dev/sync-env.mjs",
"app/scripts/build/prepublish.mjs",
"docs/extra.md",
],
{
@@ -53,7 +53,7 @@ test("findUnexpectedArtifactPaths flags app pack files outside the allowlist", (
}
);
assert.deepEqual(unexpectedPaths, ["app/scripts/prepublish.mjs", "docs/extra.md"]);
assert.deepEqual(unexpectedPaths, ["app/scripts/build/prepublish.mjs", "docs/extra.md"]);
});
test("findMissingArtifactPaths flags missing root runtime files in the tarball", () => {
@@ -62,8 +62,8 @@ test("findMissingArtifactPaths flags missing root runtime files in the tarball",
"app/server.js",
"bin/omniroute.mjs",
"package.json",
"scripts/postinstall.mjs",
"scripts/postinstallSupport.mjs",
"scripts/build/postinstall.mjs",
"scripts/build/postinstallSupport.mjs",
],
PACK_ARTIFACT_REQUIRED_PATHS
);
@@ -77,7 +77,7 @@ test("findMissingArtifactPaths flags missing root runtime files in the tarball",
"bin/cli/index.mjs",
"bin/mcp-server.mjs",
"bin/nodeRuntimeSupport.mjs",
"scripts/native-binary-compat.mjs",
"scripts/build/native-binary-compat.mjs",
"src/shared/utils/nodeRuntimeSupport.ts",
]);
});

View File

@@ -4,7 +4,7 @@ import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from "node:fs";
import { join } from "node:path";
import { tmpdir } from "node:os";
import { hasStandaloneAppBundle } from "../../scripts/postinstallSupport.mjs";
import { hasStandaloneAppBundle } from "../../scripts/build/postinstallSupport.mjs";
test("hasStandaloneAppBundle returns false for source checkout without standalone app", () => {
const root = mkdtempSync(join(tmpdir(), "omniroute-postinstall-src-"));

View File

@@ -2,7 +2,7 @@ import test from "node:test";
import assert from "node:assert/strict";
import http from "node:http";
const { createResponsesWsProxy } = await import("../../scripts/responses-ws-proxy.mjs");
const { createResponsesWsProxy } = await import("../../scripts/dev/responses-ws-proxy.mjs");
function listen(server) {
return new Promise((resolve) => {

View File

@@ -4,7 +4,7 @@ import fs from "node:fs";
import os from "node:os";
import path from "node:path";
const playwrightRunner = await import("../../scripts/run-next-playwright.mjs");
const playwrightRunner = await import("../../scripts/dev/run-next-playwright.mjs");
test("resolvePlaywrightAppBackupDir uses a per-run backup when a stale backup already exists", () => {
const cwd = "/tmp/omniroute-playwright-runner";

View File

@@ -7,7 +7,7 @@ import { pathToFileURL } from "node:url";
const require = createRequire(import.meta.url);
const childProcess = require("node:child_process");
const modulePath = path.join(process.cwd(), "scripts/runtime-env.mjs");
const modulePath = path.join(process.cwd(), "scripts/build/runtime-env.mjs");
const originalSpawn = childProcess.spawn;
const originalProcessOn = process.on;

View File

@@ -3,7 +3,7 @@ import assert from "node:assert/strict";
// We test the standalone (scripts/) version of port resolution since
// the src/ version uses @/ alias that requires the full Next.js build.
import { parsePort, resolveRuntimePorts } from "../../scripts/runtime-env.mjs";
import { parsePort, resolveRuntimePorts } from "../../scripts/build/runtime-env.mjs";
describe("parsePort", () => {
it("parses a valid port number", () => {

View File

@@ -4,7 +4,7 @@ import fs from "node:fs";
import os from "node:os";
import path from "node:path";
const { syncEnv } = (await import("../../scripts/sync-env.mjs")) as {
const { syncEnv } = (await import("../../scripts/dev/sync-env.mjs")) as {
syncEnv: (opts?: { rootDir?: string; quiet?: boolean; scope?: string }) => {
created: boolean;
added: number;

View File

@@ -2,7 +2,7 @@ import test from "node:test";
import assert from "node:assert/strict";
import http from "node:http";
const { createOmnirouteWsBridge } = await import("../../scripts/v1-ws-bridge.mjs");
const { createOmnirouteWsBridge } = await import("../../scripts/dev/v1-ws-bridge.mjs");
function listen(server) {
return new Promise((resolve) => {