commandcode-api-proxy 0.1.2
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/LICENSE +21 -0
- package/README.md +176 -0
- package/dist/auth.d.ts +7 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +77 -0
- package/dist/auth.js.map +1 -0
- package/dist/config.d.ts +22 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +67 -0
- package/dist/config.js.map +1 -0
- package/dist/logger.d.ts +9 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +36 -0
- package/dist/logger.js.map +1 -0
- package/dist/models.json +89 -0
- package/dist/proxy.d.ts +3 -0
- package/dist/proxy.d.ts.map +1 -0
- package/dist/proxy.js +80 -0
- package/dist/proxy.js.map +1 -0
- package/dist/server.d.ts +4 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +259 -0
- package/dist/server.js.map +1 -0
- package/dist/setup/opencode.d.ts +2 -0
- package/dist/setup/opencode.d.ts.map +1 -0
- package/dist/setup/opencode.js +58 -0
- package/dist/setup/opencode.js.map +1 -0
- package/dist/stream.d.ts +38 -0
- package/dist/stream.d.ts.map +1 -0
- package/dist/stream.js +89 -0
- package/dist/stream.js.map +1 -0
- package/dist/translate/models.d.ts +9 -0
- package/dist/translate/models.d.ts.map +1 -0
- package/dist/translate/models.js +41 -0
- package/dist/translate/models.js.map +1 -0
- package/dist/translate/openai.d.ts +13 -0
- package/dist/translate/openai.d.ts.map +1 -0
- package/dist/translate/openai.js +470 -0
- package/dist/translate/openai.js.map +1 -0
- package/dist/translate/types.d.ts +110 -0
- package/dist/translate/types.d.ts.map +1 -0
- package/dist/translate/types.js +3 -0
- package/dist/translate/types.js.map +1 -0
- package/dist/translate/util.d.ts +12 -0
- package/dist/translate/util.d.ts.map +1 -0
- package/dist/translate/util.js +114 -0
- package/dist/translate/util.js.map +1 -0
- package/dist/translate/validation.d.ts +6 -0
- package/dist/translate/validation.d.ts.map +1 -0
- package/dist/translate/validation.js +34 -0
- package/dist/translate/validation.js.map +1 -0
- package/dist/upstream.d.ts +41 -0
- package/dist/upstream.d.ts.map +1 -0
- package/dist/upstream.js +167 -0
- package/dist/upstream.js.map +1 -0
- package/package.json +55 -0
package/dist/server.js
ADDED
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import http from "node:http";
|
|
2
|
+
import { URL } from "node:url";
|
|
3
|
+
import { toCCRequest, toOpenAIStreamChunk, toOpenAIErrorChunk, buildNonStreamingResponse, } from "./translate/openai.js";
|
|
4
|
+
import { getDefaultModels, fetchModelList } from "./translate/models.js";
|
|
5
|
+
import { resetMessageId } from "./translate/util.js";
|
|
6
|
+
import { formatSSE, formatSSEDone } from "./stream.js";
|
|
7
|
+
import { sendToCC, collectEvents, UpstreamError } from "./upstream.js";
|
|
8
|
+
import { logger } from "./logger.js";
|
|
9
|
+
import { validateOpenAIChatRequest, ValidationError } from "./translate/validation.js";
|
|
10
|
+
// ──────────────────────────────────────────
|
|
11
|
+
// Mutable server state
|
|
12
|
+
// ──────────────────────────────────────────
|
|
13
|
+
let config;
|
|
14
|
+
let modelList = getDefaultModels();
|
|
15
|
+
function updateModelList(models) {
|
|
16
|
+
if (models.length > 0)
|
|
17
|
+
modelList = models;
|
|
18
|
+
}
|
|
19
|
+
// ──────────────────────────────────────────
|
|
20
|
+
// Request body parser
|
|
21
|
+
// ──────────────────────────────────────────
|
|
22
|
+
function parseBody(req) {
|
|
23
|
+
return new Promise((resolve, reject) => {
|
|
24
|
+
const chunks = [];
|
|
25
|
+
req.on("data", (chunk) => chunks.push(chunk));
|
|
26
|
+
req.on("end", () => {
|
|
27
|
+
const raw = Buffer.concat(chunks).toString("utf-8");
|
|
28
|
+
if (!raw)
|
|
29
|
+
return resolve(null);
|
|
30
|
+
try {
|
|
31
|
+
resolve(JSON.parse(raw));
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
reject(new Error("Invalid JSON body"));
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
req.on("error", reject);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
// ──────────────────────────────────────────
|
|
41
|
+
// Auth
|
|
42
|
+
// ──────────────────────────────────────────
|
|
43
|
+
function extractApiKey(req) {
|
|
44
|
+
let key = null;
|
|
45
|
+
const auth = req.headers.authorization;
|
|
46
|
+
if (auth) {
|
|
47
|
+
const m = auth.match(/^Bearer\s+(.+)$/i);
|
|
48
|
+
if (m)
|
|
49
|
+
key = m[1];
|
|
50
|
+
}
|
|
51
|
+
if (!key) {
|
|
52
|
+
const xApiKey = req.headers["x-api-key"];
|
|
53
|
+
if (xApiKey)
|
|
54
|
+
key = xApiKey;
|
|
55
|
+
}
|
|
56
|
+
// If the client sent "proxy-managed" or no key, fall back to the proxy's configured key
|
|
57
|
+
if (!key || key === "proxy-managed" || key === "placeholder") {
|
|
58
|
+
logger.debug(`client key sentinel, using config key (prefix: "${config.apiKey?.slice(0, 8)}")`);
|
|
59
|
+
return config.apiKey;
|
|
60
|
+
}
|
|
61
|
+
logger.debug(`using client's own key (prefix: "${key.slice(0, 8)}")`);
|
|
62
|
+
return key;
|
|
63
|
+
}
|
|
64
|
+
// ──────────────────────────────────────────
|
|
65
|
+
// Response helpers
|
|
66
|
+
// ──────────────────────────────────────────
|
|
67
|
+
function sendJson(res, status, data) {
|
|
68
|
+
res.writeHead(status, {
|
|
69
|
+
"Content-Type": "application/json",
|
|
70
|
+
...corsHeaders(),
|
|
71
|
+
});
|
|
72
|
+
res.end(JSON.stringify(data));
|
|
73
|
+
}
|
|
74
|
+
function corsHeaders() {
|
|
75
|
+
return {
|
|
76
|
+
"Access-Control-Allow-Origin": "*",
|
|
77
|
+
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
|
|
78
|
+
"Access-Control-Allow-Headers": "Content-Type, Authorization, x-api-key",
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
// ──────────────────────────────────────────
|
|
82
|
+
// Helpers
|
|
83
|
+
// ──────────────────────────────────────────
|
|
84
|
+
function abortOnClientDisconnect(req, res) {
|
|
85
|
+
const abort = new AbortController();
|
|
86
|
+
req.on("close", () => {
|
|
87
|
+
if (!res.writableEnded)
|
|
88
|
+
abort.abort();
|
|
89
|
+
});
|
|
90
|
+
return abort;
|
|
91
|
+
}
|
|
92
|
+
function destroyStreamOnClientDisconnect(req, stream) {
|
|
93
|
+
req.on("close", () => stream.destroy());
|
|
94
|
+
}
|
|
95
|
+
// ──────────────────────────────────────────
|
|
96
|
+
// Route handlers
|
|
97
|
+
// ──────────────────────────────────────────
|
|
98
|
+
function handleHealth(_req, res) {
|
|
99
|
+
sendJson(res, 200, {
|
|
100
|
+
status: "ok",
|
|
101
|
+
version: process.env.npm_package_version ?? "0.1.0",
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
function handleModels(_req, res) {
|
|
105
|
+
const data = {
|
|
106
|
+
object: "list",
|
|
107
|
+
data: modelList.map((id) => ({
|
|
108
|
+
id,
|
|
109
|
+
object: "model",
|
|
110
|
+
created: Math.floor(Date.now() / 1000),
|
|
111
|
+
owned_by: "commandcode",
|
|
112
|
+
})),
|
|
113
|
+
};
|
|
114
|
+
sendJson(res, 200, data);
|
|
115
|
+
}
|
|
116
|
+
async function handleChatCompletions(req, res) {
|
|
117
|
+
let rawBody;
|
|
118
|
+
try {
|
|
119
|
+
rawBody = await parseBody(req);
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
return sendJson(res, 400, { error: "Invalid JSON body" });
|
|
123
|
+
}
|
|
124
|
+
let openAIReq;
|
|
125
|
+
try {
|
|
126
|
+
openAIReq = validateOpenAIChatRequest(rawBody);
|
|
127
|
+
}
|
|
128
|
+
catch (err) {
|
|
129
|
+
if (err instanceof ValidationError) {
|
|
130
|
+
return sendJson(res, 400, { error: err.message });
|
|
131
|
+
}
|
|
132
|
+
return sendJson(res, 400, { error: "Invalid request body" });
|
|
133
|
+
}
|
|
134
|
+
const apiKey = extractApiKey(req);
|
|
135
|
+
if (!apiKey) {
|
|
136
|
+
return sendJson(res, 401, { error: "Unauthorized" });
|
|
137
|
+
}
|
|
138
|
+
const isStream = openAIReq.stream === true;
|
|
139
|
+
const model = openAIReq.model ?? "default";
|
|
140
|
+
logger.info(`[Incoming Request] Model: ${model}`);
|
|
141
|
+
logger.info(`[Incoming Request] Tools count: ${openAIReq.tools ? openAIReq.tools.length : 0}`);
|
|
142
|
+
if (openAIReq.tools && openAIReq.tools.length > 0) {
|
|
143
|
+
logger.info(`[Incoming Request] Tools list: ${openAIReq.tools.map((t) => t.function.name).join(", ")}`);
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
logger.info(`[Incoming Request] No tools were sent by the client!`);
|
|
147
|
+
}
|
|
148
|
+
resetMessageId();
|
|
149
|
+
const ccBody = toCCRequest(openAIReq);
|
|
150
|
+
const abort = abortOnClientDisconnect(req, res);
|
|
151
|
+
try {
|
|
152
|
+
const result = await sendToCC(ccBody, {
|
|
153
|
+
apiBase: config.ccApiBase,
|
|
154
|
+
apiKey,
|
|
155
|
+
ccVersion: config.ccVersion,
|
|
156
|
+
}, abort.signal);
|
|
157
|
+
const stream = result.stream;
|
|
158
|
+
if (isStream) {
|
|
159
|
+
res.writeHead(200, {
|
|
160
|
+
"Content-Type": "text/event-stream",
|
|
161
|
+
"Cache-Control": "no-cache",
|
|
162
|
+
Connection: "keep-alive",
|
|
163
|
+
...corsHeaders(),
|
|
164
|
+
});
|
|
165
|
+
stream.on("data", (event) => {
|
|
166
|
+
for (const chunk of toOpenAIStreamChunk(event, model)) {
|
|
167
|
+
res.write(formatSSE(chunk));
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
stream.on("end", () => {
|
|
171
|
+
res.write(formatSSEDone());
|
|
172
|
+
res.end();
|
|
173
|
+
});
|
|
174
|
+
stream.on("error", (err) => {
|
|
175
|
+
logger.error("[stream] OpenAI streaming error:", err.message);
|
|
176
|
+
if (!res.destroyed) {
|
|
177
|
+
res.write(formatSSE(toOpenAIErrorChunk(err)));
|
|
178
|
+
res.write(formatSSEDone());
|
|
179
|
+
res.end();
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
destroyStreamOnClientDisconnect(req, stream);
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
const events = await collectEvents(stream);
|
|
186
|
+
const response = buildNonStreamingResponse(events, model);
|
|
187
|
+
sendJson(res, 200, response);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
catch (err) {
|
|
191
|
+
handleUpstreamError(res, err);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
// ──────────────────────────────────────────
|
|
195
|
+
// Error handling
|
|
196
|
+
// ──────────────────────────────────────────
|
|
197
|
+
function handleUpstreamError(res, err) {
|
|
198
|
+
if (err instanceof UpstreamError) {
|
|
199
|
+
const status = err.statusCode >= 400 && err.statusCode < 500 ? err.statusCode : 502;
|
|
200
|
+
sendJson(res, status, {
|
|
201
|
+
error: { message: err.message, type: "upstream_error", code: err.statusCode },
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
sendJson(res, 502, {
|
|
206
|
+
error: { message: err.message, type: "proxy_error" },
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
export function createServer(cfg) {
|
|
211
|
+
config = cfg;
|
|
212
|
+
// Start fetching model list in background (only if we have a key to use).
|
|
213
|
+
if (cfg.apiKey) {
|
|
214
|
+
fetchModelList(cfg.ccApiBase, cfg.apiKey)
|
|
215
|
+
.then((models) => {
|
|
216
|
+
if (models.length > 0)
|
|
217
|
+
updateModelList(models);
|
|
218
|
+
})
|
|
219
|
+
.catch(() => {
|
|
220
|
+
/* keep defaults */
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
const routes = [
|
|
224
|
+
{ method: "GET", path: "/health", handler: handleHealth },
|
|
225
|
+
{ method: "GET", path: "/v1/models", handler: handleModels },
|
|
226
|
+
{ method: "POST", path: "/v1/chat/completions", handler: handleChatCompletions },
|
|
227
|
+
];
|
|
228
|
+
const server = http.createServer((req, res) => {
|
|
229
|
+
if (req.method === "OPTIONS") {
|
|
230
|
+
res.writeHead(204, corsHeaders());
|
|
231
|
+
return res.end();
|
|
232
|
+
}
|
|
233
|
+
const parsedUrl = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
|
|
234
|
+
const pathname = parsedUrl.pathname;
|
|
235
|
+
const route = routes.find((r) => r.method === req.method && r.path === pathname);
|
|
236
|
+
if (!route) {
|
|
237
|
+
return sendJson(res, 404, { error: "Not found" });
|
|
238
|
+
}
|
|
239
|
+
try {
|
|
240
|
+
const result = route.handler(req, res, parsedUrl);
|
|
241
|
+
if (result instanceof Promise) {
|
|
242
|
+
result.catch((err) => {
|
|
243
|
+
logger.error("[route] handler promise error:", err);
|
|
244
|
+
if (!res.headersSent) {
|
|
245
|
+
sendJson(res, 500, { error: "Internal server error" });
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
catch (err) {
|
|
251
|
+
logger.error("[route] handler error:", err);
|
|
252
|
+
if (!res.headersSent) {
|
|
253
|
+
sendJson(res, 500, { error: "Internal server error" });
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
return server;
|
|
258
|
+
}
|
|
259
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"opencode.d.ts","sourceRoot":"","sources":["../../src/setup/opencode.ts"],"names":[],"mappings":"AAkDA,wBAAsB,mBAAmB,CAAC,KAAK,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAanF"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import modelsData from "../models.json" with { type: "json" };
|
|
4
|
+
function buildProviderConfig() {
|
|
5
|
+
const contextWindows = modelsData.contextWindows ?? {};
|
|
6
|
+
const maxOutputTokens = modelsData.maxOutputTokens ?? {};
|
|
7
|
+
const modelNames = modelsData.modelNames ?? {};
|
|
8
|
+
const models = {};
|
|
9
|
+
for (const id of modelsData.builtin) {
|
|
10
|
+
const key = id.split("/").pop() ?? id;
|
|
11
|
+
models[key] = {
|
|
12
|
+
name: modelNames[id] ?? key,
|
|
13
|
+
limit: {
|
|
14
|
+
context: contextWindows[id] ?? 128_000,
|
|
15
|
+
output: maxOutputTokens[id] ?? 128_000,
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
npm: "@ai-sdk/openai-compatible",
|
|
21
|
+
name: "Command Code",
|
|
22
|
+
options: { baseURL: "http://127.0.0.1:8787/v1", apiKey: "proxy-managed" },
|
|
23
|
+
models,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function getConfigPath(scope) {
|
|
27
|
+
if (scope === "local") {
|
|
28
|
+
return path.join(process.cwd(), "opencode.json");
|
|
29
|
+
}
|
|
30
|
+
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
31
|
+
return path.join(home, ".config", "opencode", "opencode.json");
|
|
32
|
+
}
|
|
33
|
+
function readConfig(filePath) {
|
|
34
|
+
try {
|
|
35
|
+
const raw = fs.readFileSync(filePath, "utf-8");
|
|
36
|
+
return JSON.parse(raw);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return {};
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function writeConfig(filePath, config) {
|
|
43
|
+
const dir = path.dirname(filePath);
|
|
44
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
45
|
+
fs.writeFileSync(filePath, JSON.stringify(config, null, 2) + "\n");
|
|
46
|
+
}
|
|
47
|
+
export async function setupOpenCodeConfig(scope) {
|
|
48
|
+
const chosen = scope ?? "global";
|
|
49
|
+
const filePath = getConfigPath(chosen);
|
|
50
|
+
const config = readConfig(filePath);
|
|
51
|
+
const providers = (config.provider ?? {});
|
|
52
|
+
providers.commandcode = buildProviderConfig();
|
|
53
|
+
config.provider = providers;
|
|
54
|
+
writeConfig(filePath, config);
|
|
55
|
+
console.log(`\n✓ Config written to ${filePath}`);
|
|
56
|
+
console.log(" Restart OpenCode to use Command Code.\n");
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=opencode.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"opencode.js","sourceRoot":"","sources":["../../src/setup/opencode.ts"],"names":[],"mappings":"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"}
|
package/dist/stream.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Transform, type TransformCallback } from "node:stream";
|
|
2
|
+
import type { CCEvent } from "./translate/types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Result from parsing a single line of CC NDJSON data.
|
|
5
|
+
*/
|
|
6
|
+
export interface ParsedChunk {
|
|
7
|
+
type: "event" | "done" | "ping" | "unknown";
|
|
8
|
+
event?: CCEvent;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Parse a single NDJSON line from the CC stream.
|
|
12
|
+
*/
|
|
13
|
+
export declare function parseCCLine(line: string): ParsedChunk;
|
|
14
|
+
/**
|
|
15
|
+
* Node.js Transform stream that takes raw NDJSON buffer chunks from the CC API
|
|
16
|
+
* and emits parsed CCEvent objects.
|
|
17
|
+
*
|
|
18
|
+
* Usage:
|
|
19
|
+
* ccStream.pipe(ndjsonParser)
|
|
20
|
+
* ndjsonParser.on('data', (event: CCEvent) => { ... })
|
|
21
|
+
* ndjsonParser.on('error', (err) => { ... })
|
|
22
|
+
* ndjsonParser.on('end', () => { ... })
|
|
23
|
+
*/
|
|
24
|
+
export declare class NDJSONParser extends Transform {
|
|
25
|
+
private buffer;
|
|
26
|
+
constructor();
|
|
27
|
+
_transform(chunk: Buffer, _encoding: BufferEncoding, callback: TransformCallback): void;
|
|
28
|
+
_flush(callback: TransformCallback): void;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Format an object as an SSE message string.
|
|
32
|
+
*/
|
|
33
|
+
export declare function formatSSE(data: object): string;
|
|
34
|
+
/**
|
|
35
|
+
* Format the [DONE] signal for non-streaming mode.
|
|
36
|
+
*/
|
|
37
|
+
export declare function formatSSEDone(): string;
|
|
38
|
+
//# sourceMappingURL=stream.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../src/stream.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAEpD;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IAC5C,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CA+BrD;AAED;;;;;;;;;GASG;AACH,qBAAa,YAAa,SAAQ,SAAS;IACzC,OAAO,CAAC,MAAM,CAAM;;IAMpB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,cAAc,EAAE,QAAQ,EAAE,iBAAiB,GAAG,IAAI;IAmBvF,MAAM,CAAC,QAAQ,EAAE,iBAAiB,GAAG,IAAI;CAU1C;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE9C;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAEtC"}
|
package/dist/stream.js
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { Transform } from "node:stream";
|
|
2
|
+
/**
|
|
3
|
+
* Parse a single NDJSON line from the CC stream.
|
|
4
|
+
*/
|
|
5
|
+
export function parseCCLine(line) {
|
|
6
|
+
const trimmed = line.trim();
|
|
7
|
+
if (!trimmed || trimmed === "")
|
|
8
|
+
return { type: "ping" };
|
|
9
|
+
// The CC API sends JSON lines prefixed with "data: "
|
|
10
|
+
// Handle both with and without prefix
|
|
11
|
+
const dataStr = trimmed.startsWith("data: ") ? trimmed.slice(6) : trimmed;
|
|
12
|
+
if (dataStr === "[DONE]")
|
|
13
|
+
return { type: "done" };
|
|
14
|
+
try {
|
|
15
|
+
const parsed = JSON.parse(dataStr);
|
|
16
|
+
// CC API format 1 (flat): { type: "text-delta", id: "txt-0", text: "4" }
|
|
17
|
+
// CC API format 2 (nested): { type: "text-delta", data: { text: "Hello" } }
|
|
18
|
+
// Merge all non-type fields into data.
|
|
19
|
+
if (parsed && typeof parsed === "object" && parsed.type) {
|
|
20
|
+
const { type, id: _id, ...rest } = parsed;
|
|
21
|
+
const data = parsed.data && typeof parsed.data === "object" ? parsed.data : rest;
|
|
22
|
+
return {
|
|
23
|
+
type: "event",
|
|
24
|
+
event: { type, data },
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
return { type: "unknown" };
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
// If we can't parse as JSON, it might be a ping or keepalive
|
|
31
|
+
return { type: "ping" };
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Node.js Transform stream that takes raw NDJSON buffer chunks from the CC API
|
|
36
|
+
* and emits parsed CCEvent objects.
|
|
37
|
+
*
|
|
38
|
+
* Usage:
|
|
39
|
+
* ccStream.pipe(ndjsonParser)
|
|
40
|
+
* ndjsonParser.on('data', (event: CCEvent) => { ... })
|
|
41
|
+
* ndjsonParser.on('error', (err) => { ... })
|
|
42
|
+
* ndjsonParser.on('end', () => { ... })
|
|
43
|
+
*/
|
|
44
|
+
export class NDJSONParser extends Transform {
|
|
45
|
+
buffer = "";
|
|
46
|
+
constructor() {
|
|
47
|
+
super({ readableObjectMode: true, writableObjectMode: false });
|
|
48
|
+
}
|
|
49
|
+
_transform(chunk, _encoding, callback) {
|
|
50
|
+
this.buffer += chunk.toString("utf-8");
|
|
51
|
+
const lines = this.buffer.split("\n");
|
|
52
|
+
// Keep the last (possibly incomplete) line in the buffer
|
|
53
|
+
this.buffer = lines.pop() ?? "";
|
|
54
|
+
for (const line of lines) {
|
|
55
|
+
const result = parseCCLine(line);
|
|
56
|
+
if (result.type === "event" && result.event) {
|
|
57
|
+
this.push(result.event);
|
|
58
|
+
}
|
|
59
|
+
else if (result.type === "done") {
|
|
60
|
+
// Stream complete, will be handled by 'end' event
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
callback();
|
|
65
|
+
}
|
|
66
|
+
_flush(callback) {
|
|
67
|
+
// Process any remaining data in buffer
|
|
68
|
+
if (this.buffer.trim()) {
|
|
69
|
+
const result = parseCCLine(this.buffer);
|
|
70
|
+
if (result.type === "event" && result.event) {
|
|
71
|
+
this.push(result.event);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
callback();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Format an object as an SSE message string.
|
|
79
|
+
*/
|
|
80
|
+
export function formatSSE(data) {
|
|
81
|
+
return `data: ${JSON.stringify(data)}\n\n`;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Format the [DONE] signal for non-streaming mode.
|
|
85
|
+
*/
|
|
86
|
+
export function formatSSEDone() {
|
|
87
|
+
return "data: [DONE]\n\n";
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=stream.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream.js","sourceRoot":"","sources":["../src/stream.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fetch available models from CC provider API. Returns the model list (used by
|
|
3
|
+
* the /v1/models endpoint). Failures yield an empty list — the caller falls
|
|
4
|
+
* back to the built-in defaults.
|
|
5
|
+
*/
|
|
6
|
+
export declare function fetchModelList(apiBase: string, apiKey: string): Promise<string[]>;
|
|
7
|
+
export declare function getDefaultModels(): string[];
|
|
8
|
+
export declare function resolveModel(model: string): string;
|
|
9
|
+
//# sourceMappingURL=models.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../src/translate/models.ts"],"names":[],"mappings":"AAOA;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAavF;AAED,wBAAgB,gBAAgB,IAAI,MAAM,EAAE,CAE3C;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CASlD"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// Model resolution, aliasing, and discovery against the CC provider API.
|
|
2
|
+
import modelsData from "../models.json" with { type: "json" };
|
|
3
|
+
const BUILTIN_MODELS = modelsData.builtin;
|
|
4
|
+
const SHORT_ALIASES = modelsData.shortAliases;
|
|
5
|
+
/**
|
|
6
|
+
* Fetch available models from CC provider API. Returns the model list (used by
|
|
7
|
+
* the /v1/models endpoint). Failures yield an empty list — the caller falls
|
|
8
|
+
* back to the built-in defaults.
|
|
9
|
+
*/
|
|
10
|
+
export async function fetchModelList(apiBase, apiKey) {
|
|
11
|
+
try {
|
|
12
|
+
const url = `${apiBase}/provider/v1/models`;
|
|
13
|
+
const res = await fetch(url, {
|
|
14
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
15
|
+
signal: AbortSignal.timeout(5000),
|
|
16
|
+
});
|
|
17
|
+
if (!res.ok)
|
|
18
|
+
return [];
|
|
19
|
+
const json = (await res.json());
|
|
20
|
+
return json.data?.map((m) => m.id) ?? [];
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return [];
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export function getDefaultModels() {
|
|
27
|
+
return BUILTIN_MODELS;
|
|
28
|
+
}
|
|
29
|
+
export function resolveModel(model) {
|
|
30
|
+
if (!model || model === "default") {
|
|
31
|
+
return BUILTIN_MODELS[0];
|
|
32
|
+
}
|
|
33
|
+
const aliased = SHORT_ALIASES[model];
|
|
34
|
+
if (aliased)
|
|
35
|
+
return aliased;
|
|
36
|
+
// Already a full model ID (contains "/") — pass through untouched.
|
|
37
|
+
if (model.includes("/"))
|
|
38
|
+
return model;
|
|
39
|
+
return model;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=models.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"models.js","sourceRoot":"","sources":["../../src/translate/models.ts"],"names":[],"mappings":"AAAA,yEAAyE;AAEzE,OAAO,UAAU,MAAM,eAAe,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AAE7D,MAAM,cAAc,GAAa,UAAU,CAAC,OAAO,CAAC;AACpD,MAAM,aAAa,GAA2B,UAAU,CAAC,YAAY,CAAC;AAEtE;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,OAAe,EAAE,MAAc;IAClE,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,GAAG,OAAO,qBAAqB,CAAC;QAC5C,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YAC3B,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,MAAM,EAAE,EAAE;YAC9C,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC;SAClC,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,OAAO,EAAE,CAAC;QACvB,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAgC,CAAC;QAC/D,OAAO,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC;IAC3C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,OAAO,cAAc,CAAC;AACxB,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAAa;IACxC,IAAI,CAAC,KAAK,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QAClC,OAAO,cAAc,CAAC,CAAC,CAAC,CAAC;IAC3B,CAAC;IACD,MAAM,OAAO,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IACrC,IAAI,OAAO;QAAE,OAAO,OAAO,CAAC;IAC5B,mEAAmE;IACnE,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IACtC,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { OpenAIChatRequest, CCRequestBody, CCEvent } from "../translate/types.js";
|
|
2
|
+
export declare function toCCRequest(req: OpenAIChatRequest, configOverrides?: Partial<CCRequestBody["config"]>): CCRequestBody;
|
|
3
|
+
/**
|
|
4
|
+
* Translate a single CC event into OpenAI streaming chunks.
|
|
5
|
+
*
|
|
6
|
+
* `model` is the model id requested by the downstream client (echoed back in
|
|
7
|
+
* every chunk per the OpenAI spec). `responseModel` is the model reported by
|
|
8
|
+
* the upstream `start` event, if any — used only to enrich the first chunk.
|
|
9
|
+
*/
|
|
10
|
+
export declare function toOpenAIStreamChunk(event: CCEvent, model?: string): object[];
|
|
11
|
+
export declare function toOpenAIErrorChunk(error: Error): object;
|
|
12
|
+
export declare function buildNonStreamingResponse(events: CCEvent[], model: string): object;
|
|
13
|
+
//# sourceMappingURL=openai.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../src/translate/openai.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,iBAAiB,EASjB,aAAa,EACb,OAAO,EAER,MAAM,sBAAsB,CAAC;AA8H9B,wBAAgB,WAAW,CACzB,GAAG,EAAE,iBAAiB,EACtB,eAAe,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,GACjD,aAAa,CA+Df;AAgCD;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,SAAY,GAAG,MAAM,EAAE,CA8K/E;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CAOvD;AAqBD,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAgFlF"}
|