Skip to content

Latest commit

 

History

History
228 lines (193 loc) · 16 KB

File metadata and controls

228 lines (193 loc) · 16 KB

GoZen - Claude Code Environment Switcher

Project Overview

Go CLI tool for managing multiple Claude API provider configurations with proxy failover and TUI/Web interfaces.

Tech Stack

  • Language: Go
  • TUI: Bubble Tea (charmbracelet/bubbletea) + Lip Gloss
  • Web: Embedded static files (HTML/CSS/JS, no framework), Go HTTP server
  • CLI: Cobra
  • Config: JSON store at ~/.zen/zen.json

Project Structure

cmd/           # Cobra commands (root, config, pick, web, upgrade, bind)
internal/
  config/      # Config store, types, legacy migration, compat helpers
  daemon/      # Web server daemon management (start/stop, platform-specific)
  proxy/       # Reverse proxy with failover, token calculation, session cache
  update/      # Non-blocking version update checker with 24h cache
  web/         # HTTP API server + embedded static frontend
    static/    # app.js, index.html, style.css (vanilla JS, no build step)
tui/           # All TUI models (editor, pick, config_main, fallback, routing, etc.)

Build & Test

go build ./...
go test ./...

Dev Environment

Dev daemon runs on separate ports to avoid interfering with production:

  • Dev Web UI: http://localhost:29840
  • Dev Proxy: http://localhost:29841
  • Dev Config: ~/.zen-dev/zen.json
  • Production ports (19840/19841): NEVER touch during development
./scripts/dev.sh              # Start dev daemon (builds + starts)
./scripts/dev.sh stop         # Stop dev daemon
./scripts/dev.sh restart      # Rebuild and restart dev daemon
./scripts/dev.sh status       # Check dev daemon status
./scripts/dev.sh web          # Start frontend dev server (Vite)

After modifying Go code, always run ./scripts/dev.sh restart to rebuild and restart the dev daemon.

Release Process

Push a git tag to trigger GitHub Actions release workflow:

git tag v2.x.0
git push origin v2.x.0

Do NOT use gh release create — the CI pipeline handles release creation automatically.

Workflow Rules

  • Commit often: Each completed task/item should be committed individually, not batched into one large commit. After finishing a feature or fix, commit immediately before moving to the next task.
  • Branch protection: The main branch is protected. All changes MUST go through a pull request — never push directly to main. Create a feature branch, commit there, then use gh pr create to open a PR.
  • Pre-release check: Before tagging a release, check for unpushed commits (git log origin/main..HEAD) and push them first.
  • Update version constant: Before releasing, update Version in cmd/root.go to match the release tag.
  • Update README: Before releasing, check that README.md reflects all new features and changes. All four language versions must be updated together: README.md (English), docs/README.zh-CN.md (简体中文), docs/README.zh-TW.md (繁體中文), docs/README.es.md (Español).
  • No summary/explanation docs: Do NOT create markdown files to summarize or explain completed work. No "implementation notes", no "changes summary", no "feature documentation". The commit message and code comments are sufficient.
  • Keep planning docs: Architecture planning and design docs should be kept for context across sessions. Store them in .dev/ folder (gitignored, for internal dev use only). docs/ is for user-facing content only.
  • No example files: Do NOT create example config files (*.json, *.yaml, etc.) in the repository root. Examples belong in README.md or docs/.
  • Minimal test files: Only add tests for new public APIs or complex logic. Do not create excessive test files for simple functions. Prefer table-driven tests in existing *_test.go files.
  • No unnecessary files: Before committing, review git status and remove any generated, temporary, or example files that should not be in the repository.
  • TDD for new features: Use Test-Driven Development (TDD) for new feature development to ensure code quality. Write tests first, then implement the feature to make tests pass.

Release Checklist

Before tagging a release, complete the following checklist:

  1. Bug check: Review all code for unresolved bugs. Run go test ./... and ensure all tests pass.
  2. Version number: Verify Version in cmd/root.go matches the release tag.
  3. Website documentation:
    • Ensure website contains documentation for the release version
    • Verify documentation is accurate and complete
    • Confirm all new features are documented
    • Remove or update documentation for changed/removed features
  4. README files: Update all README files to reflect the latest version:
    • README.md (English)
    • docs/README.zh-CN.md (简体中文)
    • docs/README.zh-TW.md (繁體中文)
    • docs/README.es.md (Español)

Config Migration Rules

When modifying OpenCCConfig or its nested types in a way that changes the JSON schema (e.g. changing a field's type, renaming a field, restructuring data):

  1. Bump CurrentConfigVersion in internal/config/config.go.
  2. Add migration logic so older config files are parsed correctly. The current pattern uses a custom UnmarshalJSON on OpenCCConfig with a fallback path that parses changed fields as json.RawMessage and converts them to the new format.
  3. The version check in loadLocked() (store.go) automatically upgrades cfg.Version < CurrentConfigVersion to the current version after unmarshal.
  4. Add tests covering: old format parsing, mixed old/new format, field preservation on the fallback path, and marshal round-trip.

Brand Colors

Primary palette for website, Web UI, and marketing materials:

Name Dark Mode Light Mode Usage
Teal (Primary) #5eead4 #0d9488 Primary accent, buttons, links
Lavender (Secondary) #c4b5fd #7c3aed Secondary accent, highlights
Sage #86efac - Success states
Red #fb7185 - Error states
Amber #fbbf24 - Warning states
Blue #93c5fd - Info states

Background (Dark): #0f1117#181a24#1e2030#252837 Background (Light): #f8fafc#ffffff#f1f5f9#e2e8f0

Key Conventions

  • Config convenience functions in internal/config/compat.go wrap DefaultStore() methods
  • TUI models follow Bubble Tea pattern: newXxxModel(), Init(), Update(), View()
  • Standalone TUI entry points: RunXxx() functions in tui package
  • Inline config sub-editors use wrapper types implementing tea.Model (e.g. editorWrapper, fallbackWrapper)
  • Web API routes: /api/v1/providers, /api/v1/profiles, /api/v1/health, /api/v1/reload
  • Web frontend uses vanilla JS (no build tools), CSS custom properties for theming
  • Model IDs in autocomplete must be verified against official Anthropic docs
  • Environment variable prefix: GOZEN_ (e.g., GOZEN_WEB_DAEMON)

Version History

  • v1.0.0: Initial release
  • v1.1.0: Go rewrite with proxy failover and TUI
  • v1.1.1: Upgrade command, sorted configs
  • v1.2.0: Fallback profiles, pick command, installer
  • v1.3.0: Web UI, profile assignment on provider add, model autocomplete, CLI name args
  • v1.3.1: Download progress bar, README refresh
  • v1.3.2: Fix progress bar display (show downloaded/total size)
  • v1.4.0: Scenario routing, token calculation, session cache, project bindings
  • v1.5.0: Dashboard TUI, project binding CLI support, centralized CLI list
  • v1.5.1: Fix symlink dedup in bindings, Web UI dropdown style, --port restriction, config v3→v5 migration, SQLite log storage
  • v1.5.2: Allow reinstalling same version in upgrade command
  • v1.5.3: Per-binary PID files to avoid multi-binary conflicts
  • v2.0.0: Rename to GoZen (opencc → zen), config migration from ~/.opencc/ to ~/.zen/, non-blocking version update check
  • v3.0.0: Usage tracking & budget control, provider health monitoring, smart load balancing, webhooks, context compression, middleware pipeline, agent infrastructure
  • v3.0.1: Scenario routing redesign (protocol-agnostic normalization, per-scenario RoutePolicy), proxy transform correctness (streaming id/model/tool_calls, multi-format SSE usage tracking)

Active Technologies

  • Go 1.21+ + net/http, net/url, golang.org/x/net/proxy (for SOCKS5) (001-provider-proxy)
  • JSON config at ~/.zen/zen.json (001-provider-proxy)
  • Go 1.21+ + net, net/http, internal/config, internal/proxy (002-fix-proxy-port)
  • Go 1.21+ + Bubble Tea (TUI), Cobra (CLI), net/http (proxy/web) (003-skill-intent-recognition)
  • JSON config at ~/.zen/zen.json, match logs in memory (ring buffer) (003-skill-intent-recognition)
  • Go 1.21+ + net/http, net/url, golang.org/x/net/proxy (SOCKS5), Cobra (CLI) (004-fix-proxy-stability)
  • JSON config at ~/.zen/zen.json (no schema changes needed) (004-fix-proxy-stability)
  • Go 1.21+ + net/http, encoding/json, bufio (SSE parsing), React + TypeScript (Web UI) (005-provider-model-tag)
  • JSON config at ~/.zen/zen.json (new show_provider_tag boolean field, version 10 → 11) (005-provider-model-tag)
  • Go 1.21+ + net/http, encoding/json, sync (for in-memory buffer), React + TypeScript (Web UI) (006-revert-tag-add-monitoring)
  • In-memory ring buffer (default 1000 requests), no database persistence for MVP (006-revert-tag-add-monitoring)
  • Go 1.21+ + Cobra (CLI), net/http (proxy/web), syscall (file lock, process management), React+TypeScript (Web UI) (007-fix-daemon-stability)
  • JSON config at ~/.zen/zen.json, SQLite for proxy logs (007-fix-daemon-stability)
  • Go 1.21+ (backend), TypeScript (frontend, React 18) + net/http, net/http/httptest, os/exec, encoding/json (Go tests); vitest 4, @testing-library/react, MSW (frontend tests) (008-automated-testing)
  • JSON config at ~/.zen/zen.json (test isolation via GOZEN_CONFIG_DIR and ephemeral ports) (008-automated-testing)
  • Go 1.21+ + Cobra (CLI), Bubble Tea + Lip Gloss (TUI), React + TypeScript + Vite (Web UI) (009-code-scenario-routing)
  • JSON at ~/.zen/zen.json — no schema migration needed (string-keyed routing map) (009-code-scenario-routing)
  • Go 1.21+ + Cobra (CLI framework), existing cmd/root.go permission handling (001-use-command-enhancements)
  • JSON config at ~/.zen/zen.json (schema version bump required) (001-use-command-enhancements)
  • JSON config at ~/.zen/zen.json (version 12 → 13) (011-feature-gates-daemon-persistence)
  • File system (reads website docs directory structure at website/) (012-website-i18n-docs)
  • Go 1.21+ + net/http, encoding/json, net/url (proxy); existing internal/proxy/transform package (013-fix-proxy-model-transform)
  • Go 1.21+ + net/http, encoding/json, bufio, io (stdlib only) (014-fix-responses-api-transform)
  • N/A (no config schema changes, no version bump) (014-fix-responses-api-transform)
  • Go 1.21+ + Cobra (CLI), net/http (proxy/web), React + TypeScript + Vite (Web UI) (015-mark-provider-unavailable)
  • JSON config at ~/.zen/zen.json (version 13 → 14) (015-mark-provider-unavailable)
  • Go 1.21+ + net/http (stdlib), runtime (metrics), debug (stack traces), sync (concurrency), encoding/json (structured logging), existing internal packages (config, proxy, daemon, web, httpx) (017-proxy-stability)
  • JSON config at ~/.zen/zen.json (existing), in-memory metrics (no persistence), structured JSON logs to stderr (017-proxy-stability)
  • New packages: internal/daemon/logger.go (structured JSON logging), internal/proxy/limiter.go (semaphore-based concurrency control), internal/daemon/metrics.go (request metrics with percentiles) (017-proxy-stability)
  • JSON config at ~/.zen/zen.json (existing), in-memory metrics (no persistence) (017-proxy-stability)
  • Go 1.21+ + bufio, encoding/json, io, net/http (stdlib only) (018-proxy-transform-correctness)
  • N/A (no config schema changes) (018-proxy-transform-correctness)
  • Go 1.21+ + net/http, sync, time, encoding/json (stdlib only); existing internal/config, internal/proxy packages (019-profile-strategy-routing)
  • SQLite (existing LogDB at ~/.zen/logs.db) for latency metrics persistence; in-memory ring buffer for round-robin state (019-profile-strategy-routing)

Recent Changes

  • 020-scenario-routing-redesign: Protocol-agnostic scenario routing with middleware extensibility
    • Protocol-agnostic normalization: Anthropic Messages, OpenAI Chat, OpenAI Responses → unified NormalizedRequest
    • Middleware-driven routing: RoutingDecision/RoutingHints in RequestContext, middleware precedence over builtin classifier
    • Open scenario namespace: Custom scenario keys (camelCase, kebab-case, snake_case), no source code changes needed
    • Per-scenario routing policies: Strategy, weights, model overrides, threshold per scenario
    • Strong config validation: Provider existence, empty list, weights, strategy, scenario key format validated at load time
    • Routing observability: Structured logs (scenario, source, reason, confidence), request features, fallback logging
    • Config migration: Automatic v14→v15 migration (ScenarioRoute → RoutePolicy), backward compatible
    • UI support: TUI and Web UI support custom scenarios (add/remove/configure)
    • New types: NormalizedRequest, RequestFeatures, RoutingDecision, RoutingHints, RoutePolicy
    • New files: routing_normalize.go, routing_classifier.go, routing_resolver.go, config_migration_test.go
  • 019-profile-strategy-routing: Profile strategy-aware provider routing with 5 strategies
    • Least-latency: Routes to provider with lowest average response time (100-request rolling window, min 10 samples from SQLite LogDB)
    • Least-cost: Routes to provider with lowest model pricing (uses built-in Anthropic pricing table)
    • Round-robin: Even distribution across healthy providers using atomic counter
    • Weighted: Configurable weight-based random distribution (recalculates on health change, falls back to equal weights)
    • Failover: Default strategy, first healthy provider in configured order
    • Strategy decision logging for all strategies (provider, reason, candidates)
    • LoadBalancer integration in ProxyServer.ServeHTTP() with per-request provider reordering
    • New fields: Provider.Weight, ProviderConfig.Weight, ProfileConfig.Strategy/ProviderWeights
  • 017-proxy-stability: Daemon proxy stability improvements for 24-hour uptime and 100 concurrent request handling
    • Auto-restart with exponential backoff (max 5 restarts, 1s→30s backoff)
    • Goroutine leak detection with baseline comparison and stack dumps (1-minute ticker, 20% growth tolerance)
    • Panic recovery middleware in httpx package (captures stack traces, prevents daemon crashes)
    • Concurrency limiter with semaphore pattern (100 concurrent requests, buffered channel)
    • Request timeout enforcement (10-minute HTTP client timeout)
    • Connection pool cleanup on cache invalidation (ProfileProxy.InvalidateCache/Close)
    • Streaming write error handling (early return on client disconnect)
    • Structured JSON logging for daemon lifecycle events (daemon_started, daemon_shutdown, goroutine_leak_detected, daemon_crashed_restarting)
    • Request metrics collection with percentile calculation (P50/P95/P99, ring buffer, error grouping by provider/type)
    • Health monitoring API (/api/v1/daemon/health) with three-tier status (healthy/degraded/unhealthy)
    • Metrics API (/api/v1/daemon/metrics) with latency percentiles, error breakdowns, resource peaks
    • Test coverage: comprehensive unit tests for limiter, logger, metrics; integration tests for load (100 concurrent), timeout, connection pool cleanup
  • 006-revert-tag-add-monitoring: Removed provider tag injection from responses, added comprehensive request monitoring with detail view and filtering
    • Removed show_provider_tag setting and all tag injection logic from proxy responses
    • Added RequestMonitor with thread-safe ring buffer (1000 request capacity, LRU eviction)
    • Added GET /api/v1/monitoring/requests API with filtering (provider, model, status, time range)
    • Added GET /api/v1/monitoring/requests/:id API for detailed request metadata
    • Added React-based monitoring page with auto-refresh, filters, and detail modal
    • Monitoring tracks: timestamp, provider, model, status, duration, tokens, cost, failover chain, errors
    • Test coverage: 81.6% (proxy), 85.6% (config), 80.3% (web)