Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
c9eadf8
docs: add product evolution harness
cluic Jul 10, 2026
afc2ad1
docs: add V1 implementation plan
cluic Jul 10, 2026
7d7553e
chore: ignore local worktrees
cluic Jul 10, 2026
e18ae09
build: add CRP quality gates
cluic Jul 12, 2026
587707c
refactor: isolate Codex bootstrap
cluic Jul 12, 2026
57c3896
feat: add atomic provider registry
cluic Jul 12, 2026
343ffdf
feat: add secure credential stores
cluic Jul 12, 2026
1f5135b
feat: switch proxy settings atomically
cluic Jul 12, 2026
bc5a2f1
feat: add proxy worker protocol
cluic Jul 12, 2026
0ebb18e
feat: manage proxy worker lifecycle
cluic Jul 13, 2026
776265c
feat: orchestrate provider lifecycle
cluic Jul 13, 2026
5d7a784
feat: add secured local Admin API
cluic Jul 13, 2026
7a87466
feat: manage CRP through supervisor CLI
cluic Jul 13, 2026
d9bb271
docs: define bilingual Task11 UI plan
cluic Jul 13, 2026
d114061
feat: add guided local management UI
cluic Jul 13, 2026
dd4de3f
docs: close out bilingual Task11 UI
cluic Jul 13, 2026
af918d5
test: add release package and platform gates
cluic Jul 13, 2026
210cb71
fix: enforce write-only credential boundaries
cluic Jul 13, 2026
5fecf45
docs: prepare multi-provider UI release
cluic Jul 13, 2026
5748831
docs: define core-first CLI readiness slice
cluic Jul 14, 2026
e8d56bb
docs: plan core-first CLI implementation
cluic Jul 14, 2026
1183fb5
feat: harden bilingual core CLI startup
cluic Jul 14, 2026
f83c9d6
test: add serial core production chain
cluic Jul 14, 2026
4bbb97c
fix: complete live core request path
cluic Jul 14, 2026
89185a4
docs: mark local core readiness complete
cluic Jul 14, 2026
249c23e
fix: stabilize locale and capture tests
cluic Jul 14, 2026
0dc665a
docs: reconcile current and historical project state
cluic Jul 14, 2026
52a1a2c
feat: complete CRP management workflow
cluic Jul 17, 2026
b399c62
fix: repair cross-platform CI checks
cluic Jul 17, 2026
cc6cb07
fix: complete Windows platform checks
cluic Jul 17, 2026
73fa5c1
fix: accept Windows SQLite snapshot replacement
cluic Jul 17, 2026
72c9c09
test: diagnose Windows history repair failures
cluic Jul 17, 2026
9268fe7
test: report Windows repair syscall
cluic Jul 17, 2026
195375f
fix: support Windows SQLite history backups
cluic Jul 17, 2026
f8df6c3
fix: stabilize platform history and UI shutdown tests
cluic Jul 17, 2026
8152028
test: make config race checks portable
cluic Jul 17, 2026
7ac57b0
test: accept Windows SQLite lock failure
cluic Jul 17, 2026
8b11b1d
test: scope SQLite replacement race to POSIX
cluic Jul 17, 2026
04a21ca
test: support standard Windows filesystem permissions
cluic Jul 17, 2026
5aecd59
test: scope parent identity race to POSIX
cluic Jul 17, 2026
7425df4
fix: make CRP shutdown graceful on Windows
cluic Jul 17, 2026
c91301b
chore: exclude docs from pull request
cluic Jul 17, 2026
e706fdb
chore: record zsh path variable constraint
cluic Jul 18, 2026
711d84e
chore: remove stale docs references
cluic Jul 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .codex/hooks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "test -f docs/STATUS.md && test -f docs/AI_HANDOFF.md && test -f AGENTS.md",
"timeout": 10,
"statusMessage": "Checking CRP harness docs exist"
}
]
}
]
}
}
104 changes: 104 additions & 0 deletions .github/workflows/platform-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
name: Platform Tests

on:
pull_request:
push:
branches:
- main
workflow_dispatch:

permissions:
contents: read

concurrency:
group: platform-tests-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
platform:
name: Node 22 / ${{ matrix.label }}
runs-on: ${{ matrix.os }}
timeout-minutes: 45
strategy:
fail-fast: false
matrix:
include:
- label: Linux
os: ubuntu-latest
browser: false
- label: macOS
os: macos-latest
browser: true
- label: Windows
os: windows-latest
browser: true
defaults:
run:
working-directory: node
steps:
- name: Checkout
uses: actions/checkout@v4
with:
persist-credentials: false

- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 22
package-manager-cache: false

- name: Install dependencies
run: npm ci

- name: Run syntax checks
run: npm run lint

- name: Run deterministic tests
run: npm test

- name: Install Chromium
if: matrix.browser
run: npx playwright install chromium

- name: Run browser E2E tests
if: matrix.browser
run: npm run test:e2e -- --project=chromium --workers=1

- name: Upload sanitized UI evidence
if: always() && matrix.browser
uses: actions/upload-artifact@v4
with:
name: crp-ui-${{ matrix.label }}-${{ github.run_id }}
path: output/playwright/task11/**
if-no-files-found: warn
retention-days: 14

- name: Install Linux Secret Service
if: runner.os == 'Linux'
shell: bash
run: |
sudo apt-get update
sudo apt-get install --yes dbus-x11 gnome-keyring libglib2.0-bin

- name: Smoke test Linux native keyring through Secret Service
if: runner.os == 'Linux'
shell: bash
env:
CRP_NATIVE_KEYRING_SMOKE: "1"
run: |
smoke_home="$RUNNER_TEMP/crp-native-keyring-home"
mkdir -p "$smoke_home/.local/share/keyrings"
chmod 700 "$smoke_home" "$smoke_home/.local" "$smoke_home/.local/share" "$smoke_home/.local/share/keyrings"
HOME="$smoke_home" dbus-run-session -- bash -euo pipefail -c '
printf "%s" "crp-ci-keyring" | gnome-keyring-daemon --components=secrets --unlock >/dev/null
gdbus wait --session --timeout 10 org.freedesktop.secrets
gdbus call --session --dest org.freedesktop.secrets --object-path /org/freedesktop/secrets --method org.freedesktop.DBus.Peer.Ping >/dev/null
gdbus call --session --dest org.freedesktop.secrets --object-path /org/freedesktop/secrets/aliases/default --method org.freedesktop.DBus.Properties.Get org.freedesktop.Secret.Collection Label >/dev/null
node scripts/native-keyring-smoke.mjs
'

- name: Smoke test native keyring
if: runner.os != 'Linux'
env:
CRP_NATIVE_KEYRING_SMOKE: "1"
run: node scripts/native-keyring-smoke.mjs
72 changes: 65 additions & 7 deletions .github/workflows/release-preflight.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ on:
paths:
- ".github/workflows/release.yml"
- ".github/workflows/release-preflight.yml"
- ".github/workflows/platform-tests.yml"
- "node/bin/**"
- "node/src/**"
- "node/ui/**"
- "node/scripts/**"
- "node/test/package-content.test.mjs"
- "node/test/native-keyring-smoke.test.mjs"
- "node/test/release-workflows.test.mjs"
- "node/package.json"
- "node/package-lock.json"
- "node/.changeset/**"
Expand All @@ -24,6 +32,7 @@ jobs:
uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false

- name: Setup Node.js
uses: actions/setup-node@v6
Expand All @@ -34,18 +43,67 @@ jobs:
- name: Install dependencies
run: npm ci

- name: Run tests if present
run: npm run test --if-present
- name: Run syntax checks
run: npm run lint

- name: Detect changeset files
- name: Run deterministic tests
run: npm test

- name: Audit runtime dependencies
run: npm audit --omit=dev

- name: Verify package contents
run: node --test test/package-content.test.mjs

- name: Dry-run npm package
run: npm pack --dry-run --json --ignore-scripts

- name: Classify Changesets release pull request
id: release_pr
if: >-
github.event_name == 'pull_request' &&
github.event.pull_request.head.repo.full_name == github.repository &&
startsWith(github.event.pull_request.head.ref, 'changeset-release/') &&
github.event.pull_request.user.login == 'github-actions[bot]'
run: echo "exempt=true" >> "$GITHUB_OUTPUT"

- name: Detect release-impacting and changeset files
id: changesets
shell: bash
run: |
if git diff --name-only origin/main...HEAD | grep -E '^node/\.changeset/.*\.md$' | grep -v 'README.md' >/dev/null; then
git diff --name-only origin/main...HEAD > "$RUNNER_TEMP/crp-changed-files.txt"
if grep -E '^node/\.changeset/.*\.md$' "$RUNNER_TEMP/crp-changed-files.txt" | grep -v -E '^node/\.changeset/README\.md$' >/dev/null; then
echo "present=true" >> "$GITHUB_OUTPUT"
else
echo "present=false" >> "$GITHUB_OUTPUT"
fi
if grep -Eq '^node/(bin|src|ui)/|^node/package(-lock)?\.json$' "$RUNNER_TEMP/crp-changed-files.txt"; then
echo "release_impact=true" >> "$GITHUB_OUTPUT"
else
echo "release_impact=false" >> "$GITHUB_OUTPUT"
fi

- name: Require a minor Changeset for package behavior changes
if: steps.release_pr.outputs.exempt != 'true' && steps.changesets.outputs.release_impact == 'true' && steps.changesets.outputs.present != 'true'
run: |
echo "A minor Changeset is required for package behavior changes." >&2
exit 1

- name: Validate minor Changeset state
if: steps.release_pr.outputs.exempt != 'true' && steps.changesets.outputs.present == 'true'
shell: bash
run: |
status_file="$RUNNER_TEMP/crp-changeset-status.json"
npm run changeset -- status --since=origin/main --output "$status_file"
CHANGESET_STATUS_FILE="$status_file" node --input-type=module <<'NODE'
import { readFileSync } from "node:fs";

- name: Validate changeset state
if: steps.changesets.outputs.present == 'true'
run: npm run changeset -- status --since=origin/main
const status = JSON.parse(readFileSync(process.env.CHANGESET_STATUS_FILE, "utf8"));
const release = status.releases?.find((entry) => (
entry.name === "@cluic/codex-remote-proxy"
));
if (!release || release.type !== "minor") {
process.stderr.write("The package requires a minor Changeset.\n");
process.exit(1);
}
NODE
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,12 @@ coverage/
/scripts/__pycache__/
/node/node_modules/
/node/.npm/
/output/
/.playwright-cli/
/crp-web-product.zip
/local_docs/
/local_docs
/python/
/__pycache__/
.superpowers/
.worktrees/
77 changes: 77 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# AGENTS.md

## Project Map

- CLI and supervisor entrypoint: `node/bin/crp.mjs`
- Proxy worker: `node/src/server.mjs`
- Provider, credential, and control-plane modules: `node/src/`
- Local Web management UI: `node/ui/` (target architecture)
- Tests: `node/test/`

## Working Rules

- Read this file before editing.
- Implement only within the user-approved scope.
- Keep Codex `model_provider` and the proxy address stable; provider switching belongs inside CRP.
- Never return, log, capture, or commit complete API keys.
- Update affected API, data, permissions, UI/UX, testing, status, and handoff documentation with behavior changes.
- Do not run parallel writable agents without exact scopes and no-edit areas.
- When the user asks to conserve root-agent context, delegate repository reconnaissance and review to a subagent.
- Record reusable work mistakes as one concise required or prohibited sentence.
- Clear secret state and its current DOM value before validation, requests, or re-rendering.
- Cancel stale asynchronous focus callbacks and preserve newer user focus.
- Browser fixtures must mirror production enum values and response contracts.
- Temporary-resource checks must stay within the current `$TMPDIR`; traversing all of `/var/folders` is prohibited.
- Temporary-resource checks must target task-specific glob paths instead of recursively scanning every `$TMPDIR` entry.
- Package-content tests must compare the exact reviewed allowlist.
- CI native-backend gates must probe the intended platform service and must not accept fallback storage.
- Tests must import only declared direct dependencies, and every checkout before pull-request code must set `persist-credentials: false`.
- Secret-bearing negative tests must assert absence before equality so a RED failure cannot print the sentinel.
- CLI human-output tests must set an explicit locale instead of inheriting the developer environment.
- Deterministic loopback D1 evidence must not be reported as native-keyring or external-upstream D2 completion.
- macOS native-keyring tests must isolate CRP paths through `CRP_HOME` while preserving the real `HOME` required to access the login Keychain.
- Read-only diagnostics must not run tests or commands that create temporary resources.
- Detached Supervisor startup failures must preserve only strictly allowlisted errors and must not collapse into readiness timeouts.
- Sensitive-diff scans must use separately quoted simple patterns; nested shell quoting in one composite regex is prohibited.
- Shell search patterns containing backticks must be single-quoted so command substitution cannot occur.
- Automatic first-provider selection must use compare-and-set and must not start or reconfigure the Worker.
- Partial-commit tests must encode deterministic resource order in fixture names instead of relying on filesystem traversal order.
- Public numeric summaries must enforce explicit lower and upper bounds; safe-integer checks alone are insufficient.
- Parallel-write coordination paths must exactly match the files assigned to each agent before writing begins.
- Production recovery adapters must inject every inspection and execution method required by the lower-level recovery contract.
- Any Codex configuration lock must make public readiness false until bootstrap safely resolves it.
- A multi-resource repair may modify only snapshots that were durably backed up in the same attempt.
- Recoverable deletion must use a fixed discoverable intermediate marker; a random claim alone is prohibited.
- Exact Codex provider inspection and patching must share one semantic statement scanner.
- The target config hash must be rechecked before history writes and before pending state is cleared.
- Rollout metadata must be durable before rename, and final verification must fsync every affected parent directory.
- Every committed `pending: true` failure must preserve a discoverable marker or retain the Codex config lock.
- Unexpected Worker recovery must run inside the strict Codex readiness gate and recheck its cancellation generation before spawning.
- When a workspace read returns `EPERM`, rerun the required command with sandbox escalation before attributing it to macOS privacy controls.
- Verify a prior writer's actual status before spawning a replacement for a temporarily missing agent-list entry.
- Response-start metrics must be measured at the first non-empty response body chunk, not when response headers arrive.
- Initialization effects must not depend on state they mutate when cleanup can abort bootstrap.
- Programmatic dialog transitions must cancel stale focus restoration and prioritize explicit autofocus.
- Closed off-canvas navigation must leave the focus and accessibility order.
- Setup selection must use no-start compare-and-set; stopped-worker activation must disclose that it starts the Worker.
- Visually hidden tables must be clipped by a non-table wrapper.
- Metrics storage limits must accommodate every valid maximum-cardinality document.
- Temporary release-smoke cleanup must stay inside its exact `$TMPDIR` root and use an allowed bounded filesystem operation.
- Session-bootstrap routes must compare the raw request target, and tests must preserve non-canonical targets.
- Asynchronous catalog refresh must preserve nonempty manual selections and clear provider-scoped catalogs before switching.
- Zsh scripts must not use `path` as a variable name because it overrides the executable search path.

## Required Checks

- Current test suite: `cd node && npm test`
- Runtime dependency audit: `cd node && npm audit --omit=dev`
- Future UI-bearing changes: run browser E2E and retain visual evidence.
- Future credential, config migration, or lifecycle changes are L3 and require expert confirmation.

## Done Means

- Relevant deterministic checks pass.
- Changed behavior has tests or a documented reason.
- Sensitive values are absent from logs, API responses, fixtures, and diffs.
- Affected public documentation reflects the resulting facts.
- The diff contains no unrelated changes and its merge risk is classified.
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2026 Cluic

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Loading
Loading