From a23aa248947625d6e4f085e4ca9c6615b3616f49 Mon Sep 17 00:00:00 2001 From: Val Alexander Date: Fri, 18 Sep 2026 21:42:34 -0500 Subject: [PATCH 1/3] Run the project's checks in GitHub Actions Nothing was running the suites automatically. `.github/workflows/ci.yml` runs them on every pull request and on main, in two parallel jobs: unit tests, types, lint and build in one; the Playwright suite in the other. The end-to-end job needs no credentials, because playwright.config.ts serves the app with TYPESAFE_API_KEY unset and the suite routes through the simulator. Chromium is cached on the lockfile hash, and a failing run uploads its report as an artifact. Node is pinned in the workflow only: the manifest pins neither Node nor a package manager, and this does not change that. Co-Authored-By: Claude Fable 5.1 --- .github/workflows/ci.yml | 83 ++++++++++++++++++++++++++++++++++++++++ CONTRIBUTING.md | 2 + 2 files changed, 85 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..e95e8cf --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,83 @@ +# Runs the checks listed in CONTRIBUTING.md on every pull request and on main. +# +# Node: the manifest pins neither Node nor a package manager, so CI uses the +# version this project is developed against. Next 16 requires >= 20.9. +name: CI + +on: + push: + branches: [main] + pull_request: + +permissions: + contents: read + +# A new push to the same branch supersedes an in-flight run. +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + NODE_VERSION: "24" + +jobs: + checks: + name: unit, types, lint, build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: npm + + - run: npm ci + + - name: Unit tests + run: npm test + + - name: Types + run: npm run typecheck + + - name: Lint + run: npm run lint + + - name: Production build + run: npm run build + + e2e: + name: end-to-end (Playwright) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: npm + + - run: npm ci + + # Keyed on the lockfile, so the cache is rebuilt when Playwright's version moves. + - name: Cache Playwright browsers + uses: actions/cache@v4 + with: + path: ~/.cache/ms-playwright + key: playwright-${{ runner.os }}-${{ hashFiles('package-lock.json') }} + + - name: Install Chromium + run: npx playwright install --with-deps chromium + + # playwright.config.ts builds the app and serves it with the key unset, so + # the suite runs in demo mode and needs no credentials. + - name: End-to-end tests + run: npm run e2e + + - name: Upload report + if: failure() + uses: actions/upload-artifact@v4 + with: + name: playwright-report + path: playwright-report/ + retention-days: 7 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 772d089..5d1886f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -20,6 +20,8 @@ npm run build npm run e2e # Playwright, builds and serves the app in demo mode ``` +GitHub Actions runs all of these on every pull request and on `main` (`.github/workflows/ci.yml`): one job for the unit tests, types, lint and build, and one for the end-to-end suite. The end-to-end job needs no credentials, since the suite serves the app with the key unset and routes through the simulator. A failing run uploads its Playwright report as an artifact. + Automated tests should not need a real API key. For UI changes, exercise model/tool modes, option validation, threshold changes, fallback visibility, key removal, storage/export behavior, keyboard controls, themes, and narrow screens. Open a pull request explaining the intended behavior, why it preserves the execution boundary, which tests ran, and any unverified behavior. Include a before/after example or screenshot when useful. A proposed GitHub topic change in `repository-metadata.json` still needs a separate authorized repository-settings update. From 93ac7851073f9edcc2d3aa7f2eece875b2157f6c Mon Sep 17 00:00:00 2001 From: Val Alexander Date: Fri, 18 Sep 2026 21:43:22 -0500 Subject: [PATCH 2/3] Add 1Password env workflow for local dev Introduce a committed `.env.1password` file that stores an `op://` reference for `TYPESAFE_API_KEY` (not the secret itself), allow it in `.gitignore`, and add `dev:op`/`start:op` scripts to run Next.js through `op run`. Update README with setup, verification (`/api/route` live status), and precedence details explaining why shell-injected values from `op run` override stale `.env.local` values. --- .env.1password | 13 +++++++++++++ .gitignore | 1 + README.md | 25 +++++++++++++++++++++++++ package.json | 6 ++++-- 4 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 .env.1password diff --git a/.env.1password b/.env.1password new file mode 100644 index 0000000..8f4e655 --- /dev/null +++ b/.env.1password @@ -0,0 +1,13 @@ +# 1Password-backed development environment. Contains REFERENCES, not secrets, +# so this file is committed. Run the app through `op run` to expand them: +# +# npm run dev:op # = op run --env-file=.env.1password -- next dev +# +# Requires 1Password CLI 2.x, unlocked (desktop-app integration or `op signin`). +# Values here are injected into the shell environment, and Next.js does NOT let +# .env.local override an already-set shell variable (see @next/env processEnv), +# so this reference wins over any stale key sitting in .env.local. +TYPESAFE_API_KEY="op://Development/Jev API Key/password" + +# Optional: override the Jev model id (defaults to jev-latest). +# JEV_MODEL=jev-latest diff --git a/.gitignore b/.gitignore index e1e3cbc..0c7e312 100644 --- a/.gitignore +++ b/.gitignore @@ -70,6 +70,7 @@ web_modules/ .env.* !.env.example !.env.local.example +!.env.1password # parcel-bundler cache (https://parceljs.org/) .cache diff --git a/README.md b/README.md index 1c57bb4..7cbb76d 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,31 @@ cp .env.local.example .env.local # Edit .env.local and set TYPESAFE_API_KEY, then restart the dev server. ``` +### 1Password-backed key (recommended) + +Keep the key in 1Password and run the app through `op run`, so no secret ever +lands in a file: + +```bash +npm run dev:op +``` + +`.env.1password` is committed and holds a **reference**, not a secret: +`TYPESAFE_API_KEY="op://Development/Jev API Key/password"`. `op run` expands it +into the process environment at launch. Requires 1Password CLI 2.x, unlocked +(desktop-app integration or `op signin`). + +Verify it took effect — `live` reports whether the SERVER has a key: + +```bash +curl -s http://localhost:3000/api/route +# {"live":true} ({"live":false} means demo mode) +``` + +A shell-provided variable is not overridden by `.env.local` (`@next/env`'s +`processEnv` only fills keys absent from the initial environment), so this path +wins over any stale key left in `.env.local`. + The browser calls this app's `/api/route` endpoint. The server forwards live evaluations to `POST https://api.typesafe.ai/v1/systemone` using `jev-latest`. Jev answers typed questions; it does not generate the code that executes a route. ### Bring your own key and privacy diff --git a/package.json b/package.json index 9c7bf99..4b8657d 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "typesafe-router", "version": "0.1.0", "private": true, - "description": "Jev Tool & Model Router — Jev decides, your code executes.", + "description": "Jev Tool & Model Router \u2014 Jev decides, your code executes.", "scripts": { "dev": "next dev", "build": "next build", @@ -10,7 +10,9 @@ "lint": "eslint", "test": "vitest run", "e2e": "playwright test", - "typecheck": "next typegen && tsc --noEmit" + "typecheck": "next typegen && tsc --noEmit", + "dev:op": "op run --env-file=.env.1password -- next dev", + "start:op": "op run --env-file=.env.1password -- next start" }, "dependencies": { "next": "16.3.5", From 4a67963c0afdabb127ef83b8bc0f196520d454db Mon Sep 17 00:00:00 2001 From: Val Alexander Date: Fri, 18 Sep 2026 21:46:19 -0500 Subject: [PATCH 3/3] Move the CI actions to current majors The first run warned that actions/checkout, setup-node and cache at v4 target Node 20, which runners are forcing onto Node 24 ahead of dropping it. Bump to the current majors: checkout and setup-node v7, cache v6, upload-artifact v7. Inputs are unchanged, and this run is the check. Co-Authored-By: Claude Fable 5.1 --- .github/workflows/ci.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e95e8cf..5484168 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,9 +25,9 @@ jobs: name: unit, types, lint, build runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v7 with: node-version: ${{ env.NODE_VERSION }} cache: npm @@ -50,9 +50,9 @@ jobs: name: end-to-end (Playwright) runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v7 with: node-version: ${{ env.NODE_VERSION }} cache: npm @@ -61,7 +61,7 @@ jobs: # Keyed on the lockfile, so the cache is rebuilt when Playwright's version moves. - name: Cache Playwright browsers - uses: actions/cache@v4 + uses: actions/cache@v6 with: path: ~/.cache/ms-playwright key: playwright-${{ runner.os }}-${{ hashFiles('package-lock.json') }} @@ -76,7 +76,7 @@ jobs: - name: Upload report if: failure() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: playwright-report path: playwright-report/