@sifwenf/cc-proxy 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/bun.lock +31 -0
  2. package/package.json +2 -2
  3. package/src/server.ts +35 -9
package/bun.lock ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "lockfileVersion": 1,
3
+ "configVersion": 1,
4
+ "workspaces": {
5
+ "": {
6
+ "name": "cc-proxy",
7
+ "dependencies": {
8
+ "chokidar": "^5.0.0",
9
+ "hono": "^4.0.0",
10
+ },
11
+ "devDependencies": {
12
+ "@types/bun": "^1.0.0",
13
+ },
14
+ },
15
+ },
16
+ "packages": {
17
+ "@types/bun": ["@types/bun@1.3.9", "", { "dependencies": { "bun-types": "1.3.9" } }, "sha512-KQ571yULOdWJiMH+RIWIOZ7B2RXQGpL1YQrBtLIV3FqDcCu6FsbFUBwhdKUlCKUpS3PJDsHlJ1QKlpxoVR+xtw=="],
18
+
19
+ "@types/node": ["@types/node@25.2.3", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ=="],
20
+
21
+ "bun-types": ["bun-types@1.3.9", "", { "dependencies": { "@types/node": "*" } }, "sha512-+UBWWOakIP4Tswh0Bt0QD0alpTY8cb5hvgiYeWCMet9YukHbzuruIEeXC2D7nMJPB12kbh8C7XJykSexEqGKJg=="],
22
+
23
+ "chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="],
24
+
25
+ "hono": ["hono@4.11.9", "", {}, "sha512-Eaw2YTGM6WOxA6CXbckaEvslr2Ne4NFsKrvc0v97JD5awbmeBLO5w9Ho9L9kmKonrwF9RJlW6BxT1PVv/agBHQ=="],
26
+
27
+ "readdirp": ["readdirp@5.0.0", "", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="],
28
+
29
+ "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
30
+ }
31
+ }
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@sifwenf/cc-proxy",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Lightweight LLM proxy server for Claude Code",
5
5
  "type": "module",
6
6
  "bin": {
7
- "ccp": "./scripts/ccp"
7
+ "ccp": "scripts/ccp"
8
8
  },
9
9
  "scripts": {
10
10
  "start": "bun run src/server.ts",
package/src/server.ts CHANGED
@@ -11,6 +11,29 @@ import chokidar from 'chokidar';
11
11
 
12
12
  const app = new Hono();
13
13
 
14
+ // Strip thinking blocks from messages to avoid signature mismatch errors
15
+ // when switching between different Claude models
16
+ function stripThinkingBlocks(body: any): any {
17
+ if (!body.messages || !Array.isArray(body.messages)) {
18
+ return body;
19
+ }
20
+
21
+ const processed = {
22
+ ...body,
23
+ messages: body.messages.map((msg: any) => {
24
+ if (!msg.content || !Array.isArray(msg.content)) {
25
+ return msg;
26
+ }
27
+ return {
28
+ ...msg,
29
+ content: msg.content.filter((item: any) => item.type !== 'thinking')
30
+ };
31
+ })
32
+ };
33
+
34
+ return processed;
35
+ }
36
+
14
37
  let config;
15
38
  try {
16
39
  config = loadConfig();
@@ -120,16 +143,19 @@ app.post('/v1/messages', async (c) => {
120
143
  const body = await c.req.json();
121
144
  const headers = Object.fromEntries(c.req.raw.headers);
122
145
 
146
+ // Strip thinking blocks to avoid signature mismatch when switching models
147
+ const processedBody = stripThinkingBlocks(body);
148
+
123
149
  // Log request
124
150
  requestId = appLogger.startRequest(
125
151
  'POST',
126
152
  '/v1/messages',
127
153
  headers,
128
- body
154
+ processedBody
129
155
  );
130
156
 
131
157
  // Resolve provider and model mapping
132
- const context = requestMapper.resolveProvider(body.model);
158
+ const context = requestMapper.resolveProvider(processedBody.model);
133
159
  const { provider, modelName } = context;
134
160
 
135
161
  // Normalize format to lowercase
@@ -141,7 +167,7 @@ app.post('/v1/messages', async (c) => {
141
167
 
142
168
  if (format === 'openai') {
143
169
  // Convert Anthropic → OpenAI format
144
- providerRequest = convertAnthropicToOpenAI({ ...body, model: modelName });
170
+ providerRequest = convertAnthropicToOpenAI({ ...processedBody, model: modelName });
145
171
  fetchHeaders = {
146
172
  'Content-Type': 'application/json',
147
173
  'Authorization': `Bearer ${provider.apiKey}`,
@@ -150,7 +176,7 @@ app.post('/v1/messages', async (c) => {
150
176
  };
151
177
  } else if (format === 'anthropic') {
152
178
  // Explicit Anthropic format headers
153
- providerRequest = { ...body, model: modelName };
179
+ providerRequest = { ...processedBody, model: modelName };
154
180
  fetchHeaders = {
155
181
  'Content-Type': 'application/json',
156
182
  'x-api-key': provider.apiKey,
@@ -169,12 +195,12 @@ app.post('/v1/messages', async (c) => {
169
195
  if (provider.apiKey) {
170
196
  fetchHeaders['x-api-key'] = provider.apiKey;
171
197
  }
172
- providerRequest = { ...body, model: modelName };
198
+ providerRequest = { ...processedBody, model: modelName };
173
199
  }
174
200
 
175
201
  // Forward request to provider
176
202
  // Log forward details before sending (only once, not verbose for streaming)
177
- if (!body.stream) {
203
+ if (!processedBody.stream) {
178
204
  appLogger.logForward(
179
205
  provider.name,
180
206
  modelName,
@@ -208,7 +234,7 @@ app.post('/v1/messages', async (c) => {
208
234
  }
209
235
 
210
236
  // Handle streaming response
211
- if (body.stream) {
237
+ if (processedBody.stream) {
212
238
  // Performance optimization: Skip per-chunk logging for streaming
213
239
  // Each SSE chunk would trigger disk I/O, killing performance
214
240
  // Instead, log a single summary after stream completes
@@ -254,7 +280,7 @@ app.post('/v1/messages', async (c) => {
254
280
 
255
281
  // Convert OpenAI format to Anthropic format if needed
256
282
  if (format === 'openai' && responseBody.choices) {
257
- responseBody = convertOpenAIToAnthropic(responseBody, body.model);
283
+ responseBody = convertOpenAIToAnthropic(responseBody, processedBody.model);
258
284
  }
259
285
 
260
286
  // Log response details (combine forward + response into one call)
@@ -264,7 +290,7 @@ app.post('/v1/messages', async (c) => {
264
290
  responseBody,
265
291
  provider.name,
266
292
  modelName,
267
- body.model
293
+ processedBody.model
268
294
  );
269
295
 
270
296
  return c.json(responseBody);