cc-hub-cli 1.1.15 → 1.1.17
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.
- package/dist/index.js +301 -38
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -10,7 +10,7 @@ import { Command as Command2 } from "commander";
|
|
|
10
10
|
// src/config.ts
|
|
11
11
|
import fs4 from "fs";
|
|
12
12
|
import path4 from "path";
|
|
13
|
-
import
|
|
13
|
+
import os4 from "os";
|
|
14
14
|
|
|
15
15
|
// src/platform/desktop-app.ts
|
|
16
16
|
import fs2 from "fs";
|
|
@@ -266,16 +266,55 @@ function sanitizeToolId(id) {
|
|
|
266
266
|
}
|
|
267
267
|
return sanitized;
|
|
268
268
|
}
|
|
269
|
+
function buildSystemMessage(system) {
|
|
270
|
+
if (typeof system === "string") {
|
|
271
|
+
return { role: "system", content: system };
|
|
272
|
+
}
|
|
273
|
+
if (Array.isArray(system)) {
|
|
274
|
+
const textBlocks = system.filter((b) => b.type === "text" && b.text);
|
|
275
|
+
const hasCacheControl = textBlocks.some((b) => b.cache_control);
|
|
276
|
+
if (hasCacheControl) {
|
|
277
|
+
const parts = textBlocks.map((b) => {
|
|
278
|
+
const part = { type: "text", text: b.text };
|
|
279
|
+
if (b.cache_control) part.cache_control = b.cache_control;
|
|
280
|
+
return part;
|
|
281
|
+
});
|
|
282
|
+
if (parts.length > 0) return { role: "system", content: parts };
|
|
283
|
+
} else {
|
|
284
|
+
const text = textBlocks.map((b) => b.text).join("\n");
|
|
285
|
+
if (text) return { role: "system", content: text };
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return null;
|
|
289
|
+
}
|
|
290
|
+
function convertAnthropicContentPart(part) {
|
|
291
|
+
let convertedPart = null;
|
|
292
|
+
if (part.type === "image") {
|
|
293
|
+
if (part.source?.type === "base64" && part.source.media_type && part.source.data) {
|
|
294
|
+
const url = `data:${part.source.media_type};base64,${part.source.data}`;
|
|
295
|
+
debug(`transform: converting base64 image (${part.source.media_type}, ${part.source.data.length} chars)`);
|
|
296
|
+
convertedPart = { type: "image_url", image_url: { url } };
|
|
297
|
+
} else if (part.source?.type === "url" && part.source.url) {
|
|
298
|
+
debug(`transform: converting image url (${part.source.url.slice(0, 80)}...)`);
|
|
299
|
+
convertedPart = { type: "image_url", image_url: { url: part.source.url } };
|
|
300
|
+
} else {
|
|
301
|
+
warn(`transform: skipping invalid image block (missing source fields)`);
|
|
302
|
+
return null;
|
|
303
|
+
}
|
|
304
|
+
} else if (part.type === "text") {
|
|
305
|
+
convertedPart = { type: "text", text: part.text };
|
|
306
|
+
}
|
|
307
|
+
if (convertedPart && part.cache_control) {
|
|
308
|
+
convertedPart.cache_control = part.cache_control;
|
|
309
|
+
}
|
|
310
|
+
return convertedPart;
|
|
311
|
+
}
|
|
269
312
|
function transformAnthropicToOpenAI(body) {
|
|
270
313
|
debug(`transform: anthropic -> openai model=${body.model} messages=${(body.messages ?? []).length}`);
|
|
271
314
|
const messages = [];
|
|
272
315
|
if (body.system) {
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
} else if (Array.isArray(body.system)) {
|
|
276
|
-
const text = body.system.filter((b) => b.type === "text" && b.text).map((b) => b.text).join("\n");
|
|
277
|
-
if (text) messages.push({ role: "system", content: text });
|
|
278
|
-
}
|
|
316
|
+
const systemMsg = buildSystemMessage(body.system);
|
|
317
|
+
if (systemMsg) messages.push(systemMsg);
|
|
279
318
|
}
|
|
280
319
|
for (const msg of body.messages ?? []) {
|
|
281
320
|
if (typeof msg.content === "string") {
|
|
@@ -301,23 +340,9 @@ function transformAnthropicToOpenAI(body) {
|
|
|
301
340
|
(b) => b.type === "text" && b.text || b.type === "image" && (b.source?.type === "base64" && b.source.media_type && b.source.data || b.source?.type === "url" && b.source.url)
|
|
302
341
|
);
|
|
303
342
|
if (contentParts.length > 0) {
|
|
304
|
-
const converted = contentParts.map((
|
|
305
|
-
if (part.type === "image") {
|
|
306
|
-
if (part.source?.type === "base64" && part.source.media_type && part.source.data) {
|
|
307
|
-
const url = `data:${part.source.media_type};base64,${part.source.data}`;
|
|
308
|
-
debug(`transform: converting base64 image (${part.source.media_type}, ${part.source.data.length} chars)`);
|
|
309
|
-
return { type: "image_url", image_url: { url } };
|
|
310
|
-
} else if (part.source?.type === "url" && part.source.url) {
|
|
311
|
-
debug(`transform: converting image url (${part.source.url.slice(0, 80)}...)`);
|
|
312
|
-
return { type: "image_url", image_url: { url: part.source.url } };
|
|
313
|
-
}
|
|
314
|
-
warn(`transform: skipping invalid image block (missing source fields)`);
|
|
315
|
-
return null;
|
|
316
|
-
}
|
|
317
|
-
return { type: "text", text: part.text };
|
|
318
|
-
}).filter(Boolean);
|
|
343
|
+
const converted = contentParts.map(convertAnthropicContentPart).filter(Boolean);
|
|
319
344
|
if (converted.length === 0) {
|
|
320
|
-
} else if (converted.every((p) => p.type === "text")) {
|
|
345
|
+
} else if (converted.every((p) => p.type === "text") && !converted.some((p) => p.cache_control)) {
|
|
321
346
|
messages.push({
|
|
322
347
|
role: "user",
|
|
323
348
|
content: converted.map((p) => p.text).join("")
|
|
@@ -334,6 +359,12 @@ function transformAnthropicToOpenAI(body) {
|
|
|
334
359
|
if (textParts.length > 0) {
|
|
335
360
|
assistantMsg.content = textParts.map((b) => b.text).join("\n");
|
|
336
361
|
}
|
|
362
|
+
const thinkingParts = msg.content.filter(
|
|
363
|
+
(b) => b.type === "thinking" && b.thinking
|
|
364
|
+
);
|
|
365
|
+
if (thinkingParts.length > 0) {
|
|
366
|
+
assistantMsg.reasoning_content = thinkingParts.map((b) => b.thinking).join("\n");
|
|
367
|
+
}
|
|
337
368
|
const toolUseParts = msg.content.filter(
|
|
338
369
|
(b) => b.type === "tool_use" && b.id
|
|
339
370
|
);
|
|
@@ -386,6 +417,13 @@ function transformOpenAIResponseToAnthropic(openaiResponse, originalModel) {
|
|
|
386
417
|
const choice = openaiResponse.choices?.[0];
|
|
387
418
|
if (!choice) throw new Error("No choices in OpenAI response");
|
|
388
419
|
const content = [];
|
|
420
|
+
if (choice.message?.reasoning_content) {
|
|
421
|
+
content.push({
|
|
422
|
+
type: "thinking",
|
|
423
|
+
thinking: choice.message.reasoning_content,
|
|
424
|
+
signature: ""
|
|
425
|
+
});
|
|
426
|
+
}
|
|
389
427
|
if (choice.message?.content) {
|
|
390
428
|
content.push({ type: "text", text: choice.message.content });
|
|
391
429
|
}
|
|
@@ -422,7 +460,8 @@ function transformOpenAIResponseToAnthropic(openaiResponse, originalModel) {
|
|
|
422
460
|
usage: {
|
|
423
461
|
input_tokens: (openaiResponse.usage?.prompt_tokens ?? 0) - (openaiResponse.usage?.prompt_tokens_details?.cached_tokens ?? 0),
|
|
424
462
|
output_tokens: openaiResponse.usage?.completion_tokens ?? 0,
|
|
425
|
-
cache_read_input_tokens: openaiResponse.usage?.prompt_tokens_details?.cached_tokens ?? 0
|
|
463
|
+
cache_read_input_tokens: openaiResponse.usage?.prompt_tokens_details?.cached_tokens ?? 0,
|
|
464
|
+
cache_creation_input_tokens: openaiResponse.usage?.prompt_tokens_details?.cache_creation_tokens ?? 0
|
|
426
465
|
}
|
|
427
466
|
};
|
|
428
467
|
}
|
|
@@ -445,7 +484,8 @@ data: ${JSON.stringify(data)}
|
|
|
445
484
|
usage: {
|
|
446
485
|
input_tokens: usage.input_tokens ?? 0,
|
|
447
486
|
output_tokens: 0,
|
|
448
|
-
cache_read_input_tokens: usage.cache_read_input_tokens ?? 0
|
|
487
|
+
cache_read_input_tokens: usage.cache_read_input_tokens ?? 0,
|
|
488
|
+
cache_creation_input_tokens: usage.cache_creation_input_tokens ?? 0
|
|
449
489
|
}
|
|
450
490
|
}
|
|
451
491
|
});
|
|
@@ -454,7 +494,7 @@ data: ${JSON.stringify(data)}
|
|
|
454
494
|
yield sse("content_block_start", {
|
|
455
495
|
type: "content_block_start",
|
|
456
496
|
index: i,
|
|
457
|
-
content_block: block.type === "tool_use" ? { type: "tool_use", id: block.id, name: block.name, input: {} } : { type: "text", text: "" }
|
|
497
|
+
content_block: block.type === "tool_use" ? { type: "tool_use", id: block.id, name: block.name, input: {} } : block.type === "thinking" ? { type: "thinking", thinking: "", signature: block.signature ?? "" } : { type: "text", text: "" }
|
|
458
498
|
});
|
|
459
499
|
if (block.type === "text" && block.text) {
|
|
460
500
|
yield sse("content_block_delta", {
|
|
@@ -462,6 +502,12 @@ data: ${JSON.stringify(data)}
|
|
|
462
502
|
index: i,
|
|
463
503
|
delta: { type: "text_delta", text: block.text }
|
|
464
504
|
});
|
|
505
|
+
} else if (block.type === "thinking" && block.thinking) {
|
|
506
|
+
yield sse("content_block_delta", {
|
|
507
|
+
type: "content_block_delta",
|
|
508
|
+
index: i,
|
|
509
|
+
delta: { type: "thinking_delta", thinking: block.thinking }
|
|
510
|
+
});
|
|
465
511
|
} else if (block.type === "tool_use" && block.input) {
|
|
466
512
|
yield sse("content_block_delta", {
|
|
467
513
|
type: "content_block_delta",
|
|
@@ -482,9 +528,184 @@ data: ${JSON.stringify(data)}
|
|
|
482
528
|
yield sse("message_stop", { type: "message_stop" });
|
|
483
529
|
}
|
|
484
530
|
|
|
531
|
+
// src/provider/kimi.ts
|
|
532
|
+
function transformAnthropicToKimi(body) {
|
|
533
|
+
debug(`transform: anthropic -> kimi model=${body.model} messages=${(body.messages ?? []).length}`);
|
|
534
|
+
const messages = [];
|
|
535
|
+
debug(`transform: anthropic -> kimi messageCount=${(body.messages ?? []).length}`);
|
|
536
|
+
if (body.system) {
|
|
537
|
+
const systemMsg = buildSystemMessage(body.system);
|
|
538
|
+
if (systemMsg) messages.push(systemMsg);
|
|
539
|
+
}
|
|
540
|
+
for (const msg of body.messages ?? []) {
|
|
541
|
+
if (typeof msg.content === "string") {
|
|
542
|
+
messages.push({ role: msg.role, content: msg.content });
|
|
543
|
+
continue;
|
|
544
|
+
}
|
|
545
|
+
if (!Array.isArray(msg.content)) {
|
|
546
|
+
messages.push({ role: msg.role, content: JSON.stringify(msg.content) });
|
|
547
|
+
continue;
|
|
548
|
+
}
|
|
549
|
+
if (msg.role === "user") {
|
|
550
|
+
const toolResults = msg.content.filter(
|
|
551
|
+
(b) => b.type === "tool_result" && b.tool_use_id
|
|
552
|
+
);
|
|
553
|
+
for (const tr of toolResults) {
|
|
554
|
+
messages.push({
|
|
555
|
+
role: "tool",
|
|
556
|
+
tool_call_id: sanitizeToolId(tr.tool_use_id),
|
|
557
|
+
content: typeof tr.content === "string" ? tr.content : Array.isArray(tr.content) ? tr.content.filter((b) => b.type === "text").map((b) => b.text).join("") : JSON.stringify(tr.content)
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
const contentParts = msg.content.filter(
|
|
561
|
+
(b) => b.type === "text" && b.text || b.type === "image" && (b.source?.type === "base64" && b.source.media_type && b.source.data || b.source?.type === "url" && b.source.url)
|
|
562
|
+
);
|
|
563
|
+
if (contentParts.length > 0) {
|
|
564
|
+
const converted = contentParts.map(convertAnthropicContentPart).filter(Boolean);
|
|
565
|
+
if (converted.length === 0) {
|
|
566
|
+
} else if (converted.every((p) => p.type === "text") && !converted.some((p) => p.cache_control)) {
|
|
567
|
+
messages.push({
|
|
568
|
+
role: "user",
|
|
569
|
+
content: converted.map((p) => p.text).join("")
|
|
570
|
+
});
|
|
571
|
+
} else {
|
|
572
|
+
messages.push({ role: "user", content: converted });
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
} else if (msg.role === "assistant") {
|
|
576
|
+
const assistantMsg = { role: "assistant", content: null };
|
|
577
|
+
const textParts = msg.content.filter(
|
|
578
|
+
(b) => b.type === "text" && b.text
|
|
579
|
+
);
|
|
580
|
+
if (textParts.length > 0) {
|
|
581
|
+
assistantMsg.content = textParts.map((b) => b.text).join("\n");
|
|
582
|
+
}
|
|
583
|
+
const thinkingParts = msg.content.filter(
|
|
584
|
+
(b) => b.type === "thinking" && b.thinking
|
|
585
|
+
);
|
|
586
|
+
const toolUseParts = msg.content.filter(
|
|
587
|
+
(b) => b.type === "tool_use" && b.id
|
|
588
|
+
);
|
|
589
|
+
if (toolUseParts.length > 0) {
|
|
590
|
+
assistantMsg.tool_calls = toolUseParts.map((b) => ({
|
|
591
|
+
id: sanitizeToolId(b.id),
|
|
592
|
+
type: "function",
|
|
593
|
+
function: {
|
|
594
|
+
name: b.name,
|
|
595
|
+
arguments: JSON.stringify(b.input ?? {})
|
|
596
|
+
}
|
|
597
|
+
}));
|
|
598
|
+
if (thinkingParts.length > 0) {
|
|
599
|
+
assistantMsg.reasoning_content = thinkingParts.map((b) => b.thinking).join("\n");
|
|
600
|
+
} else {
|
|
601
|
+
assistantMsg.reasoning_content = " ";
|
|
602
|
+
}
|
|
603
|
+
} else if (thinkingParts.length > 0) {
|
|
604
|
+
assistantMsg.reasoning_content = thinkingParts.map((b) => b.thinking).join("\n");
|
|
605
|
+
}
|
|
606
|
+
messages.push(assistantMsg);
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
const result = {
|
|
610
|
+
model: body.model,
|
|
611
|
+
messages,
|
|
612
|
+
stream: body.stream ?? false
|
|
613
|
+
};
|
|
614
|
+
if (body.max_tokens != null) result.max_tokens = body.max_tokens;
|
|
615
|
+
if (body.temperature != null) result.temperature = body.temperature;
|
|
616
|
+
if (body.tools?.length) {
|
|
617
|
+
debug(`transform: mapping ${body.tools.length} tool(s)`);
|
|
618
|
+
result.tools = body.tools.map((t) => ({
|
|
619
|
+
type: "function",
|
|
620
|
+
function: {
|
|
621
|
+
name: t.name,
|
|
622
|
+
description: t.description ?? "",
|
|
623
|
+
parameters: t.input_schema
|
|
624
|
+
}
|
|
625
|
+
}));
|
|
626
|
+
if (body.tool_choice) {
|
|
627
|
+
const tc = body.tool_choice;
|
|
628
|
+
if (tc.type === "auto" || tc.type === "none" || tc.type === "required") {
|
|
629
|
+
result.tool_choice = tc.type;
|
|
630
|
+
} else if (tc.type === "tool") {
|
|
631
|
+
result.tool_choice = {
|
|
632
|
+
type: "function",
|
|
633
|
+
function: { name: tc.name }
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
return result;
|
|
639
|
+
}
|
|
640
|
+
function transformKimiResponseToAnthropic(kimiResponse, originalModel) {
|
|
641
|
+
debug(`transform: kimi -> anthropic model=${kimiResponse.model ?? originalModel} choices=${kimiResponse.choices?.length ?? 0}`);
|
|
642
|
+
const choice = kimiResponse.choices?.[0];
|
|
643
|
+
if (!choice) throw new Error("No choices in Kimi response");
|
|
644
|
+
const content = [];
|
|
645
|
+
if (choice.message?.reasoning_content) {
|
|
646
|
+
content.push({
|
|
647
|
+
type: "thinking",
|
|
648
|
+
thinking: choice.message.reasoning_content,
|
|
649
|
+
signature: ""
|
|
650
|
+
});
|
|
651
|
+
}
|
|
652
|
+
if (choice.message?.content) {
|
|
653
|
+
content.push({ type: "text", text: choice.message.content });
|
|
654
|
+
}
|
|
655
|
+
if (choice.message?.tool_calls?.length) {
|
|
656
|
+
for (const tc of choice.message.tool_calls) {
|
|
657
|
+
let input = {};
|
|
658
|
+
try {
|
|
659
|
+
input = typeof tc.function.arguments === "string" ? JSON.parse(tc.function.arguments) : tc.function.arguments;
|
|
660
|
+
} catch {
|
|
661
|
+
input = { text: tc.function.arguments ?? "" };
|
|
662
|
+
}
|
|
663
|
+
content.push({
|
|
664
|
+
type: "tool_use",
|
|
665
|
+
id: tc.id,
|
|
666
|
+
name: tc.function.name,
|
|
667
|
+
input
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
const finishMap = {
|
|
672
|
+
stop: "end_turn",
|
|
673
|
+
length: "max_tokens",
|
|
674
|
+
tool_calls: "tool_use",
|
|
675
|
+
content_filter: "stop_sequence"
|
|
676
|
+
};
|
|
677
|
+
const cachedTokens = kimiResponse.usage?.prompt_tokens_details?.cached_tokens ?? kimiResponse.usage?.cached_tokens ?? 0;
|
|
678
|
+
return {
|
|
679
|
+
id: kimiResponse.id ?? `msg_${Date.now()}`,
|
|
680
|
+
type: "message",
|
|
681
|
+
role: "assistant",
|
|
682
|
+
model: originalModel,
|
|
683
|
+
content,
|
|
684
|
+
stop_reason: finishMap[choice.finish_reason] ?? "end_turn",
|
|
685
|
+
stop_sequence: null,
|
|
686
|
+
usage: {
|
|
687
|
+
input_tokens: (kimiResponse.usage?.prompt_tokens ?? 0) - cachedTokens,
|
|
688
|
+
output_tokens: kimiResponse.usage?.completion_tokens ?? 0,
|
|
689
|
+
cache_read_input_tokens: cachedTokens,
|
|
690
|
+
cache_creation_input_tokens: kimiResponse.usage?.prompt_tokens_details?.cache_creation_tokens ?? 0
|
|
691
|
+
}
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
|
|
485
695
|
// src/provider/server.ts
|
|
486
696
|
import http from "http";
|
|
487
|
-
|
|
697
|
+
import os3 from "os";
|
|
698
|
+
function getKimiHeaders(claudeVersion) {
|
|
699
|
+
const platform = os3.platform();
|
|
700
|
+
const stainlessOS = platform === "darwin" ? "MacOS" : platform === "win32" ? "Windows" : "Linux";
|
|
701
|
+
return {
|
|
702
|
+
"X-Stainless-OS": stainlessOS,
|
|
703
|
+
"X-Stainless-Package-Version": "1.1.17",
|
|
704
|
+
"X-Stainless-Runtime": "node",
|
|
705
|
+
"User-Agent": `claude-code/${claudeVersion}`
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
async function startOpenAIProxy(targetUrl, apiKey, model, models = [], modelMappings = {}, provider = "openai", claudeVersion = "0.0.0") {
|
|
488
709
|
const base = targetUrl.replace(/\/+$/, "");
|
|
489
710
|
const server = http.createServer(async (req, res) => {
|
|
490
711
|
debug(`Proxy request: ${req.method} ${req.url}`);
|
|
@@ -512,7 +733,8 @@ async function startOpenAIProxy(targetUrl, apiKey, model, models = [], modelMapp
|
|
|
512
733
|
const isStream = !!parsed.stream;
|
|
513
734
|
const requestModel = parsed.model ?? model;
|
|
514
735
|
const actualModel = modelMappings[requestModel] || requestModel;
|
|
515
|
-
const
|
|
736
|
+
const transformBody = provider === "kimi" ? transformAnthropicToKimi : transformAnthropicToOpenAI;
|
|
737
|
+
const openaiBody = transformBody({ ...parsed, model: actualModel, stream: false });
|
|
516
738
|
if (isStream) {
|
|
517
739
|
res.writeHead(200, {
|
|
518
740
|
"Content-Type": "text/event-stream",
|
|
@@ -525,7 +747,8 @@ async function startOpenAIProxy(targetUrl, apiKey, model, models = [], modelMapp
|
|
|
525
747
|
method: "POST",
|
|
526
748
|
headers: {
|
|
527
749
|
"Content-Type": "application/json",
|
|
528
|
-
"Authorization": `Bearer ${apiKey}
|
|
750
|
+
"Authorization": `Bearer ${apiKey}`,
|
|
751
|
+
...provider === "kimi" ? getKimiHeaders(claudeVersion) : {}
|
|
529
752
|
},
|
|
530
753
|
body: JSON.stringify(openaiBody)
|
|
531
754
|
});
|
|
@@ -540,7 +763,8 @@ data: ${errText}
|
|
|
540
763
|
return;
|
|
541
764
|
}
|
|
542
765
|
const data2 = await upstream2.json();
|
|
543
|
-
const
|
|
766
|
+
const transformResponse2 = provider === "kimi" ? transformKimiResponseToAnthropic : transformOpenAIResponseToAnthropic;
|
|
767
|
+
const anthropicResponse2 = transformResponse2(data2, parsed.model ?? model);
|
|
544
768
|
for (const chunk of synthesizeAnthropicSSE(anthropicResponse2)) {
|
|
545
769
|
res.write(chunk);
|
|
546
770
|
}
|
|
@@ -554,7 +778,8 @@ data: ${errText}
|
|
|
554
778
|
method: "POST",
|
|
555
779
|
headers: {
|
|
556
780
|
"Content-Type": "application/json",
|
|
557
|
-
"Authorization": `Bearer ${apiKey}
|
|
781
|
+
"Authorization": `Bearer ${apiKey}`,
|
|
782
|
+
...provider === "kimi" ? getKimiHeaders(claudeVersion) : {}
|
|
558
783
|
},
|
|
559
784
|
body: JSON.stringify(openaiBody)
|
|
560
785
|
});
|
|
@@ -566,7 +791,8 @@ data: ${errText}
|
|
|
566
791
|
return;
|
|
567
792
|
}
|
|
568
793
|
const data = await upstream.json();
|
|
569
|
-
const
|
|
794
|
+
const transformResponse = provider === "kimi" ? transformKimiResponseToAnthropic : transformOpenAIResponseToAnthropic;
|
|
795
|
+
const anthropicResponse = transformResponse(data, parsed.model ?? model);
|
|
570
796
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
571
797
|
res.end(JSON.stringify(anthropicResponse));
|
|
572
798
|
return;
|
|
@@ -615,6 +841,10 @@ var PROVIDERS = [
|
|
|
615
841
|
{
|
|
616
842
|
name: "openai",
|
|
617
843
|
description: "Embedded proxy \u2014 translates Anthropic requests to OpenAI Chat Completions format"
|
|
844
|
+
},
|
|
845
|
+
{
|
|
846
|
+
name: "kimi",
|
|
847
|
+
description: "Embedded proxy \u2014 translates Anthropic requests to Kimi API format (handles reasoning_content compatibility)"
|
|
618
848
|
}
|
|
619
849
|
];
|
|
620
850
|
var ANTHROPIC_ALIASES = ["claude-sonnet-4-6", "claude-opus-4-7", "claude-haiku-4-5"];
|
|
@@ -763,6 +993,29 @@ var DesktopProfileSyncer = class {
|
|
|
763
993
|
|
|
764
994
|
// src/platform/binary-resolver.ts
|
|
765
995
|
import { spawnSync } from "child_process";
|
|
996
|
+
var cachedVersion;
|
|
997
|
+
function getClaudeVersion() {
|
|
998
|
+
if (cachedVersion) return cachedVersion;
|
|
999
|
+
debug("binary-resolver: detecting Claude version");
|
|
1000
|
+
try {
|
|
1001
|
+
const result = spawnSync("claude", ["--version"], {
|
|
1002
|
+
shell: process.platform === "win32",
|
|
1003
|
+
encoding: "utf-8"
|
|
1004
|
+
});
|
|
1005
|
+
if (result.status === 0 && result.stdout) {
|
|
1006
|
+
const match = result.stdout.trim().match(/^(\d+\.\d+\.\d+)/);
|
|
1007
|
+
if (match) {
|
|
1008
|
+
cachedVersion = match[1];
|
|
1009
|
+
debug(`binary-resolver: detected Claude version ${cachedVersion}`);
|
|
1010
|
+
return cachedVersion;
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
} catch {
|
|
1014
|
+
}
|
|
1015
|
+
cachedVersion = "0.0.0";
|
|
1016
|
+
debug("binary-resolver: could not detect Claude version, using default");
|
|
1017
|
+
return cachedVersion;
|
|
1018
|
+
}
|
|
766
1019
|
var SystemBinaryResolver = class {
|
|
767
1020
|
constructor(app) {
|
|
768
1021
|
this.app = app;
|
|
@@ -831,10 +1084,10 @@ function createPathCodec() {
|
|
|
831
1084
|
}
|
|
832
1085
|
|
|
833
1086
|
// src/config.ts
|
|
834
|
-
var CLAUDE_DIR = process.env.CLAUDE_DIR || path4.join(
|
|
1087
|
+
var CLAUDE_DIR = process.env.CLAUDE_DIR || path4.join(os4.homedir(), ".claude");
|
|
835
1088
|
var PROFILES_FILE = process.env.CLAUDE_PROFILES_FILE || path4.join(CLAUDE_DIR, "profiles.json");
|
|
836
1089
|
var SETTINGS_FILE = process.env.CLAUDE_SETTINGS_FILE || path4.join(CLAUDE_DIR, "settings.json");
|
|
837
|
-
var CLAUDE_JSON = path4.join(
|
|
1090
|
+
var CLAUDE_JSON = path4.join(os4.homedir(), ".claude.json");
|
|
838
1091
|
var PROJECTS_DIR = path4.join(CLAUDE_DIR, "projects");
|
|
839
1092
|
var SESSIONS_DIR = path4.join(CLAUDE_DIR, "sessions");
|
|
840
1093
|
var desktopApp = createDesktopApp();
|
|
@@ -996,15 +1249,17 @@ function execClaude(profileName, p, extraArgs) {
|
|
|
996
1249
|
}
|
|
997
1250
|
delete env.ANTHROPIC_API_KEY;
|
|
998
1251
|
info(`Launching Claude with profile '${profileName}': model=${firstModel || "(default)"} url=${p.url || "(default)"} provider=${p.provider || "anthropic"} binary=${binary}`);
|
|
999
|
-
if (p.provider === "openai") {
|
|
1252
|
+
if (p.provider === "openai" || p.provider === "kimi") {
|
|
1000
1253
|
const allModels = p.models || (p.model ? [p.model] : []);
|
|
1001
|
-
debug(`execClaude: starting
|
|
1254
|
+
debug(`execClaude: starting ${p.provider} proxy for ${allModels.length} model(s)`);
|
|
1002
1255
|
startOpenAIProxy(
|
|
1003
1256
|
p.url || "https://api.openai.com",
|
|
1004
1257
|
p.token || "",
|
|
1005
1258
|
firstModel || "gpt-4o",
|
|
1006
1259
|
allModels,
|
|
1007
|
-
{}
|
|
1260
|
+
{},
|
|
1261
|
+
p.provider,
|
|
1262
|
+
getClaudeVersion()
|
|
1008
1263
|
).then(({ baseUrl, stop }) => {
|
|
1009
1264
|
env.ANTHROPIC_BASE_URL = baseUrl;
|
|
1010
1265
|
debug(`execClaude: proxy running at ${baseUrl}`);
|
|
@@ -2566,7 +2821,15 @@ function proxyCommand() {
|
|
|
2566
2821
|
}
|
|
2567
2822
|
models = [defaultModel];
|
|
2568
2823
|
}
|
|
2569
|
-
const { baseUrl, stop } = await startOpenAIProxy(
|
|
2824
|
+
const { baseUrl, stop } = await startOpenAIProxy(
|
|
2825
|
+
targetUrl,
|
|
2826
|
+
apiKey,
|
|
2827
|
+
defaultModel,
|
|
2828
|
+
models,
|
|
2829
|
+
modelMappings,
|
|
2830
|
+
"openai",
|
|
2831
|
+
getClaudeVersion()
|
|
2832
|
+
);
|
|
2570
2833
|
console.log(`Proxy running at ${baseUrl}`);
|
|
2571
2834
|
console.log("Press Ctrl+C to stop");
|
|
2572
2835
|
process.on("SIGINT", () => {
|