Hardens the macOS bar launch.json descriptor against untrusted/foreign-owned files and constrains the decoded descriptor; blocks dashboard file writes to the launch descriptor.
Adds opt-in config.runtime.outputLimits to inject MAX_MCP_OUTPUT_TOKENS/BASH_MAX_OUTPUT_LENGTH into the spawned CLI; defaults unchanged when unset. Closes#231.
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.
The dropdown height defaulted to screenCap before the content was measured,
and the ScrollView frame had no alignment, so an unmeasured panel rendered at
nearly full screen height with the content centered in it (macOS then centered
the oversized window). Default to a modest height until measured and pin the
frame to .top, so the popover anchors under the menu bar and sizes to its
content, scrolling only on genuine overflow.
The app now port-probes for a live CCS server and, if none is found,
starts the recorded detached server itself (launch.json, with a login-shell
fallback) before connecting, so double-clicking the app works without the
CLI. The offline panel gains a Start CCS action and a starting state. The
dropdown is sized to its measured content up to a screen-aware cap instead
of a fixed maxHeight, so it no longer collapses and clips rows.
Refs #1526, #1527
ccs bar now spawns the web-server as a detached background process and
exits immediately instead of hosting it in the foreground, so closing the
terminal no longer takes the bar offline. Adds 'bar serve' (the long-lived
host), 'bar stop', and 'bar status', and records ~/.ccs/bar/launch.json at
install so the app can start the server without a shell PATH.
Refs #1526
Generate AppIcon.icns from the CCS mark at package time (sips + iconutil)
and reference it via CFBundleIconFile so Finder, Spotlight, and the Dock
show the brand icon instead of a blank bundle.
Refs #1525
Use version-aware CLIProxy Plus asset names so current
no-plugin releases install without breaking older fallback releases.
Tag latest-version caches with their GitHub repo and ignore
legacy or cross-repo cache entries.
Closes#1522
Some providers (e.g. Kimi, Anthropic-API mirrors) reject OpenAI-format
chat-completions requests and/or only accept requests from a recognized
coding-agent User-Agent (e.g. Claude Code, Roo Code, Kilo Code). The
OpenAI-compat proxy previously translated every profile's request to
OpenAI format and overwrote the User-Agent with a fixed sentinel,
which made these providers unreachable.
This change adds an opt-in Anthropic passthrough mode:
- New CCS_OPENAI_PROXY_PASSTHROUGH=1 env var on a profile opts it in.
- The base URL is auto-detected as Anthropic-style for known hosts
(api.kimi.com, api.minimax.com, api.anthropic.com) or any base URL
ending in /v1.
- In passthrough mode the proxy forwards the incoming Anthropic body
verbatim to the upstream /v1/messages endpoint, preserving the
original User-Agent (or x-stainless-user-agent) so coding-agent
provider checks pass.
- The Anthropic-format response is streamed back unchanged.
Adds:
- isAnthropicPassthroughProfile() + passthrough option on
resolveOpenAIChatCompletionsUrl/resolveOpenAIModelsUrl
- CCS_OPENAI_PROXY_PASSTHROUGH env var on OpenAICompatProfileConfig
- readRawBody() helper for the passthrough path
- Preserved User-Agent (or x-stainless-user-agent) on the upstream
request, falling back to CCS-OpenAI-Compat-Proxy/1.0
- Skip SSE response transformation in passthrough mode (upstream
already returns Anthropic-format bytes)
Tests:
- 9 new tests in upstream-url.test.ts covering auto-detection and the
passthrough URL contract
- 2 new tests in profile-router.test.ts covering the env var
Verified end-to-end against api.kimi.com: a request through the
modified proxy returned a real Kimi response (model kimi-k2p7-coding)
with the original claude-cli/2.1.170 User-Agent preserved.
- create-command: show the pool onboarding hint only after profile
creation fully succeeds; the pre-create placement burned the
once-per-install dismissal when creation failed or rolled back
- remote env (claude): skip the read-level stale-pin filter once the
migration marker exists - a post-migration pin is user-intentional
(e.g. an explicit --config pick equal to a historical default)
- unified-config loader: document that loadOrCreateUnifiedConfig never
writes to disk, so read paths on legacy installs stay side-effect free
Legacy profiles.json-only installs get pool guidance exclusively from
ccs doctor (once-per-install semantics); printing a migrate notice on
every account add nagged with no dismissal available. Also document why
type === 'account' profiles are Claude by schema in the onboarding
counter, so the filter is not misread as over-counting.
- pool --enable/--disable: refuse remote targets with manual config
guidance; resolve the lifecycle port instead of assuming 8317
- enablePoolRouting: roll back the pool flag when config regeneration
fails; already-enabled path re-runs regeneration (repairable state)
- pool opt-in prompt: gate on hasUnifiedConfig (legacy installs skip);
never auto-accept consent under --yes/CCS_YES
- order show (file mode): render selector pick order incl. residual
on-disk priorities and surface drift instead of alphabetical order
- order --reset: clear residual priority fields via management-API
PATCH when proxy runs, atomic direct write when stopped
- quota pool section: classify in-proxy 429 cooldowns as cooling with
reset times (graceful degradation when proxy or endpoint is absent);
honest paused label plus resume hint
- routing state: remote targets report pool as not manageable instead
of echoing the local flag; strategy/affinity apply warns when pool
routing overrides the change (CLI, API message, and dashboard)
- claude model-neutral: --config explicit pins survive the stale-pin
migration; remote env path filters historical default pins read from
claude.settings.json without mutating the file
- cross-lane guard: also checks account-profile CLAUDE_CONFIG_DIR lanes
for email overlap, not only the ambient ~/.claude login
- onboarding hint: opt-in copy naming ccs cliproxy pool --enable
- dashboard card: pool-ON shows drain-order pointer, pool-OFF shows the
enable command, local-only note for remote proxies (i18n x5)
- ccs cliproxy quota: per-provider Pool context section with routing
mode (pool on/off, strategy, affinity, effective retry cap), drain
order resolved exactly as the selector picks (incl. residual on-disk
priorities reordering display + drift warning), and per-account state
named distinctly: available / cooling-until-<time> / paused - these
produce different client errors so they read differently
- cooling visibility degrades honestly when pool cooling is disabled
(no cooldown data exists; renderer says so)
- dashboard routing card: pool badge + retry cap + drain-order hint in
compact and full variants via additive poolRouting state block
- no Bar summary fields, no per-session pin introspection (cut per
plan); policy_quota_unsupported semantics untouched
Part of #1464 account pools (phase 6).
- ccs doctor, native account launch, and ccs auth create now surface a
one-line pool suggestion when >=2 native Claude profiles exist and no
pool is enabled; shared once-per-install dismissal; TTY-gated; hint
failures can never break a launch (guarded, single config read)
- credential import from native profiles evaluated and REJECTED:
refresh tokens are client_id-bound and not replayable cross-client;
pool setup requires fresh OAuth per account (verdict report in plan)
Part of #1464 account pools (phase 5).