diff --git a/README.md b/README.md index a2f11dbd..77ac455b 100644 --- a/README.md +++ b/README.md @@ -201,17 +201,21 @@ Built-in Droid runtime aliases are installed with the package: ```bash ccs-droid glm # explicit alias ccsd glm # legacy shortcut +ccs-codex # explicit Codex alias +ccsx # short Codex alias ``` Need additional alias names? First create the matching symlink or another launcher that preserves the invoked basename, then map that name with `CCS_TARGET_ALIASES` (preferred) or legacy -`CCS_DROID_ALIASES`: +target-specific env vars: ```bash ln -s "$(command -v ccs)" /usr/local/bin/mydroid -CCS_TARGET_ALIASES='droid=mydroid' +ln -s "$(command -v ccs)" /usr/local/bin/mycodex +CCS_TARGET_ALIASES='droid=mydroid;codex=mycodex' # Legacy fallback still supported: CCS_DROID_ALIASES='mydroid' +CCS_CODEX_ALIASES='mycodex' ``` For Factory BYOK compatibility, CCS also stores a per-profile Droid provider hint @@ -239,6 +243,30 @@ flag and warns about duplicates. Dashboard parity: `ccs config` -> `Factory Droid` +### Native Codex Runtime (runtime-only in v1) + +CCS can launch native Codex as a first-class runtime target without rewriting your +`~/.codex/config.toml` on every run. CCS uses transient `codex -c key=value` overrides for +Codex-routed sessions and leaves your existing Codex home/config in place. + +Supported in v1: + +```bash +ccs --target codex # native Codex default session +ccs-codex # explicit Codex alias +ccsx # short alias +ccs codex --target codex # built-in CLIProxy Codex on native Codex +ccs api create codex-api --cliproxy-provider codex +ccs codex-api --target codex # Codex bridge profile on native Codex +``` + +Not supported in v1: +- Claude account profiles on Codex target +- Copilot profiles on Codex target +- Generic API profiles that are not Codex-routed CLIProxy bridges +- Non-Codex CLIProxy providers on Codex target +- Composite CLIProxy variants on Codex target + ### Per-Profile Target Defaults You can pin a default target (`claude` or `droid`) per profile: diff --git a/docs/codebase-summary.md b/docs/codebase-summary.md index 2f35dd80..acc97821 100644 --- a/docs/codebase-summary.md +++ b/docs/codebase-summary.md @@ -1,8 +1,8 @@ # CCS Codebase Summary -Last Updated: 2026-03-24 +Last Updated: 2026-03-28 -Comprehensive overview of the modularized CCS codebase structure following the Phase 9 modularization effort (Settings, Analytics, Auth Monitor splits + Test Infrastructure), v7.1 Remote CLIProxy feature, v7.2 Kiro + GitHub Copilot (ghcp) OAuth providers, v7.14 Hybrid Quota Management, v7.34 Image Analysis Hook, account-context validation hardening, and Official Claude Channels runtime support. +Comprehensive overview of the modularized CCS codebase structure following the Phase 9 modularization effort (Settings, Analytics, Auth Monitor splits + Test Infrastructure), v7.1 Remote CLIProxy feature, v7.2 Kiro + GitHub Copilot (ghcp) OAuth providers, v7.14 Hybrid Quota Management, v7.34 Image Analysis Hook, account-context validation hardening, Official Claude Channels runtime support, and native Codex runtime target support. ## Repository Structure @@ -35,6 +35,9 @@ The main CLI is organized into domain-specific modules with barrel exports. ``` src/ ├── ccs.ts # Main entry point & profile execution flow +├── bin/ # Dedicated runtime entrypoints +│ ├── droid-runtime.ts # argv[0] shim for ccs-droid / ccsd +│ └── codex-runtime.ts # argv[0] shim for ccs-codex / ccsx ├── types/ # TypeScript type definitions │ ├── index.ts # Barrel export (aggregates all types) │ ├── cli.ts # CLI types (ParsedArgs, ExitCode) @@ -67,8 +70,12 @@ src/ │ ├── target-adapter.ts # TargetAdapter interface contract │ ├── target-registry.ts # Registry for runtime adapter lookup │ ├── target-resolver.ts # Resolution logic (flag > config > argv[0]) +│ ├── target-metadata.ts # Runtime vs persisted target metadata and alias lists +│ ├── target-runtime-compatibility.ts # Guardrails for target/profile combinations │ ├── claude-adapter.ts # Claude Code CLI implementation │ ├── droid-adapter.ts # Factory Droid CLI implementation +│ ├── codex-adapter.ts # Native Codex CLI implementation +│ ├── codex-detector.ts # Codex binary detection and capability probing │ ├── droid-detector.ts # Droid binary detection & version checks │ └── droid-config-manager.ts # ~/.factory/settings.json management │ @@ -203,7 +210,7 @@ src/ | Category | Directories | Purpose | |----------|-------------|---------| | Core | `commands/`, `errors/` | CLI commands, error handling | -| Targets | `targets/` | Multi-CLI adapter pattern (Claude Code, Factory Droid, extensible) | +| Targets | `bin/`, `targets/` | Multi-CLI adapter pattern (Claude Code, Factory Droid, Codex CLI, extensible) | | Auth | `auth/`, `cliproxy/auth/` | Authentication across providers | | Config | `config/`, `types/` | Configuration & type definitions | | Providers | `cliproxy/`, `copilot/`, `glmt/` | Provider integrations plus retained legacy transformer internals | @@ -238,6 +245,17 @@ src/ - Runtime contract lives in `src/channels/official-channels-runtime.ts` and is consumed from `src/ccs.ts`, `src/commands/config-channels-command.ts`, and `src/web-server/routes/channels-routes.ts`. - Canonical config lives under `channels.*` in `~/.ccs/config.yaml`; legacy `discord_channels.*` remains read-compatible only when canonical fields are absent. + +### Native Codex Runtime Target + +- Runtime aliases: `ccs-codex` and `ccsx` resolve through `src/bin/codex-runtime.ts` and `src/targets/target-resolver.ts`. +- Metadata boundary: `src/targets/target-metadata.ts` keeps Codex runtime-only in v1, so persisted default targets remain `claude | droid`. +- Compatibility guardrails: `src/targets/target-runtime-compatibility.ts` centralizes which profile types can execute on Codex. +- Adapter behavior: `src/targets/codex-adapter.ts` and `src/targets/codex-detector.ts` launch native Codex without rewriting `~/.codex/config.toml`; CCS-backed routes use transient `codex -c key=value` overrides and env-key injection. +- Supported Codex flows in v1: + - `default` + - CLIProxy provider `codex` + - settings/API profiles only when they resolve to a Codex CLIProxy bridge - Telegram and Discord bot tokens are intentionally written into Claude-managed machine state under `~/.claude/channels//.env`, unless the official `*_STATE_DIR` environment override redirects that channel elsewhere. - iMessage is tokenless, macOS-only, and still depends on Claude-side plugin install plus OS permissions. - Auto-enable is gated on Bun availability, verified Claude Code v2.1.80+, verified `claude.ai` auth, native Claude `default/account` sessions, and per-channel setup readiness. diff --git a/docs/system-architecture/target-adapters.md b/docs/system-architecture/target-adapters.md index 23a29a97..a390cc23 100644 --- a/docs/system-architecture/target-adapters.md +++ b/docs/system-architecture/target-adapters.md @@ -1,6 +1,6 @@ # Target Adapters -Last Updated: 2026-02-16 +Last Updated: 2026-03-28 Detailed documentation of the target adapter pattern and implementations. @@ -20,8 +20,8 @@ Each CLI target implements the `TargetAdapter` contract: ```typescript export interface TargetAdapter { - readonly type: TargetType; // 'claude' | 'droid' - readonly displayName: string; // "Claude Code" | "Factory Droid" + readonly type: TargetType; // 'claude' | 'droid' | 'codex' + readonly displayName: string; // "Claude Code" | "Factory Droid" | "Codex CLI" /** Detect if the target CLI binary exists on system */ detectBinary(): TargetBinaryInfo | null; @@ -30,7 +30,15 @@ export interface TargetAdapter { prepareCredentials(creds: TargetCredentials): Promise; /** Build spawn arguments for the target CLI */ - buildArgs(profile: string, userArgs: string[]): string[]; + buildArgs( + profile: string, + userArgs: string[], + options?: { + creds?: TargetCredentials; + profileType?: ProfileType; + binaryInfo?: TargetBinaryInfo; + } + ): string[]; /** Build environment variables for the target CLI */ buildEnv(creds: TargetCredentials, profileType: string): NodeJS.ProcessEnv; @@ -46,7 +54,7 @@ export interface TargetAdapter { ### Type Definitions ```typescript -export type TargetType = 'claude' | 'droid'; +export type TargetType = 'claude' | 'droid' | 'codex'; export interface TargetCredentials { baseUrl: string; // API endpoint @@ -59,6 +67,8 @@ export interface TargetCredentials { export interface TargetBinaryInfo { path: string; // Full path to binary needsShell: boolean; // Windows .cmd/.bat/.ps1? + version?: string; // Optional version string + features?: readonly string[]; // Capability probes } ``` @@ -73,8 +83,10 @@ CCS resolves which adapter to use via priority-ordered checks: ``` 1. --target flag (CLI argument) — highest priority └─ ccs --target droid glm + └─ ccs --target codex 2. Per-profile config (from ~/.ccs/config.yaml or settings.json) + └─ persisted targets are currently only `claude` and `droid` └─ profiles: glm: target: droid @@ -82,6 +94,8 @@ CCS resolves which adapter to use via priority-ordered checks: 3. argv[0] detection (runtime alias pattern) — binary name mapping └─ ccs-droid (explicit alias) → droid └─ ccsd (legacy shortcut) → droid + └─ ccs-codex (explicit alias) → codex + └─ ccsx (short alias) → codex └─ ccs (regular command) → default 4. Fallback: 'claude' — lowest priority @@ -105,6 +119,7 @@ export function resolveTargetType( // 2. Check profile config if (profileConfig?.target) { + // Persisted targets intentionally exclude runtime-only codex. return profileConfig.target; } @@ -380,6 +395,103 @@ CCS_TARGET_ALIASES=droid=mydroid --- +## Codex Adapter + +### Implementation + +The Codex adapter keeps CCS-backed Codex launches transient. It does not rewrite +`~/.codex/config.toml`. Instead it: + +- passes through native default Codex sessions unchanged +- probes the installed Codex binary for `--config ` support +- injects CCS-backed provider credentials through temporary `-c` overrides +- stores the routed API key only in process env via `CCS_CODEX_API_KEY` + +```typescript +// src/targets/codex-adapter.ts + +export class CodexAdapter implements TargetAdapter { + readonly type: TargetType = 'codex'; + readonly displayName = 'Codex CLI'; + + detectBinary(): TargetBinaryInfo | null { + return getCodexBinaryInfo(); + } + + async prepareCredentials(_creds: TargetCredentials): Promise { + // No file writes. Codex uses transient -c overrides plus env_key injection. + } + + buildArgs(profile: string, userArgs: string[], options?: BuildOptions): string[] { + if ((options?.profileType || 'default') === 'default') { + return userArgs; + } + + if (!codexBinarySupportsConfigOverrides(options?.binaryInfo)) { + throw new Error('Upgrade Codex before using CCS-backed Codex profiles.'); + } + + return [ + '-c', + 'model_provider=\"ccs_runtime\"', + '-c', + 'model_providers.ccs_runtime.base_url=\"http://127.0.0.1:8317/api/provider/codex\"', + '-c', + 'model_providers.ccs_runtime.env_key=\"CCS_CODEX_API_KEY\"', + '-c', + 'model_providers.ccs_runtime.wire_api=\"responses\"', + ...userArgs, + ]; + } + + buildEnv(creds: TargetCredentials, profileType: string): NodeJS.ProcessEnv { + const env = { ...stripAnthropicEnv(process.env) }; + if (profileType !== 'default') { + env['CCS_CODEX_API_KEY'] = creds.apiKey; + } + return env; + } +} +``` + +### Support Matrix + +Codex is a real runtime target, but it is intentionally narrower than Claude or Droid in v1: + +| Profile Type | Codex Target | Notes | +|--------------|--------------|-------| +| `default` | Yes | Uses existing native Codex auth/config | +| `cliproxy` provider=`codex` | Yes | Routed through CLIProxy Codex Responses bridge | +| `cliproxy` composite | No | Not proven native-Codex-safe | +| `settings` with Codex bridge metadata | Yes | Only when the API profile resolves to a Codex CLIProxy bridge | +| `settings` generic API profile | No | Claude/Droid only | +| `account` | No | Claude-only account isolation concept | +| `copilot` | No | Not a native Codex provider path | + +### Runtime Alias Pattern + +```bash +# Built-in package bin aliases +ccs-codex +→ Target: codex (forced by runtime alias) + +ccsx codex +→ Target: codex (forced by runtime alias) +→ codex ...args +``` + +Runtime aliases can also be extended with `CCS_TARGET_ALIASES` or legacy +`CCS_CODEX_ALIASES` after creating a matching launcher: + +```bash +ln -s /path/to/ccs /path/to/mycodex +CCS_TARGET_ALIASES='codex=mycodex' +# Legacy fallback: +CCS_CODEX_ALIASES='mycodex' +``` + +--- + ## Registry and Lookup The target registry is a simple map-based store for adapters: @@ -415,6 +527,7 @@ At startup, adapters self-register: registerTarget(new ClaudeAdapter()); registerTarget(new DroidAdapter()); +registerTarget(new CodexAdapter()); ``` --- @@ -522,7 +635,7 @@ export class MyAiAdapter implements TargetAdapter { ```typescript // src/targets/target-adapter.ts -export type TargetType = 'claude' | 'droid' | 'myai'; +export type TargetType = 'claude' | 'droid' | 'codex' | 'myai'; ``` ### 3. Register in ccs.ts @@ -621,8 +734,13 @@ ccs --target claude help # Test Droid adapter (if installed) ccs --target droid help +# Test Codex adapter (if installed) +ccs --target codex +ccs-codex + # Test argv[0] detection ccs-droid help +ccsx ``` ---