Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion docs/code-standards.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# CCS Code Standards

Last Updated: 2026-02-04
Last Updated: 2026-04-07

Code standards, modularization patterns, and conventions for the CCS codebase.

Expand Down Expand Up @@ -383,6 +383,14 @@ export type {

## Terminal Output Standards

### CCS Logging Standards

- Use the shared logger from `src/services/logging/` for CCS-owned runtime diagnostics, request tracing, and structured events.
- Keep `utils/ui` and deliberate `console.log`/`console.error` output for user-facing CLI UX only.
- Redact secrets before persistence; never write raw tokens, cookies, API keys, or password hashes into CCS-owned logs.
- Persist CCS-owned logs only under `getCcsDir()/logs`; do not invent per-feature log roots.
- When adding dashboard polling or diagnostics routes, prevent them from recursively logging the log viewer itself.

### ASCII Only

```typescript
Expand Down
10 changes: 9 additions & 1 deletion docs/codebase-summary.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# CCS Codebase Summary

Last Updated: 2026-03-28
Last Updated: 2026-04-07

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.

Expand Down Expand Up @@ -269,6 +269,14 @@ src/
- 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.
- The dashboard channels section surfaces Bun/version/auth/state-scope status from `/api/channels`, preserves token drafts when save-follow-up refresh fails, and keeps unsupported selected iMessage visible only so it can be turned off.

### Structured Logging Domain

- CCS-owned runtime logging now lives in `src/services/logging/`.
- The shared domain owns path resolution, redaction, rotation/pruning, buffered recent-entry reads, and the logger factory used by CLI/server/runtime code.
- Dashboard exposure lives in `src/web-server/routes/logs-routes.ts`, `src/web-server/services/logs-dashboard-service.ts`, and `src/web-server/middleware/request-logging-middleware.ts`.
- The native dashboard viewer lives at `ui/src/pages/logs.tsx` with supporting components under `ui/src/components/logs/` and hooks in `ui/src/hooks/use-logs.ts`.
- Legacy CLIProxy error files still exist under `~/.ccs/cliproxy/logs` and are surfaced as a labeled legacy source rather than the primary CCS logging model.

### Target Adapter Module

The targets module provides an extensible interface for dispatching profiles to different CLI implementations.
Expand Down
1 change: 1 addition & 0 deletions docs/project-roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ All major modularization work is complete. The codebase evolved from monolithic

- **2026-04-08**: **#929** Image Analysis hardening now makes the managed `ccs-image-analysis` MCP path authoritative on healthy Claude-target launches, suppresses stale CCS-managed image `Read` hooks instead of letting them compete with MCP, keeps the legacy hook available only as compatibility fallback when MCP provisioning fails, and extends self-heal to dashboard provisioning plus `ccs doctor --fix` so stale hook files and missing isolated MCP sync are repaired automatically.
- **2026-04-07**: CLIProxy routing strategy is now a first-class CCS surface. Users can inspect and explicitly change `round-robin` vs `fill-first` from `ccs cliproxy routing` and from a native `/cliproxy` dashboard card. Local mode now persists the chosen startup default into CCS-managed CLIProxy config generation, while untouched installs remain on `round-robin`. CCS deliberately does not infer strategy from account composition.
- **2026-04-07**: **#926** CCS now has a first-class structured logging layer under `src/services/logging/`, a bounded top-level `logging` config section in `~/.ccs/config.yaml`, automatic rotation/retention for CCS-owned logs under `~/.ccs/logs/`, native `/api/logs` dashboard endpoints, request tracing for the dashboard backend, and a dedicated `System -> Logs` dashboard route for browsing recent entries and editing retention settings. Legacy CLIProxy error files remain available as a labeled legacy source instead of acting as the primary logging model.
- **2026-04-06**: The dashboard login surface now distinguishes a real sign-in from a host-setup requirement. Remote/IP visitors no longer see a misleading blank credential form when dashboard auth is disabled or incomplete; they now get explicit guidance that CCS has no default credentials, should be enabled on the host with `ccs config auth setup`, or should be reopened via localhost when used on the same machine. The password field now includes a show/hide toggle, and the page exposes an explicit light/dark theme switch before sign-in.
- **2026-04-04**: The GitHub README was reduced from a wall-of-text reference dump into a shorter conversion surface that keeps the hero, proof screenshots, and fast-start commands while delegating deeper installation, provider, feature, and CLI-reference content to `docs.ccs.kaitran.ca`. The docs site now includes a dedicated `Product Tour` page for the screenshot-led walkthrough.
- **2026-04-05**: **#912 #913 #914** Kiro auth is now aligned with the current CLIProxyAPIPlus contract. CCS auto-selects the Builder ID path for the default `ccs kiro --auth` flow instead of stalling on the upstream Builder ID vs IDC chooser, callback-based Kiro auth methods can use `--paste-callback` by replaying the pasted redirect URL back into the local callback server, and the CLI now supports IDC auth via `--kiro-auth-method idc` plus `--kiro-idc-start-url`, `--kiro-idc-region`, and `--kiro-idc-flow`.
Expand Down
11 changes: 10 additions & 1 deletion docs/system-architecture/index.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# CCS System Architecture

Last Updated: 2026-03-28
Last Updated: 2026-04-07

High-level architecture overview for the CCS (Claude Code Switch) system.

Expand All @@ -18,6 +18,7 @@ The system consists of two main components:
Dashboard localization (i18n) architecture and contributor workflow are documented in [Dashboard i18n Guide](../i18n-dashboard.md).

CCS v7.34 adds Image Analysis Hook for vision model proxying through CLIProxy with automatic injection for all profile types.
CCS v7.67 adds a native structured logging lane for CCS-owned runtime events, backed by `src/services/logging/`, bounded JSONL files under `~/.ccs/logs/`, and a dedicated dashboard `/logs` route.

```
+===========================================================================+
Expand Down Expand Up @@ -216,6 +217,14 @@ For detailed provider flows (CLIProxyAPI, legacy GLMT compatibility, quota manag

## Configuration Architecture

### CCS Logging Architecture

- Shared logging contract lives in `src/services/logging/` and is used for CCS-owned runtime diagnostics, request tracing, and bounded recent-entry reads.
- Config lives at top-level `logging.*` in `~/.ccs/config.yaml`; `cliproxy.logging.*` still controls upstream CLIProxy runtime files only.
- CCS-owned runtime logs write to `~/.ccs/logs/current.jsonl` and rotate into `~/.ccs/logs/archive/` based on policy.
- Dashboard exposure uses native `/api/logs/config`, `/api/logs/sources`, and `/api/logs/entries` endpoints plus the `System -> Logs` React page.
- Request logging explicitly skips `/api/logs` reads so the log viewer does not recursively log itself.

### Config File Hierarchy

```
Expand Down
11 changes: 11 additions & 0 deletions src/ccs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ import { tryHandleRootCommand } from './commands/root-command-router';
import { execClaude } from './utils/shell-executor';
import { isDeprecatedGlmtProfileName, normalizeDeprecatedGlmtEnv } from './utils/glmt-deprecation';
import { maybeWarnAboutResumeLaneMismatch } from './auth/resume-lane-warning';
import { createLogger } from './services/logging';

// Import target adapter system
import {
Expand Down Expand Up @@ -321,6 +322,7 @@ async function main(): Promise<void> {
registerTarget(new ClaudeAdapter());
registerTarget(new DroidAdapter());
registerTarget(new CodexAdapter());
const cliLogger = createLogger('cli');

const args = process.argv.slice(2);
const isCompletionCommand = args[0] === '__complete';
Expand Down Expand Up @@ -398,6 +400,12 @@ async function main(): Promise<void> {
return;
}

cliLogger.info('command.start', 'CLI invocation started', {
command: args[0] || 'default',
argCount: args.length,
flags: args.filter((arg) => arg.startsWith('-')).slice(0, 20),
});

if (shouldPassthroughNativeCodexFlagCommand(args)) {
execNativeCodexFlagCommand(args);
return;
Expand Down Expand Up @@ -448,6 +456,9 @@ async function main(): Promise<void> {
recovery.showRecoveryHints();
}
} catch (err) {
cliLogger.warn('recovery.failed', 'Auto-recovery failed during CLI startup', {
message: (err as Error).message,
});
// Recovery is best-effort - don't block basic CLI functionality
console.warn('[!] Recovery failed:', (err as Error).message);
}
Expand Down
8 changes: 6 additions & 2 deletions src/cliproxy/proxy-detector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { getExistingProxy, registerSession, getRunningProxyVersion } from './ses
import { isCliproxyRunning } from './stats-fetcher';
import { getPortProcess, isCLIProxyProcess, PortProcess } from '../utils/port-utils';
import { CLIPROXY_DEFAULT_PORT } from './config-generator';
import { createLogger } from '../services/logging';

/** Detection method used to find the proxy */
export type DetectionMethod = 'http' | 'session-lock' | 'port-process' | 'http-retry';
Expand Down Expand Up @@ -48,6 +49,7 @@ type LogFn = (msg: string) => void;

/** No-op logger for when verbose is disabled */
const noopLog: LogFn = () => {};
const logger = createLogger('cliproxy:proxy-detector');

/**
* Detect running CLIProxy using multiple methods with fallbacks.
Expand All @@ -65,7 +67,7 @@ export async function detectRunningProxy(
port: number = CLIPROXY_DEFAULT_PORT,
verbose: boolean = false
): Promise<ProxyStatus> {
const log: LogFn = verbose ? (msg) => console.error(`[proxy-detector] ${msg}`) : noopLog;
const log: LogFn = verbose ? (msg) => logger.debug('detect.verbose', msg, { port }) : noopLog;

// Validate port - fallback to default if invalid
const validPort =
Expand Down Expand Up @@ -235,7 +237,9 @@ export function reclaimOrphanedProxy(
pid: number,
verbose: boolean = false
): string | null {
const log: LogFn = verbose ? (msg) => console.error(`[proxy-detector] ${msg}`) : noopLog;
const log: LogFn = verbose
? (msg) => logger.debug('reclaim.verbose', msg, { port, pid })
: noopLog;

try {
log(`Reclaiming orphaned proxy: port=${port}, pid=${pid}`);
Expand Down
6 changes: 5 additions & 1 deletion src/cliproxy/startup-lock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import * as fs from 'fs';
import * as path from 'path';
import { getCliproxyDir } from './config-generator';
import { createLogger } from '../services/logging';

/** Lock file structure */
interface LockData {
Expand All @@ -51,6 +52,7 @@ type LogFn = (msg: string) => void;

/** No-op logger for when verbose is disabled */
const noopLog: LogFn = () => {};
const logger = createLogger('cliproxy:startup-lock');

/**
* Get path to startup lock file
Expand Down Expand Up @@ -184,7 +186,9 @@ export async function acquireStartupLock(options?: {
}): Promise<LockResult> {
const retries = options?.retries ?? 20;
const retryInterval = options?.retryInterval ?? 250;
const log: LogFn = options?.verbose ? (msg) => console.error(`[startup-lock] ${msg}`) : noopLog;
const log: LogFn = options?.verbose
? (msg) => logger.debug('lock.verbose', msg, { retries, retryInterval })
: noopLog;

log(`Attempting to acquire startup lock (max ${retries} retries, ${retryInterval}ms interval)`);

Expand Down
7 changes: 7 additions & 0 deletions src/cliproxy/tool-sanitization-proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
} from './model-id-normalizer';
import { getModelMaxLevel } from './model-catalog';
import { getCcsDir } from '../utils/config-manager';
import { createLogger } from '../services/logging';

export interface ToolSanitizationProxyConfig {
/** Upstream CLIProxy URL */
Expand Down Expand Up @@ -153,6 +154,7 @@ export class ToolSanitizationProxy {
private readonly config: Required<ToolSanitizationProxyConfig>;
private readonly logFilePath: string;
private readonly debugMode: boolean;
private readonly logger = createLogger('cliproxy:tool-sanitization-proxy');

constructor(config: ToolSanitizationProxyConfig) {
this.config = {
Expand Down Expand Up @@ -207,6 +209,11 @@ export class ToolSanitizationProxy {
if (this.debugMode) {
console.error(`${prefix} ${message}`);
}

this.logger[level](level, message, {
debugMode: this.debugMode,
logFilePath: this.logFilePath,
});
}

private log(message: string): void {
Expand Down
Loading
Loading