Skip to content

Commit 7fc6cb6

Browse files
committed
2 parents 3a2e9de + e63ac4d commit 7fc6cb6

181 files changed

Lines changed: 18414 additions & 4960 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Added model aliases for Trae models to improve client compatibility and discovery.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
## 2026-02-20
2+
3+
- SQLite 드라이버는 CGO-free 요구사항을 만족하기 위해 `modernc.org/sqlite`를 선택했다.
4+
- `ReadVSCDBToken`는 DB 경로 미입력 시 `DefaultVSCDBPath()`를 사용하도록 하여 운영 환경 기본 동작을 보장했다.
5+
- 토큰 마스킹은 요구사항(앞 4자리만 표시)에 맞춰 `xxxx...` 포맷 전용 헬퍼(`maskTokenPrefix`)를 사용했다.
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
## LS Crash Issue Analysis - 2026-02-20
2+
3+
### Issue Summary
4+
LS (Language Server) process crashes with "signal: killed" immediately after SendUserCascadeMessage completes successfully (HTTP 200 response received after 33.928s).
5+
6+
---
7+
8+
### Root Cause Identified
9+
10+
**MISSING: StreamCascadeReactiveUpdates subscription**
11+
12+
The ZeroGravity reference implementation (`backend.rs` lines 418-527) maintains an active subscription to `StreamCascadeReactiveUpdates` after sending a cascade message. This is absent in the current implementation.
13+
14+
#### Key Differences from ZeroGravity:
15+
16+
| Aspect | ZeroGravity (Working) | CLIProxy (Crashing) |
17+
|--------|----------------------|---------------------|
18+
| Post-Message | Subscribes to reactive updates | No reactive subscription |
19+
| Stream Reading | Background goroutine consumes chunks | No stream consumer |
20+
| Protocol | Connect protocol envelope handling | Missing |
21+
| Connection Lifecycle | Maintains stream connection | Connection may be orphaned |
22+
23+
#### ZeroGravity Reactive Stream Implementation:
24+
25+
```rust
26+
// backend.rs lines 418-527
27+
async fn stream_reactive_rpc(
28+
&self,
29+
rpc_method: &str,
30+
cascade_id: &str,
31+
) -> Result<tokio::sync::mpsc::Receiver<serde_json::Value>, String> {
32+
// Uses application/connect+json content-type
33+
// Sends envelope: [flags:1][length:4][payload]
34+
// Spawns background task to continuously read chunks
35+
// Parses Connect protocol frames
36+
}
37+
```
38+
39+
---
40+
41+
### Why This Causes Crashes
42+
43+
1. **LS expects active reader**: After SendUserCascadeMessage, the LS likely expects the client to subscribe to reactive updates to receive streaming responses
44+
2. **Buffer backpressure**: Without a consumer reading the reactive stream, the LS write buffers fill up, potentially causing it to block and eventually be killed
45+
3. **Missing lifecycle signal**: The reactive stream subscription signals to LS that the client is ready to receive streaming updates
46+
4. **USS topic insufficient**: Current USS topic subscription in stub server (`uss-oauth`) handles authentication only, not cascade-specific reactive updates
47+
48+
---
49+
50+
### Current State Analysis
51+
52+
**Files examined:**
53+
54+
1. **antigravity_executor.go** (lines 677-850)
55+
- `ExecuteStream()` calls `SendUserCascadeMessage` and then reads response body
56+
- No reactive update subscription before/during message send
57+
- Response body is read as SSE stream, but this is different from reactive updates
58+
59+
2. **ls_client.go**
60+
- `SendProto()` sends protobuf requests
61+
- No stream subscription capability
62+
- HTTP client has 300s response header timeout
63+
64+
3. **ls_process.go**
65+
- Handles LS lifecycle (start/stop/healthcheck)
66+
- Crash recovery mechanism present but triggered after "signal: killed"
67+
- No reactive stream handling
68+
69+
4. **ls_stub_server.go** (lines 164-169)
70+
- Handles `SubscribeToUnifiedStateSyncTopic` for USS (oauth token)
71+
- Only handles initial state + keepalive
72+
- Does NOT handle cascade-specific reactive updates
73+
74+
5. **ls_warmup.go**
75+
- Warmup sequence runs initialization RPCs
76+
- No reactive stream setup in warmup
77+
78+
---
79+
80+
### Required Fix
81+
82+
**Implement StreamCascadeReactiveUpdates subscription** similar to ZeroGravity:
83+
84+
1. **Add subscription before SendUserCascadeMessage:**
85+
- Open streaming connection to `/exa.language_server_pb.LanguageServerService/StreamCascadeReactiveUpdates`
86+
- Use `application/connect+json` content-type
87+
- Send proper Connect protocol envelope
88+
89+
2. **Background stream reader:**
90+
- Spawn goroutine to continuously read chunks
91+
- Parse `[flags:1][length:4][payload]` envelope format
92+
- Handle both data frames (flags=0x00) and end frames (flags=0x02)
93+
94+
3. **Integration with executor:**
95+
- Subscribe when cascade is created
96+
- Keep subscription alive during message exchange
97+
- Close subscription when cascade completes
98+
99+
---
100+
101+
### Technical Details
102+
103+
**Connect Protocol Frame Format:**
104+
```
105+
[1 byte flags] [4 bytes big-endian length] [payload data]
106+
107+
Flags:
108+
- 0x00 = Data frame (JSON payload)
109+
- 0x02 = End frame (stream complete)
110+
```
111+
112+
**Request Format:**
113+
```json
114+
{
115+
"protocolVersion": 1,
116+
"id": "<cascade_id>"
117+
}
118+
```
119+
120+
**Headers Required:**
121+
```
122+
Content-Type: application/connect+json
123+
Connect-Protocol-Version: 1
124+
```
125+
126+
---
127+
128+
### Evidence from ZeroGravity
129+
130+
ZeroGravity's `stream_reactive_rpc()` function:
131+
- Creates mpsc channel for streaming updates
132+
- Spawns tokio task to continuously consume chunks
133+
- Parses Connect protocol frames
134+
- Sends parsed JSON to channel
135+
- This keeps the connection alive and LS responsive
136+
137+
---
138+
139+
### Additional Observations
140+
141+
1. **UpdateConversationAnnotations**: ZeroGravity also calls `update_annotations()` after SendUserCascadeMessage (backend.rs lines 207-226) - this might be another fingerprinting issue
142+
143+
2. **Heartbeat**: Current heartbeat runs every ~1s (ls_warmup.go line 77-106) - this is good but not sufficient without reactive subscription
144+
145+
3. **USS Keepalive**: Stub server sends keepalive every 5s for USS topics (ls_stub_server.go line 241-259) - this is correct for USS but unrelated to cascade reactive updates
146+
147+
---
148+
149+
### Conclusion
150+
151+
The LS crash is caused by the **missing reactive updates subscription**. The LS expects a consumer for `StreamCascadeReactiveUpdates` to be active during cascade operations. Without this, the LS likely encounters buffer issues or timeout conditions that result in the process being killed.
152+
153+
**Priority**: HIGH - This is a critical missing feature that causes LS instability
154+
**Fix Complexity**: MEDIUM - Requires implementing Connect protocol streaming support
155+
156+
### Related Code Locations
157+
158+
- ZeroGravity reference: `/home/jc01rho/git/zerogravity-src/src/backend.rs` lines 418-527
159+
- Current stub server: `CLIProxyAPIPlus/internal/runtime/executor/ls_stub_server.go`
160+
- Executor: `CLIProxyAPIPlus/internal/runtime/executor/antigravity_executor.go`
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
2+
3+
## 2026-02-20 (Reactive Stream Subscription)
4+
5+
### 구현 개요
6+
- 파일: `CLIProxyAPIPlus/internal/runtime/executor/ls_reactive_stream.go`
7+
8+
### 문제 배경
9+
- LS가 SendUserCascadeMessage 후 "signal: killed"로 크래시됨
10+
- 원인: LS는 cascade 생성 후 reactive updates 스트림 소비자를 기대함
11+
- 소비자 없으면 LS write buffer가 차서 backpressure 발생 → 프로세스 킬됨
12+
13+
### 해결책: StreamCascadeReactiveUpdates 구독
14+
ZeroGravity 참조 구현(`backend.rs` lines 418-527)을 Go로 포팅:
15+
16+
**Connect Protocol Frame Format:**
17+
```
18+
[1 byte flags] [4 bytes big-endian length] [payload]
19+
- flags=0x00: data frame (JSON payload)
20+
- flags=0x02: end frame (stream complete)
21+
```
22+
23+
**핵심 구현 패턴:**
24+
25+
1. **HTTP 클라이언트 설정 (스트리밍용)**
26+
- `http.Client.Timeout = 0` (무제한 - 스트리밍 연결용)
27+
- `ResponseHeaderTimeout = 300s` (ls_client.go 패턴과 동일)
28+
- `DialContext.Timeout = 10s`
29+
30+
2. **Connect Protocol 요청**
31+
- URL: `https://127.0.0.1:{port}/exa.language_server_pb.LanguageServerService/StreamCascadeReactiveUpdates`
32+
- Content-Type: `application/connect+json`
33+
- Headers: `Connect-Protocol-Version: 1`, `x-codeium-csrf-token`
34+
- Body envelope: `[flags:1][length:4][payload]`
35+
36+
3. **백그라운드 고루틴 (핵심)**
37+
- `readStream()`이 별도 고루틴에서 지속적으로 chunk 소비
38+
- 버퍼에 데이터 축적 후 `processBuffer()`로 frame 단위 파싱
39+
- `processFrame()`에서 flags별 처리 (0x00=data, 0x02=end)
40+
- 스트림 소비이 목적 - 실제 데이터는 로깅만 함
41+
42+
4. **Integration with Executor**
43+
- `createCascadeWithReactiveSubscription()` 헬퍼 추가
44+
- 모든 Execute 메서드에서 cascade 생성 직후 구독 시작
45+
- `defer subscription.Close()`로 cascade 종료 시 정리
46+
47+
### 수정된 파일
48+
1. `ls_reactive_stream.go` (새 파일, 261 lines)
49+
2. `antigravity_executor.go`:
50+
- `createCascadeWithReactiveSubscription()` 메서드 추가
51+
- `Execute()`: cascade 생성 부분 수정
52+
- `executeClaudeNonStream()`: cascade 생성 부분 수정
53+
- `ExecuteStream()`: cascade 생성 부분 수정
54+
55+
### 검증
56+
```bash
57+
cd CLIProxyAPIPlus
58+
go build -o cliproxy ./cmd/server
59+
# 빌드 성공
60+
```
61+
62+
### 핵심 학습
63+
- LS 바이너리는 단순 HTTP 요청-응답이 아닌, 연결 유지형 스트리밍 패턴 요구
64+
- Reactive updates 스트림 소비는 필수이며 선택사항이 아님
65+
- Connect Protocol envelope 파싱: `[flags:1][length:4][payload]`
66+
- Background goroutine으로 지속적 소비 - 메인 플로우 차단 없음
67+
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
## 2026-02-20
2+
3+
- 현재 구현은 `ItemTable` 전체 key/value 스캔 방식이므로, 향후 키 스키마가 확정되면 대상 key를 좁혀 성능 최적화 여지가 있다.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Cline Provider - Decisions
2+
3+
## 2026-02-18 Task: Architecture Decisions
4+
- Provider name: "cline" (constant in constant.go)
5+
- Follow Kilo provider pattern for executor structure
6+
- OAuth flow uses local callback server (like Claude provider, NOT device flow like Kilo)
7+
- Token refresh uses RefreshLead of 5 minutes (not nil like Kilo)
8+
- Executor targets: `https://api.cline.bot/api/v1/chat/completions`
9+
- Auth format: `Bearer workos:{accessToken}` with workos prefix
10+
11+
## 2026-02-18 Task: OAuth flow compatibility decisions
12+
- `InitiateOAuth`는 리다이렉트 자동 추적을 끄는 임시 `http.Client`를 사용해 3xx/200 응답을 모두 수용한다.
13+
- `AuthorizeResponse`는 유지하되 `redirect_url` 필드를 확장해 JSON 응답 변형을 흡수한다.
14+
- `ExchangeCode(ctx, code, callbackURL)`로 시그니처를 전환하여 토큰 교환 시 `redirect_uri`를 명시적으로 전달한다.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Cline Provider - Issues
2+
3+
(none yet)
4+
5+
## 2026-02-18 Task: OAuth 400 장애 원인
6+
- 증상: authorize/token 호출 시 `invalid or missing client_type parameter`로 400 발생.
7+
- 원인: authorize query/payload가 Cline API 기대 스키마(`client_type=extension` 포함)와 불일치.
8+
- 해결: authorize 파라미터 및 token exchange payload를 Cline 규격으로 수정, 호출부 시그니처 동기화.
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# Cline Provider - Learnings
2+
3+
## 2026-02-18 Task: Initial Analysis
4+
- Cline uses OAuth 2.0 via WorkOS for authentication
5+
- Base URL: `https://api.cline.bot`
6+
- Auth header format: `Bearer workos:{accessToken}` (CRITICAL: the `workos:` prefix!)
7+
- Custom headers: `HTTP-Referer: https://cline.bot`, `X-Title: Cline`
8+
- Chat completion endpoint: `POST /api/v1/chat/completions` (OpenAI-compatible)
9+
- Token response: `{ success: true, data: { accessToken, refreshToken, expiresAt, userInfo } }`
10+
- Token refresh: 5 minutes before expiry
11+
- OAuth callback: local HTTP server on ports 48801-48811
12+
- Model IDs use OpenRouter format: `anthropic/claude-sonnet-4.6`
13+
- Free models: `["anthropic/claude-sonnet-4.6", "kwaipilot/kat-coder-pro", "z-ai/glm-5"]`
14+
- Provider constant should be `"cline"`
15+
- Kilo provider pattern is the closest reference (device flow vs OAuth, but same executor pattern)
16+
- Module path: `github.com/router-for-me/CLIProxyAPI/v6`
17+
- Use `util.NewProxyClient()` or `newProxyAwareHTTPClient()` for HTTP clients
18+
- Kilo provider executor/auth flow was a useful reference during early Cline implementation analysis.
19+
20+
## 2026-02-18 Task: Cline auth package implementation
21+
- `internal/auth/cline/cline_token.go` was added following Kilo token persistence pattern exactly (MkdirAll 0700, JSON encode, `misc.LogSavingCredentials`, `Type="cline"`).
22+
- `ClineTokenStorage` stores `accessToken`, `refreshToken`, `expiresAt`, `email`, `userId`, `displayName`, and `type`.
23+
- `internal/auth/cline/cline_auth.go` implements WorkOS OAuth flow endpoints:
24+
- `GET /api/v1/auth/authorize` with `callbackUrl`
25+
- `POST /api/v1/auth/token` with `{code, state}`
26+
- `POST /api/v1/auth/refresh` with `{refreshToken}`
27+
- `GET /api/v1/users/me` with `Authorization: Bearer workos:{accessToken}`
28+
- Added local callback server method with automatic fallback over ports `48801..48811` and graceful shutdown after receiving code/state.
29+
- `expiresAt` parsing supports integer, float, numeric string, and RFC3339/RFC3339Nano timestamp string formats to match possible API variants.
30+
31+
## 2026-02-18 Task: Create cline_models.go
32+
- Created `/home/jc01rho/git/cli-proxy/CLIProxyAPIPlus/internal/registry/cline_models.go`
33+
- Followed exact pattern from `kilo_models.go` (same package structure, no imports needed)
34+
- Function: `GetClineModels() []*ModelInfo`
35+
- Models defined:
36+
- `cline/auto`: Auto model selection with thinking support (200K context, 64K completion)
37+
- `anthropic/claude-sonnet-4.6`: Claude Sonnet 4.6 via Cline (200K context, 64K completion, thinking support)
38+
- `kwaipilot/kat-coder-pro`: KAT Coder Pro via Cline (128K context, 32K completion)
39+
- `z-ai/glm-5`: GLM-5 via Cline (128K context, 32K completion)
40+
- All models have Type="cline", OwnedBy="cline"
41+
- No LSP errors in the created file
42+
43+
## 2026-02-18 Task: Create cline_executor.go
44+
- Created `/home/jc01rho/git/cli-proxy/CLIProxyAPIPlus/internal/runtime/executor/cline_executor.go`
45+
- Followed exact pattern from kilo_executor.go (same structure and method organization)
46+
- Key implementations:
47+
- `ClineExecutor` struct with config
48+
- `NewClineExecutor()` constructor
49+
- `Identifier()` returns "cline"
50+
- `PrepareRequest()` - applies Cline headers with workos: prefix
51+
- `HttpRequest()` - raw HTTP request execution
52+
- `Execute()` - non-streaming chat completion
53+
- `ExecuteStream()` - streaming chat completion with SSE handling
54+
- `Refresh()` - placeholder (returns auth as-is, will be enhanced when cline auth package is ready)
55+
- `CountTokens()` - returns unsupported error (matching Kilo pattern)
56+
- `clineCredentials()` - extracts tokens from auth metadata/attributes
57+
- `applyClineHeaders()` - sets required headers including Authorization: Bearer workos:{token}
58+
- `FetchClineModels()` - dynamic model fetching from Cline API
59+
- Critical implementation details:
60+
- Authorization header uses `Bearer workos:` prefix (CRITICAL for Cline API)
61+
- Custom headers: HTTP-Referer: https://cline.bot, X-Title: Cline
62+
- API endpoint: https://api.cline.bot/api/v1/chat/completions
63+
- Uses existing executor package utilities (newProxyAwareHTTPClient, newUsageReporter, parseOpenAIUsage, etc.)
64+
- File compiles successfully with existing codebase (verified with go build)
65+
- All comments follow Go docstring conventions matching kilo_executor.go pattern
66+
67+
## 2026-02-18 Task: Integrate Cline into registration wiring (6-file surgical update)
68+
- Registration touched exactly 6 existing files, following Kilo registration pattern with minimal deltas.
69+
- `internal/constant/constant.go`: added `Cline = "cline"` constant for provider identity consistency.
70+
- `internal/cmd/auth_manager.go`: registered `sdkAuth.NewClineAuthenticator()` in manager constructor list.
71+
- `sdk/auth/refresh_registry.go`: added refresh lead registration for `cline` using `NewClineAuthenticator()`.
72+
- `internal/registry/model_definitions.go`: wired `case "cline"` in static channel lookup and included `GetClineModels()` in global static lookup slice.
73+
- `sdk/cliproxy/service.go`: wired executor registration (`NewClineExecutor`) and dynamic model fetch path (`FetchClineModels`).
74+
- `sdk/cliproxy/auth/oauth_model_alias.go`: added `cline` to OAuth model-alias-supported providers.
75+
- Build verification passed with `go build ./cmd/server` from `CLIProxyAPIPlus`.
76+
- LSP diagnostics tool in this session reports workspace-scoping warnings (`No active builds contain ...`) rather than code issues; build success served as functional compile verification.
77+
78+
## 2026-02-18 Task: Cline OAuth 파라미터/토큰 교환 정합성 수정
79+
- `/api/v1/auth/authorize` 호출 시 Cline은 `callbackUrl`이 아니라 `client_type=extension`, `callback_url`, `redirect_uri`를 기대한다.
80+
- authorize 응답은 환경에 따라 두 형태가 가능하다: (1) HTTP 3xx + `Location` 헤더, (2) 200 JSON + `redirect_url`/`url`.
81+
- OAuth state는 JSON의 `state`가 있으면 우선 사용하고, 없으면 redirect URL query의 `state`를 파싱하며, 둘 다 없을 때만 fallback 생성이 안전하다.
82+
- `/api/v1/auth/token` 교환은 `{grant_type, code, client_type, redirect_uri}` 조합이 필요하며 기존 `state` 기반 payload는 400 원인이 된다.
83+
- `ExchangeCode` 시그니처가 `callbackURL` 기반으로 바뀌면 SDK/관리 핸들러 호출부도 함께 맞춰야 컴파일 및 런타임 일관성이 유지된다.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Cline Provider - Problems
2+
3+
(none yet)
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Kilocode API Research - Key Decisions
2+
3+
## Free Model Detection Strategy
4+
Based on research, free models can be identified by:
5+
6+
1. **Primary Method**: Check `pricing` object fields
7+
- `pricing.prompt === "0"`
8+
- `pricing.completion === "0"`
9+
- Both conditions should be true for a model to be considered free
10+
11+
2. **Secondary Method**: Model name contains "(free)" suffix
12+
- Some models explicitly marked as "(free)" in their names
13+
- Examples: "Qwen3 Coder (free)", "GLM 4.5 Air (free)"
14+
15+
3. **Fallback Method**: Check if all pricing fields are "0"
16+
- `pricing.request === "0"`
17+
- `pricing.image === "0"`
18+
19+
## API Integration Approach
20+
- Use existing `getOpenRouterModels()` function pattern
21+
- Endpoint: `https://api.kilo.ai/api/openrouter/v1/models`
22+
- Authentication: Bearer token with Kilocode JWT
23+
- Response format follows OpenRouter standard
24+
25+
## Model Registration Logic
26+
Filter models where:
27+
```javascript
28+
model.pricing &&
29+
model.pricing.prompt === "0" &&
30+
model.pricing.completion === "0"
31+
```
32+
33+
## Error Handling
34+
- Handle cases where pricing object might be missing
35+
- Graceful fallback if API is unavailable
36+
- Log warnings for unexpected response formats

0 commit comments

Comments
 (0)