Commit Graph
1475 Commits
Author SHA1 Message Date
Kai (Tam Nhu) TranandGitHub 609bed0c39 fix(codex): normalize native cliproxy tuning aliases (#1254) 2026-05-15 09:51:42 -04:00
Tam Nhu Tran 5b664d2105 hotfix(codex): tolerate BOM in Codex TOML config 2026-05-14 13:13:55 -04:00
Kai (Tam Nhu) TranandGitHub 4748c452bd fix(codex): self-heal ccsxp cliproxy provider (#1243) 2026-05-14 12:35:31 -04:00
walker1211andGitHub 35b6210cd2 fix: support Claude subcommands and background sessions
Squash merge PR #1240.\n\nValidated locally from the PR head because trusted-author GitHub CI was skipped for this fork PR:\n- bun run validate\n- bun run build:all\n- bun run test:all\n- CCS_E2E_SKIP_BUILD=1 bun run test:e2e
2026-05-14 10:18:23 -04:00
Tam Nhu Tran 1932b2ca61 fix(proxy): strip stale encoding from upstream error responses 2026-05-13 09:13:28 -04:00
Kai (Tam Nhu) TranandGitHub 667b1d1e1f fix: ignore internal instance directories in health scans
Closes #1236
2026-05-12 22:17:16 -04:00
Kai (Tam Nhu) TranandGitHub 349db830df fix(settings): confine dashboard settings paths (#1231) 2026-05-12 18:13:26 -04:00
Kai (Tam Nhu) TranandGitHub a840793a9b fix(proxy): avoid exposing local auth token in daemon argv (#1230) 2026-05-12 18:13:20 -04:00
Kai (Tam Nhu) TranandGitHub 9c5d4976c5 fix(windows): pin cmd shell for wrapper launches (#1229) 2026-05-12 18:13:13 -04:00
Tam Nhu Tran c2b00b7ad6 feat(dispatcher): pass through Claude subcommands without interactive-session args
Closes #1218.

When the user invokes `ccs <profile> agents` (and other Claude subcommands
like `mcp`, `doctor`, `plugin`, ...), CCS was unconditionally injecting
session-only Claude flags (`--append-system-prompt`, `--disallowedTools`,
`--settings`, official-channels plugin specs) and forwarding the
`DISABLE_TELEMETRY=1` env var from profile settings. Each of those
either errored out the subcommand (`error: unknown option
'--append-system-prompt'`) or silently flipped Claude into
non-interactive list mode, so the new `claude agents` agent view never
opened under CCS.

Add a small `claude-subcommand-detector` that recognizes the documented
Claude subcommand set after skipping known value-taking flags, then have
the three steering-prompt injectors (websearch, image-analysis, browser),
the cliproxy and settings launchers, and the official-channels plan
short-circuit when a subcommand invocation is detected. Also strip
`DISABLE_TELEMETRY` from the spawned env only for subcommand
invocations — upstream Claude Code uses that var as a kill switch for
the subcommand TUIs, and the user's telemetry preference still applies
to every normal interactive session.

Verified end-to-end against `ccs glm agents` and `ccs ck agents`: the
agent view opens correctly, no `unknown option` error, and all 1938
existing tests pass.
2026-05-12 16:35:37 -04:00
Kai (Tam Nhu) TranandGitHub d61469edcb fix(config): bind dashboard to loopback by default 2026-05-12 10:59:49 -04:00
Kai (Tam Nhu) TranandGitHub f09cdfcf2b fix(security): protect dashboard WebSocket upgrades 2026-05-12 10:42:05 -04:00
Kai (Tam Nhu) TranandGitHub bd588a271d fix(codex): require local access for dashboard config routes 2026-05-12 10:24:25 -04:00
Kai (Tam Nhu) TranandGitHub 29da75c0d4 fix(cliproxy): explain headless Codex OAuth recovery
Closes #1213
2026-05-11 14:25:34 -04:00
Tam Nhu Tran a983332016 refactor(web-server): modularize shared-routes and Windows-aware symlink status 2026-05-10 22:15:33 -04:00
Kai (Tam Nhu) TranandGitHub 248b6b735d Merge pull request #1200 from kaitranntt/kai/feat/1199-shared-resource-controls
feat(auth): add shared resource controls
2026-05-10 22:01:57 -04:00
Tam Nhu Tran 31076dff3e fix(web-server): gate Gemini/AGY dashboard OAuth on Plus credential availability
The dashboard Add Account flow hits POST /api/cliproxy/auth/:provider/start-url
which fetches the OAuth consent URL directly from the CLIProxy Plus
management API. With cliproxy.backend=plus and unset
CLIPROXY_{GEMINI,ANTIGRAVITY}_OAUTH_CLIENT_ID/SECRET, Plus returns a URL
with empty client_id= and Google rejects with 400 invalid_request. PR
#1131 only guarded the CLI /start path; /start-url was unguarded.

- Pre-fetch guard via getPlusOAuthCredentialError -> 400
  plus_oauth_credentials_missing before contacting the Plus binary.
- Post-fetch guard via getPlusAuthUrlCredentialError -> 502
  plus_oauth_url_missing_client_id if Plus still emits a URL without
  client_id (logged with query string redacted).
- New integration test (cliproxy-auth-routes-oauth-guard.test.ts) covers
  gemini/agy guard firing, non-table providers passing through, and both
  error body contracts. 15 cases pass.

Closes #1208
2026-05-10 21:55:08 -04:00
Tam Nhu Tran 4ba9df5425 fix(cliproxy/auth): generalize Plus OAuth credential guards for gemini and agy
Refactor getGeminiPlusOAuthCredentialError / getGeminiAuthUrlCredentialError
behind a provider-table-driven helper (PLUS_OAUTH_ENV_BY_PROVIDER) and add
getPlusOAuthCredentialError / getPlusAuthUrlCredentialError exports covering
both gemini and agy. Existing gemini-named exports preserved as aliases so
PR #1131 test surface remains unchanged. New AGY-parity test cases mirror
the original gemini diagnostics. CLI call sites in triggerOAuth /
handlePasteCallbackMode stay gemini-only; dashboard handler in the
follow-up commits will use the generalized exports for both providers.

Refs #1208
2026-05-10 21:55:08 -04:00
Tam Nhu Tran 641d492cd6 feat(cliproxy): add OAuth callback traceability across profiles
Introduces structured per-phase tracing for OAuth flows so users see
branch-specific error messages instead of a generic "token not found"
fallback.

- New oauth-trace/ module: recorder, redactor, three sinks
  (in-memory ring buffer, verbose stdout, opt-in JSONL file at
  ~/.ccs/logs/oauth-YYYYMMDD.log mode 0o600 via CCS_OAUTH_LOG_FILE=1)
- Branch-specific diagnostics for: URL-not-displayed,
  callback-not-observed, binary-error, token-exchange-error,
  session-expired, token-file-missing; UNKNOWN fallback preserves
  prior UX
- Trace recorder threaded through oauth-process.ts spawn/stdout/stderr/
  exit lifecycle; SIGINT/SIGTERM cleanup flushes recorder and records
  Cancelled
- Redactor strips code, state, access_token, refresh_token, id_token,
  client_secret, code_verifier, device_code, assertion, subject_token,
  plus Authorization: Bearer headers; covers URL fragments,
  URL-encoded keys, and arrays
- 68 new tests including adversarial regression cases for redactor
  bypass attempts

Phases for paste-callback path and cross-profile failure-matrix
deferred to follow-up #1207.

Closes #1206
2026-05-10 15:12:40 -04:00
Tam Nhu Tran 8b34060294 fix(dashboard): show real shared plugin registry state 2026-05-09 03:18:20 -04:00
Tam Nhu Tran 2a422b3bd3 feat(dashboard): add shared resource controls 2026-05-09 02:29:08 -04:00
Tam Nhu Tran 3b550205dd fix(auth): require resource mode value 2026-05-08 23:36:12 -04:00
Tam Nhu Tran bfe32257b6 fix(auth): reject resource mode outside resources command 2026-05-08 23:29:03 -04:00
Tam Nhu Tran 64e1d1f815 feat(auth): add shared resource controls 2026-05-08 11:15:28 -04:00
Kai (Tam Nhu) TranandGitHub 74d73748ee feat: support Codex fast service-tier aliases
* feat: support Codex fast service-tier aliases

* fix: send Codex fast tier as priority
2026-05-07 15:34:05 -04:00
Kai (Tam Nhu) TranandGitHub 19a50a8dd8 feat: deprecate GitHub Copilot compatibility surfaces (#1196) 2026-05-07 13:06:03 -04:00
Kai (Tam Nhu) TranandGitHub 8b681df455 fix: route Cursor auth through browser polling
Closes #1194
2026-05-07 11:25:17 -04:00
Kai (Tam Nhu) TranandGitHub 61390e5691 fix(cliproxy): diagnose Gemini Plus OAuth credentials
Closes #1131
2026-05-07 11:05:53 -04:00
Kai (Tam Nhu) TranandGitHub 1b5376239f fix: preserve native Claude passthrough args
Closes #1189
2026-05-07 06:14:12 -04:00
Kai (Tam Nhu) TranandGitHub ba18b68494 fix: count CLIProxy OAuth usage in dashboard stats (#1190)
* fix: count CLIProxy OAuth usage in dashboard stats

* fix: fill cliproxy oauth usage details from logs

* fix: keep mixed cliproxy oauth usage details

* fix: avoid aggregate usage enrichment inflation

* fix: cover oauth source prefixes and full log scans

* fix: preserve distinct oauth usage details

* fix: dedupe oauth log usage by auth identity

* fix: drain usage queue and preserve repeated oauth requests

* fix: guard usage queue drain against repeated full batches

* fix(cliproxy): harden oauth usage stats merging
2026-05-06 17:35:00 -04:00
Kai (Tam Nhu) TranandGitHub be9effcce3 feat: clarify account history sync route (#1187)
* feat: clarify account history sync route

* fix: clarify shared context command examples

* fix: report missing bare profile settings
2026-05-05 13:33:04 -04:00
Tam Nhu Tran dc8bbd85e7 fix: route OpenRouter profiles through v1 API 2026-05-05 11:44:58 -04:00
d6b705e6a9 fix(dispatcher): re-inject anthropic auth env for anthropic-compatible api profiles (#1181)
* fix(dispatcher): preserve anthropic auth env for settings profiles on non-proxy path

API profiles whose `ANTHROPIC_BASE_URL` is classified as `'anthropic'`
(anthropic.com, paths containing `/anthropic`, ollama.com) skip the
local OpenAI-compat proxy. The non-proxy launch path stripped
`ANTHROPIC_BASE_URL` / `ANTHROPIC_AUTH_TOKEN` / `ANTHROPIC_API_KEY`
from the subprocess env without re-injecting them, so Claude Code
launched with no routing/auth in `process.env` and failed with
`Not logged in - Please run /login`. The `--settings` env block does
not satisfy Claude Code's auth check.

Pre-existing for `anthropic.com` and `/anthropic` profiles. Newly
broken in v7.77.0 for Ollama Cloud profiles - PR #1175 reclassified
`ollama.com` from `generic-chat-completion-api` to `anthropic`,
moving it from the proxy path onto this broken non-proxy path.

Fix by extending `stripAnthropicRoutingEnv` with an optional
`preserveFrom` parameter. Routing keys present in `preserveFrom`
survive the strip (with values from `preserveFrom`). Settings-type
profiles pass their own `settings.env` as the preserve source so
routing they explicitly supplied is kept while routing leaked from
the parent shell or `global.env` is dropped.

Wired into both call sites:
- `headless-executor.ts` (the `-p` headless executor)
- `settings-flow.ts` (the interactive flow, which then calls
  `execClaude` - whose own `stripAnthropicRoutingEnv` pass on the
  merged env now also takes `envVars` as the preserve source so the
  caller-supplied routing is not stripped a second time before spawn)

Native Anthropic / Bedrock / Vertex profiles are unaffected (they
don't put routing keys in `settings.env`). The OpenAI-compat proxy
path is unaffected because `buildOpenAICompatProxyEnv` overrides
`BASE_URL` / `AUTH_TOKEN` with localhost values and explicitly deletes
`API_KEY` after this strip.

Replaces the Apr 21 defensive double-strip test (which was the
mechanism causing this regression on the interactive path) with a
test asserting the new contract: caller-supplied routing in `envVars`
survives, parent-process routing is still stripped.

* test: harden anthropic settings env preservation coverage

* fix: preserve explicit blank anthropic routing env

---------

Co-authored-by: Tam Nhu Tran <kaitran.ntt@gmail.com>
2026-05-04 20:22:13 -04:00
Tam Nhu Tran 5f05dea5ac fix: harden cursor daemon model fallback 2026-05-04 11:51:22 -04:00
Tam Nhu Tran 9a687fc03c fix: preserve CLIProxy platform arch compatibility 2026-05-04 11:39:47 -04:00
Tam Nhu Tran 2274cef658 fix: preserve arm64 compatibility for CLIProxy assets 2026-05-04 10:44:20 -04:00
juwain 4acc81c082 style: apply prettier formatting 2026-05-04 15:36:37 +03:00
juwain 70bf0806f3 fix: use aarch64 for CLIProxy arm assets 2026-05-04 14:59:14 +03:00
4e2def6769 feat: support ollama cloud anthropic compatible api (#1175)
* feat(droid-provider): support ollama cloud anthropic compatible api

* fix(droid-provider): move ollama.com to anthropic block to prevent model inference override

* fix(droid-provider): move pathname-based generic checks before host-based anthropic check

* test(proxy): cover ollama cloud native routing boundaries

---------

Co-authored-by: Tam Nhu Tran <kaitran.ntt@gmail.com>
2026-05-03 22:28:38 -04:00
Tam Nhu Tran 3862411bb7 fix(cliproxy): harden custom local port handling 2026-05-03 14:07:31 -04:00
Tam Nhu Tran a7c10be72e Merge origin/dev into fix/cliproxy-local-port-config
Resolve the dispatcher and dashboard conflicts from origin/dev while preserving the configured local CLIProxy port behavior. Also harden lifecycle port fallback and wait for the actual stopped port during binary installs.
2026-05-03 13:56:43 -04:00
Kai (Tam Nhu) TranandGitHub 923683bf30 feat(cliproxy): route plus dashboard to maintained fork (#1173) 2026-05-03 13:01:04 -04:00
Tam Nhu Tran b6a49eeab7 fix(config/loader): break runtime cycle from normalizers to channels-runtime
The normalizers extraction in #1168 introduced a circular module-load
chain that crashed the built CLI:

  ccs.js -> errors -> services/logging -> log-config -> config-loader-facade
   -> unified-config-loader -> loader/normalizers -> channels/official-channels-runtime
   -> utils/claude-detector -> utils/shell-executor -> utils/websearch-manager
   -> utils/websearch/hook-env -> utils/websearch/trace
   -> services/logging (mid-load - createLogger undefined) -> CRASH

normalizers only needed three pure helpers from official-channels-runtime
(isOfficialChannelId, normalizeOfficialChannelIds, resolveLegacyDiscord
Selection) but pulled in the whole file's claude-detector / shell-
executor / websearch chain.

Fix: extract those three helpers + OFFICIAL_CHANNEL_IDS into a leaf
module src/channels/official-channels-ids.ts with no runtime deps.
Update normalizers.ts and config-getters.ts to import from the leaf.
official-channels-runtime.ts re-exports from the leaf for callers that
still want the bundled API.

Also revert the over-eager facade-import migration in
src/utils/config-manager.ts (it was importing from config-loader-facade
which re-exports from itself, creating a direct cycle).

Verified: dist/ccs.js boots cleanly (--version returns); test:all
1828/1828 pass; typecheck/lint/format clean.

Refs #1135
2026-05-03 12:20:32 -04:00
Tam Nhu Tran 164dcabd20 Merge remote-tracking branch 'origin/dev' into kai/refactor/1135-structural-maintainability 2026-05-03 12:00:48 -04:00
Tam Nhu Tran 70b5dc247f refactor: merge facade adoption from #1169 into ccs.ts dispatcher 2026-05-03 11:56:38 -04:00
Tam Nhu Tran 05e1c35203 refactor: merge facade adoption from #1169 into executor extraction 2026-05-03 11:54:04 -04:00
Kai (Tam Nhu) TranandGitHub df8efc985e refactor(config): adopt config-loader-facade across codebase (#1169)
refactor(config): adopt config-loader-facade across codebase (#1161)
2026-05-03 11:51:14 -04:00
MolkoandClaude Opus 4.7 509bd5dbef fix(cliproxy): respect configured local port instead of hardcoding 8317
All call sites that spawn or probe CLIProxyApiPlus now read
cliproxy_server.local.port from config via resolveLifecyclePort()
instead of using the hardcoded CLIPROXY_DEFAULT_PORT constant.

- Move resolveLifecyclePort helper to src/cliproxy/config/port-manager.ts
- Fix 7 call sites: ccs.ts, config-command.ts, copilot-executor.ts,
  lifecycle.ts, binary-manager.ts, cliproxy-stats-routes.ts,
  cliproxy-local-proxy.ts
- Remove duplicate resolveLocalCliproxyPort helper
- Cache port resolution in /proxy-status handler to avoid repeated I/O

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-03 07:40:48 -04:00
Tam Nhu Tran 4f6e61739c refactor(config): adopt config-loader-facade across the codebase
Issue #1161. Sweeps 127 files to import from
src/config/config-loader-facade.ts instead of unified-config-loader or
utils/config-manager directly.

WRITE callers (32 files): replaced raw saveUnifiedConfig /
mutateUnifiedConfig / updateUnifiedConfig calls with the facade's
cache-coherent wrappers saveConfig / mutateConfig / updateConfig. This
fixes a latent stale-cache window where direct writes through the
underlying loader bypassed the facade's memoization.

READ callers (95 files): mechanical import-path migration only —
function names unchanged because the facade re-exports them. No
behavior change.

Also updated:
- tests/unit/utils/browser/browser-setup.test.ts (DI interface rename)
- src/management/checks/image-analysis-check.ts (dynamic import rename)
- src/web-server/health-service.ts (dynamic require rename)
- src/ccs.ts (path prefix fix from sweep script)

After sweep: zero raw write callers remain outside src/config/. Direct
imports of config-manager remain only for symbols not in the facade
(getConfigPath, getCcsDirSource, etc). Behavior unchanged; full suite
passes 1824/1824.

Out of scope: switching loadOrCreateUnifiedConfig() callers to
getCachedConfig() — needs per-callsite cache-safety analysis. Tracked
as follow-up.

Refs #1161
2026-05-03 01:42:53 -04:00
Tam Nhu Tran f3e79fd4e8 refactor(config/loader): extract defaults-merger, config-getters, polish orchestrator
Phases 4-6 of #1164. Final extractions and orchestrator cleanup:

- src/config/loader/defaults-merger.ts (323 LOC):
  mergeWithDefaults — pure transform that fills defaults across browser,
  websearch, dashboard auth, image analysis, logging, cursor, continuity,
  official channels.
- src/config/loader/config-getters.ts (339 LOC):
  getWebSearchConfig, getGlobalEnvConfig, getContinuityInheritanceMap,
  getCliproxySafetyConfig, getThinkingConfig, getOfficialChannelsConfig,
  isDashboardAuthEnabled, getDashboardAuthConfig, getBrowserConfig,
  getImageAnalysisConfig, getLoggingConfig, getCursorConfig,
  GeminiWebSearchInfo. Lazy require('../unified-config-loader') call-time
  resolution preserves spy-based test compatibility while breaking the
  config-getters -> orchestrator cycle.
- unified-config-loader.ts: rewritten as 268-LOC orchestrator. Hosts core
  load/save/mutate logic + re-exports of all moved symbols for backwards
  compat with codebase imports.

Forward-reference callbacks in io-locks.ts kept as-is — defaults-merger
and normalizers don't import from io-locks (no cycle), but simplifying
io-locks's internal callback API risks lock-semantics regression.
Deferred per KISS.

unified-config-loader.ts: 1508 -> 268 LOC (-1240, -82%). Behavior
unchanged; full suite passes 1824/1824.

Refs #1164
2026-05-03 01:04:57 -04:00