24 Commits
Author SHA1 Message Date
Tam Nhu Tran abc3002e89 fix(websearch): honor disabled steering per launch 2026-07-29 13:22:21 -04:00
minhbi245 54a2b4759b feat(cliproxy): add xAI Grok provider 2026-07-17 00:02:12 +07:00
Tam Nhu Tran 819d43914a fix(dispatch): restore copilot and legacy-cursor arg passthrough
Routing every non-flag token after 'copilot'/'legacy-cursor' to the subcommand
handler turned the legacy profile/prompt passthrough into an 'Unknown subcommand'
error. Route only known subcommands (matching prior behavior); other args pass
through to the copilot/cursor bridge flow. Bar keeps its unknown-subcommand exit
code (it has no passthrough).
2026-06-22 13:10:59 -04:00
Tam Nhu Tran 2900743767 fix(dispatch): route unknown bar/copilot/cursor subcommands and survive corrupt config
Pre-dispatch only routed known subcommand tokens, so unknown bar/copilot/cursor
subcommands never reached their handlers (no error, no non-zero exit). Route them
through. Also wrap autoMigrate() so a corrupt config.yaml no longer crashes
startup, and print only the first line of a best-effort recovery failure.
2026-06-22 12:06:58 -04:00
Kai (Tam Nhu) Tran 1a0398579a fix: degrade WebSearch launch provisioning failures (#1571) 2026-06-18 22:56:20 -04:00
Tam Nhu Tran 87aeb8f193 feat(logging): P3 hotpath console.error migration + redaction gate (928->267)
Epic P3. Migrates hotpath console.error/warn to the structured logger
(diagnostics) or process.stderr.write (user-facing), and adds a redaction
safety gate so the migration cannot leak credentials.

Redaction gate (MR1):
- log-redaction: scrub known credential token shapes (sk-ant, sk-, xoxb,
  ghp, glpat, AIza, JWT bodies, api_key=, Bearer/Basic/Token scheme) in
  string values, Error.message, AND the log message string (defense-in-depth).
- logger: message now passes through maskSecretTokens.

tool-sanitization-proxy: deleted the private file-logging subsystem
(initLogFile/writeLog/log/warn, logFilePath, debugMode); 13 call sites now
route through the existing createLogger('cliproxy:tool-sanitization-proxy').

Sweep (~120 diagnostic -> structured createLogger; ~540 user-facing -> stderr):
- diagnostics converted across proxy, web-server/routes, glmt pipeline, quota
  fetchers, executors, delegation, session-bridge, https-tunnel-proxy.
- user-facing CLI output (flows, arg-parser usage, installers, prompts, adapter
  launch errors, error display) moved to process.stderr.write (preserves stderr).
- src/utils/error-manager.ts reclassified CLI-UX-exempt (user-facing display).

Metric: hotpath console.error 928 -> 267 (71%); createLogger files 35 -> 64.
Residual 267 is user-facing CLI output (not diagnostics); documented in
docs/hardening-debt-burndown.md. Redaction gate makes further conversion safe.

Tests: hotpath-redaction-regression (12 token shapes); updated delegation-handler,
arg-parser, model-warnings spies (console.error -> process.stderr.write).
validate + validate:ci-parity green.
2026-06-18 18:48:12 -04:00
Kai (Tam Nhu) Tran ab6c59ba35 fix(browser): preserve profile-bound DevTools discovery (#1541)
Preserves the profile-bound DevTools port/discovery so browser launches resolve the correct per-profile endpoint.
2026-06-15 23:14:55 -04:00
Kai (Tam Nhu) Tran 473aa082f6 fix: honor default target for Claude passthrough (#1539)
Honors the configured default target in the Claude subcommand passthrough resolver.
2026-06-15 23:12:52 -04:00
Tam Nhu Tran 0343734665 feat(cliproxy): pool onboarding hints for existing multi-profile users
- 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).
2026-06-11 12:09:53 -04:00
Tam Nhu Tran 3fbf850471 fix: forward bare Claude subcommands through the default profile
`ccs agents` exited with "Profile not found: agents" instead of opening
the Claude agent view. The first positional token was treated as a
profile name, so `detectProfileType` threw for any bare Claude subcommand
that is not also a profile. `ccs <profile> agents` (#1218) and
`ccs default agents` already worked; only the bare form was unrouted.

On the profile-not-found path, reroute documented Claude subcommands
(agents, mcp, plugin, project, setup-token, auto-mode, remote-control,
ultrareview, upgrade, install) through the default profile so they launch
`claude <subcommand>` — where the existing launcher already strips
interactive-session args. Gated to the claude target so codex/droid keep
their own subcommand routing, and only reached when no profile of that
name exists, so a real configured profile still wins. CCS commands that
shadow Claude subcommands (doctor, update, auth) are intercepted earlier
by the root-command router and are unaffected.

Closes #1404
2026-05-28 22:21:26 -04:00
Kai (Tam Nhu) Tran 8a37578702 fix(codex): pass native ccsx subcommands through (#1290) 2026-05-18 10:48:44 -04:00
Kai (Tam Nhu) Tran 5a54c1b536 fix(codex): pass ccsx resume through to native codex
Closes #1287
2026-05-18 09:45:20 -04:00
Tam Nhu Tran bf92645b35 feat(codex-auth): add ccsx auth CLI subcommands (create/login/switch/use/show/remove)
Implements the user-facing surface for ccsx auth profile management.
After `ccsx auth create work` (auto-spawns codex login with CODEX_HOME
pinned per D11), users can `eval "$(ccsx auth use work)"` in any shell
to scope all subsequent codex invocations to that profile — letting
two terminals run two different Codex accounts concurrently.

- codex-auth-router: dispatches argv to subcommand handlers
- create: idempotent, --force re-links config.toml preserving auth.json
  (D9), then auto-spawns codex login with CODEX_HOME pinned (D11);
  filesystem ops happen before registry write to avoid registry orphans
  on EACCES/ENOSPC
- login: standalone re-auth for an existing profile
- switch: persistent default in YAML registry
- use: STDOUT-DISCIPLINED — emits only shell-evalable exports;
  bash/zsh/fish/PowerShell/cmd syntaxes via shell-detect; sets
  CCS_NO_PRE_DISPATCH=1 at module load to suppress recovery/migration
  banners that would otherwise contaminate eval (C2)
- show: list (active(missing) row at top per D14) + detail views
- remove: default-profile guard, active-shell warn, --yes / --force
- ASCII-only output, NO_COLOR honored, all errors to stderr via
  exitWithError
- pre-dispatch.ts: early-return when CCS_NO_PRE_DISPATCH=1, placed
  before autoMigrate which is itself a stdout writer

57 unit tests, all green. Help text cross-references the ccsxp/ccsx
distinction since the binaries differ by one character (H5).
2026-05-17 14:44:44 -04:00
Kai (Tam Nhu) Tran 4748c452bd fix(codex): self-heal ccsxp cliproxy provider (#1243) 2026-05-14 12:35:31 -04:00
walker1211 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 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
Tam Nhu Tran 64e1d1f815 feat(auth): add shared resource controls 2026-05-08 11:15:28 -04:00
Wei HeandTam Nhu Tran 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 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
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 0910a75850 refactor(dispatcher): extract per-profile flows from ccs.ts
Phases 5+6 of #1165. Final big extraction: collapses the 6-branch
profileInfo.type switch in main() into a single dispatchProfile call.

- src/dispatcher/dispatcher-context.ts (40 LOC): ProfileDispatchContext type
- src/dispatcher/flows/cliproxy-flow.ts (211 LOC)
- src/dispatcher/flows/copilot-flow.ts (66 LOC)
- src/dispatcher/flows/cursor-flow.ts (54 LOC)
- src/dispatcher/flows/settings-flow.ts (400 LOC)
- src/dispatcher/flows/settings-image-analysis-prep.ts (126 LOC):
  split out per plan to keep settings-flow control flow whole
- src/dispatcher/flows/account-flow.ts (63 LOC)
- src/dispatcher/flows/default-flow.ts (136 LOC)
- src/dispatcher/target-executor.ts (+38 LOC): dispatchProfile switch

settings-flow stays at 400 LOC — flow control (3 pre-flight blocks +
env construction + 2 dispatch paths) is maximally cohesive. Further
splitting would fragment dispatch logic per plan note.

process.exit parity preserved exactly (cliproxy non-claude path uses
process.exitCode=1; return; others use process.exit(1)).

ccs.ts: 1040 -> 170 LOC (-870). Full reduction across phases 1-6:
1775 -> 170 (-1605, -90%). main() body now ~100 LOC of awaited phase
calls. Behavior unchanged; full suite passes 1824/1824.

Refs #1165
2026-05-02 23:52:25 -04:00
Tam Nhu Tran a807de6f4c refactor(dispatcher): extract profile and target detection from ccs.ts
Phase 4 of #1165. Extracts the largest remaining concern in main():
profile detection, target resolution, Claude CLI detection, adapter
lookup, compatibility preflight, binary detection, droid prune, and
per-target arg normalization.

- src/dispatcher/profile-resolver.ts (358 LOC):
  resolveProfileAndTarget + ProfileResolutionContext / ResolvedProfile
  types. Includes droid + codex per-target arg normalization (cohesive
  with profile resolution; splitting would create incomplete extraction).
- 7 unit tests covering default, gemini, codex, unknown profile, droid,
  and compatibility preflight runs.

Compatibility preflight duplication between this phase and the settings
flow (Phase E) is preserved exactly per plan risk note 5 — do not dedupe
in this PR. Module is 358 LOC (over 300 ideal) — kept whole because the
profile resolution + per-target normalization form a single dependency
chain.

ccs.ts: 1262 -> 1040 LOC (-222). Behavior unchanged; full suite passes
1824/1824.

Refs #1165
2026-05-02 23:41:48 -04:00
Tam Nhu Tran 0cf4ad7b48 refactor(dispatcher): extract bootstrap and pre-dispatch handlers from ccs.ts
Phases 2+3 of #1165. Extracts main() bootstrap + pre-dispatch handler
chain into focused dispatcher modules:

- src/dispatcher/cli-argument-parser.ts (+138 LOC):
  bootstrapAndParseEarlyCli + DispatcherBootstrap. Encapsulates UI init,
  --config-dir parse/validate, cloud-sync warnings, completion command
  short-circuit, legacy cursor arg normalization, browser launch flag
  resolution, and native codex passthrough escape.
- src/dispatcher/pre-dispatch.ts (155 LOC):
  runPreDispatchHandlers. Wraps update check, root command router,
  provider help shortcut, copilot/cursor subcommand routing, first-time
  install hint. Returns boolean consumed signal.
- New tests: 12 + 7 covering both modules.

Adapter registration intentionally stays in main() — singleton wiring
with no arg dependency; folding it into bootstrap would conflate
concerns. ccs.ts: 1475 -> 1262 LOC (-213). Behavior unchanged; full
suite passes 1824/1824.

Refs #1165
2026-05-02 23:27:05 -04:00
Tam Nhu Tran f759cfeaaa refactor(dispatcher): scaffold src/dispatcher/ and extract pure helpers from ccs.ts
Phase 1 of #1165. Pure code-motion: moves top-level helpers out of the
1775-LOC CLI entry point into focused dispatcher modules. Zero
behavior change, zero main() changes.

- src/dispatcher/cli-argument-parser.ts (199 LOC):
  DetectedProfile, RuntimeReasoningResolution, NATIVE_CLAUDE_EFFORT
  constants, detectProfile, normalizeLegacyCursorArgs,
  printCursorLegacySubcommandDeprecation, resolveRuntimeReasoningFlags,
  normalizeCodexRuntimeReasoningOverride,
  exitWithRuntimeReasoningFlagError, normalizeNativeClaudeEffortArgs,
  shouldNormalizeNativeClaudeEffort,
  shouldPassthroughNativeCodexFlagCommand,
  getNativeCodexPassthroughArgs.
- src/dispatcher/environment-builder.ts (125 LOC):
  resolveCodexRuntimeConfigOverrides, refreshUpdateCache,
  showCachedUpdateNotification, resolveNativeClaudeLaunchArgs.
- src/dispatcher/target-executor.ts (56 LOC):
  ProfileError, execNativeCodexFlagCommand.

ccs.ts: 1775 -> 1475 LOC (-300). Cleaned up 8 now-orphaned imports.
Behavior unchanged; full suite passes 1824/1824.

Refs #1165
2026-05-02 22:49:45 -04:00