Google retired the gemini CLI on 2026-06-18, so the gemini websearch fallback no longer
works. Add agy (Antigravity) as the primary CLI websearch provider end to end: runtime spawn
in websearch-transformer.cjs (agy --dangerously-skip-permissions --print-timeout Ns -p),
detection (agy.ts), config schema and defaults, hook env, status, dashboard persistence, and docs.
The legacy gemini provider stays present but is marked deprecated for enterprise users who
retain access. Image analysis already supported the agy provider (no change). The CLIProxy
gemini quota fetcher (Gemini API OAuth, not the cli binary) is untouched.
Validation: typecheck, lint, and the websearch + config + web-server unit suites pass.
Releasing the macOS bar app was fully manual (run package_app.sh on a Mac, then
gh release upload --clobber), so Swift-side changes never reached users until
someone remembered to rebuild and re-upload the floating ccs-bar-latest asset.
Add a tightly-scoped Bar Release workflow that does it automatically:
- triggers ONLY on push to main touching macos-bar/**, or manual dispatch, so
regular PRs, dev pushes, and non-bar changes never start it
- runs ONLY on the dedicated self-hosted macOS runner (label ccs-bar); the Linux
CI runners never match it and it never competes for them
- least-privilege contents:write, single-flight via concurrency
Version is sourced from a new macos-bar/VERSION single-line file (the workflow
reads it; the asset is always the latest build regardless). package_app.sh now
defaults to that file when no version arg is passed, so the local manual path and
CI share one source of truth. Documents the release process in docs/ccs-bar.md.
Redact StageOptions error payloads and summarize debug launch args.
Propagate request IDs through Cursor daemon and dashboard completion logs.
Mark remaining P2/P3 maintainability targets as partial instead of overclaiming.
Epic P7. Locks in the epic's gains with ESLint gates so the adoption work
does not regress.
- ccs/no-new-throw-error (error): custom flat-config rule that flags NEW
throw new Error(...) outside a generated baseline allowlist. Forces the
typed-error taxonomy (src/errors/error-types.ts). Existing 338 sites are
grandfathered in eslint-rules/throw-error-baseline.json; only NEW violations
error. Rule normalizes the filename to repo-root-relative to match baseline
keys regardless of how ESLint reports paths.
- max-lines (warn, 400, skipBlankLines/skipComments): warns on files over
400 LOC (goal of P5/P6 god-file splits). Currently 51 warnings on the
not-yet-split god-files (P6 territory).
- scripts/generate-throw-error-baseline.js: emits the allowlist from raw
source (superset of real throws; never undercounts). Run after intentionally
grandfathering a site, or quarterly to prune.
- tests/unit/eslint-rules/no-throw-new-error.test.ts: rule logic (flags
off-allowlist, passes on-allowlist/typed/rethrow, line-sensitivity).
- docs/code-standards.md: Lint Enforcement Gates section.
- docs/logging-contract.md: error.code -> ExitCode table (from P4).
validate + validate:ci-parity green.
Epic P4. Migrates plain throw new Error to the typed-error classes in the four
locked subdomains, and makes the taxonomy erasable-syntax-compatible so it can
be adopted across UI-reachable code.
Migration (cliproxy/auth, web-server/routes, auth):
- 21 of 23 throws in the locked subdomains now use typed subclasses
(ProfileError, AuthError, ConfigError, ValidationError, ProviderError).
- Typed adoption in locked subdomains: 0/23 -> 21/23 (91.3%), > 40% target.
- Overall typed adoption: 0.9% -> 8.6%.
- Messages preserved exactly (message-based tests stable). Exit codes now
differentiate via handleError (ProfileError=7, AuthError=4, ConfigError=2,
ProviderError=6). ccs doctor 0/1 contract untouched (outside scope).
Erasable-syntax fix (unblocks the migration in the UI build graph):
- exit-codes.ts: enum ExitCode -> const object + union type (value and type
usage both preserved; no Object.values(ExitCode) consumers).
- error-types.ts: constructor parameter properties -> explicit readonly field
declarations + body assignment.
- The web UI build enforces erasableSyntaxOnly (ui/tsconfig.app.json) and
reaches src/errors via the @shared -> src/auth graph; pre-erasable
error-types blocked the build once profile-registry adopted typed errors.
Compat audit: docs/reports/typed-error-exit-code-compat-audit.md (Q1 resolved:
migrate freely; only documented contract is ccs doctor, which is untouched).
Behavior-lock: src/errors/__tests__/typed-error-migration-exit-codes.test.ts
(taxonomy -> exit-code mapping, instanceof chains, context fields).
validate + validate:ci-parity green (incl. UI build).
Preserves the Gatekeeper quarantine attribute on the installed CCS Bar app so the user makes the right-click to Open trust decision, instead of silently clearing it.
Sets undici headersTimeout/bodyTimeout to request_timeout+30s so the AbortController is the single authority on upstream request lifetime, preventing premature socket closes on slow self-hosted upstreams. Verified undici v5 ProxyAgent object-signature compat.
Reflect the seamless-launch change: the app self-starts the background
server, ccs bar runs it detached, and add the serve/stop/status commands
plus the launch.json/server.pid/serve.log files.
'ccs bar install' previously ended with two manual steps: clearing the
Gatekeeper quarantine by hand and running 'ccs bar' separately.
Install now detects an existing installation and says so before
reinstalling, clears the quarantine attribute itself via execFile with
a graceful fallback to the printed hint when xattr fails, and ends with
a TTY-aware 'Launch CCS Bar now?' prompt (default yes) that hands off
to the existing launch flow. --launch forces the handoff and
--no-launch suppresses it for scripted installs; non-TTY runs skip the
prompt and print the manual command instead.
Closes#1504
Add a 'Get CCS Bar' promo banner + card to the dashboard (mirroring the
OpenRouter promo pattern + design system) so users discover the macOS menu-bar
app, with a macOS-aware install CTA. Add a user-facing docs/ccs-bar.md covering
what it is, install via 'ccs bar install', launch, what it shows, uninstall,
and the loopback requirement -- closing the docs-sync gap for the new ccs bar
command.
Adds opt-in `ccsx auth import-default <name>` to migrate the existing
~/.codex/auth.json into a new profile, plus the cross-system integration
tests and user-facing documentation.
- import-default-command (C3 torn-write protection):
- readFileSync + JSON.parse with 3x retry / 100ms backoff to survive
Codex's truncate-then-write auth.json refresh race
- decode-id-token sanity-check on JWT shape (catches mid-write JWT
corruption that JSON.parse alone wouldn't notice)
- pgrep -f codex best-effort detection; warns + refuses without
--force-while-running flag if a live codex process is found
- rejects cliproxy-format auth files ({type: "codex", ...} wrapper)
with a clear "use ccs cliproxy ..." pointer
- atomic write to <dest>.tmp.<pid>.<rand> + rename
- --with-history defaults to false per D8 (auth-only is the safer
default; opt in for bulkier data)
- --force backs up existing auth.json to .bak-<ts> before overwrite
- non-destructive — never modifies ~/.codex/; legacy mode keeps
working without ever running this command
- integration tests:
- two-terminal-isolation: two profiles with separate CODEX_HOMEs
write to their own auth.json/history.jsonl with no crosstalk
- ccsxp-independence: codex-auth profile set; ccsxp still uses its
own CCSXP_CODEX_HOME / ~/.codex pool (H5 stderr notice present)
- legacy-fallback: no profiles registered → codex-runtime-router
leaves CODEX_HOME unset → codex falls back to ~/.codex
- import-default.integration: real fs copy + decode + register
- docs/codex-auth.md: user guide covering quick start, two-terminal
example, migration, dashboard, and caveats (cmd.exe, Windows
symlinks, ccsx vs ccsxp distinction)
155 codex-auth-scope tests green (45 Phase 1 + 57 Phase 2 + 19 Phase 3
+ 15 Phase 4 + 19 Phase 5). Full suite 3051/3082 — the 1 failure is a
pre-existing test-pollution issue between ccsxp-runtime.test.ts and
codex-runtime-integration.test.ts that exists on dev today; the test
passes in isolation.
Preserve valid custom model_providers.cliproxy.base_url values during ccsxp Codex provider repair while keeping local fallback repair for missing or invalid URLs.\n\nCloses #1281
Reverts the loop-1 prerelease channel that was publishing npm as
vX.Y.Z-rc.N to the `rc` dist-tag instead of `latest`.
`main` is now a stable semantic-release channel again: every merge
publishes vX.Y.Z immediately to npm @latest. The rc.1 soak window
that guards Docker mutable tags is moved entirely into the Docker
publish workflow:
- `.releaserc.cjs`: remove `prerelease: 'rc'` from productionConfig,
restore `branches: ['main']` (stable). Restore full successComment
and `released` label. Keep loop-1 releaseNotesGenerator additions
(revert section, breaking change comment).
- `docker-release.yml`: every `release: published` event publishes
only the immutable `:<ver>` Docker tag. `promote-mutable-tags` job
now gates exclusively on `workflow_dispatch` with
`promote_to_latest=true` — no longer triggered automatically by
non-prerelease release events.
- `promote-release.yml`: rewritten as a dispatch wrapper that validates
the stable tag exists and is not a prerelease, verifies the immutable
Docker image is in the registry, then dispatches docker-release.yml
with `promote_to_latest=true`. Removes the `gh release edit
--prerelease=false` approach that required the rc soak to be wired
through GitHub release state.
- `docs/release-process.md`: updated to reflect the decoupled model —
npm @latest is immediate; Docker :latest requires manual promote after
soak. Documents the `why` split between npm and Docker soak windows.
* docs: hoist Docker zero-install quickstart above npm install path
- Create docs/quickstart-snippet.md as canonical source for the
two-command flow (curl + docker compose up -d), wrapped in
<!-- quickstart-snippet-start/end --> markers
- Hoist the snippet into README.md immediately below the deprecation
banner, above all other install paths
- Rename old npm-only "## Quick Start" to "## Install on Host (npm)"
and move it below the Docker quickstart
* docs(docker): restructure README with zero-install first and migration section
- Reorder top-level sections: zero-install (canonical snippet with
markers), choosing an image, power-user ccs docker, prebuilt image,
connect your app to CLIProxy, migration, env vars, troubleshooting
- Add deprecation banner at the top pointing at the migration section
- Add ## Migration from ccs-dashboard:latest section with step-by-step
instructions covering compose down, data preservation, named volume
vs bind-mount path, and compose up with the new image
- Keep P1's Choosing an image table and P5's Connect Your App to
CLIProxy section intact, just repositioned
* test(docs): parity check for quickstart snippet across README files
Assert README.md and docker/README.md both contain the canonical
quickstart block verbatim, anchored by marker comments. Exits non-zero
and prints a diff on any drift.
* ci(docs): wire quickstart-parity test on push and PR
Runs tests/docs/quickstart-parity.sh on self-hosted runner whenever
docs/quickstart-snippet.md, README.md, docker/README.md, or the
test/workflow files themselves change. Fails fast on snippet drift.