codefree-proxy 0.1.0
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 +58 -0
- package/dist/auth/auth-manager.d.ts +24 -0
- package/dist/auth/auth-manager.js +61 -0
- package/dist/auth/auth-manager.js.map +1 -0
- package/dist/auth/browser-login.d.ts +17 -0
- package/dist/auth/browser-login.js +155 -0
- package/dist/auth/browser-login.js.map +1 -0
- package/dist/auth/codefree-o.d.ts +4 -0
- package/dist/auth/codefree-o.js +30 -0
- package/dist/auth/codefree-o.js.map +1 -0
- package/dist/auth/config-store.d.ts +10 -0
- package/dist/auth/config-store.js +36 -0
- package/dist/auth/config-store.js.map +1 -0
- package/dist/cli.d.ts +9 -0
- package/dist/cli.js +55 -0
- package/dist/cli.js.map +1 -0
- package/dist/compat/zed-tool-call-stream.d.ts +1 -0
- package/dist/compat/zed-tool-call-stream.js +121 -0
- package/dist/compat/zed-tool-call-stream.js.map +1 -0
- package/dist/proxy/model-map.d.ts +3 -0
- package/dist/proxy/model-map.js +6 -0
- package/dist/proxy/model-map.js.map +1 -0
- package/dist/proxy/openai-routes.d.ts +15 -0
- package/dist/proxy/openai-routes.js +177 -0
- package/dist/proxy/openai-routes.js.map +1 -0
- package/dist/proxy/upstream-client.d.ts +9 -0
- package/dist/proxy/upstream-client.js +33 -0
- package/dist/proxy/upstream-client.js.map +1 -0
- package/dist/server.d.ts +8 -0
- package/dist/server.js +13 -0
- package/dist/server.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.js +3 -0
- package/dist/version.js.map +1 -0
- package/package.json +39 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Maxwell
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# codefree-proxy
|
|
2
|
+
|
|
3
|
+
OpenAI-compatible local proxy for SRD Cloud CodeFree.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
Run without installation:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx codefree-proxy
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Or install globally:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install -g codefree-proxy
|
|
17
|
+
codefree-proxy
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Default server:
|
|
21
|
+
|
|
22
|
+
```text
|
|
23
|
+
http://127.0.0.1:8788/v1
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Use any API key value in OpenAI-compatible clients, or omit it if the client allows.
|
|
27
|
+
|
|
28
|
+
## Commands
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
codefree-proxy --port 8788 --host 127.0.0.1
|
|
32
|
+
codefree-proxy login
|
|
33
|
+
codefree-proxy logout
|
|
34
|
+
codefree-proxy status
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Endpoints
|
|
38
|
+
|
|
39
|
+
- `POST /v1/chat/completions`
|
|
40
|
+
- `GET /v1/models`
|
|
41
|
+
- `GET /health`
|
|
42
|
+
- `GET /auth/status`
|
|
43
|
+
- `POST /auth/refresh`
|
|
44
|
+
|
|
45
|
+
## Models
|
|
46
|
+
|
|
47
|
+
- `GLM-5.1`
|
|
48
|
+
- `GLM-4.7`
|
|
49
|
+
- `Qwen3.5-122B-A10B`
|
|
50
|
+
|
|
51
|
+
Unknown model names are mapped to `GLM-5.1`.
|
|
52
|
+
|
|
53
|
+
## Credentials
|
|
54
|
+
|
|
55
|
+
Credentials are stored at:
|
|
56
|
+
|
|
57
|
+
- macOS/Linux: `~/.codefree-proxy/config.json`
|
|
58
|
+
- Windows: `%USERPROFILE%\.codefree-proxy\config.json`
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { StoredCredentials } from './config-store.js';
|
|
2
|
+
interface AuthDeps {
|
|
3
|
+
loadConfig?: () => Promise<StoredCredentials | null>;
|
|
4
|
+
saveConfig?: (credentials: StoredCredentials) => Promise<void>;
|
|
5
|
+
deleteConfig?: () => Promise<void>;
|
|
6
|
+
loadCodeFreeO?: () => Promise<StoredCredentials | null>;
|
|
7
|
+
browserLogin?: () => Promise<StoredCredentials>;
|
|
8
|
+
}
|
|
9
|
+
export declare class AuthManager {
|
|
10
|
+
private cache;
|
|
11
|
+
private source;
|
|
12
|
+
private deps;
|
|
13
|
+
constructor(deps?: AuthDeps);
|
|
14
|
+
getCredentials(): Promise<StoredCredentials>;
|
|
15
|
+
login(): Promise<StoredCredentials>;
|
|
16
|
+
logout(): Promise<void>;
|
|
17
|
+
refresh(): Promise<StoredCredentials>;
|
|
18
|
+
status(): Promise<{
|
|
19
|
+
status: 'ok' | 'missing';
|
|
20
|
+
source?: string;
|
|
21
|
+
userId?: string;
|
|
22
|
+
}>;
|
|
23
|
+
}
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { deleteCredentials, loadCredentials, saveCredentials } from './config-store.js';
|
|
2
|
+
import { loadCodeFreeOCredentials } from './codefree-o.js';
|
|
3
|
+
import { authorizeWithBrowser } from './browser-login.js';
|
|
4
|
+
export class AuthManager {
|
|
5
|
+
cache = null;
|
|
6
|
+
source;
|
|
7
|
+
deps;
|
|
8
|
+
constructor(deps = {}) {
|
|
9
|
+
this.deps = {
|
|
10
|
+
loadConfig: deps.loadConfig ?? loadCredentials,
|
|
11
|
+
saveConfig: deps.saveConfig ?? saveCredentials,
|
|
12
|
+
deleteConfig: deps.deleteConfig ?? deleteCredentials,
|
|
13
|
+
loadCodeFreeO: deps.loadCodeFreeO ?? loadCodeFreeOCredentials,
|
|
14
|
+
browserLogin: deps.browserLogin ?? authorizeWithBrowser
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
async getCredentials() {
|
|
18
|
+
if (this.cache)
|
|
19
|
+
return this.cache;
|
|
20
|
+
const config = await this.deps.loadConfig();
|
|
21
|
+
if (config) {
|
|
22
|
+
this.cache = config;
|
|
23
|
+
this.source = 'config';
|
|
24
|
+
return config;
|
|
25
|
+
}
|
|
26
|
+
const codefreeO = await this.deps.loadCodeFreeO();
|
|
27
|
+
if (codefreeO) {
|
|
28
|
+
this.cache = codefreeO;
|
|
29
|
+
this.source = 'codefree-o';
|
|
30
|
+
await this.deps.saveConfig(codefreeO);
|
|
31
|
+
return codefreeO;
|
|
32
|
+
}
|
|
33
|
+
return this.login();
|
|
34
|
+
}
|
|
35
|
+
async login() {
|
|
36
|
+
const credentials = await this.deps.browserLogin();
|
|
37
|
+
this.cache = credentials;
|
|
38
|
+
this.source = 'browser';
|
|
39
|
+
await this.deps.saveConfig(credentials);
|
|
40
|
+
return credentials;
|
|
41
|
+
}
|
|
42
|
+
async logout() {
|
|
43
|
+
this.cache = null;
|
|
44
|
+
this.source = undefined;
|
|
45
|
+
await this.deps.deleteConfig();
|
|
46
|
+
}
|
|
47
|
+
async refresh() {
|
|
48
|
+
this.cache = null;
|
|
49
|
+
return this.getCredentials();
|
|
50
|
+
}
|
|
51
|
+
async status() {
|
|
52
|
+
try {
|
|
53
|
+
const credentials = await this.getCredentials();
|
|
54
|
+
return { status: 'ok', source: this.source, userId: credentials.userId };
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return { status: 'missing' };
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=auth-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-manager.js","sourceRoot":"","sources":["../../src/auth/auth-manager.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AACvF,OAAO,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAA;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AAUzD,MAAM,OAAO,WAAW;IACd,KAAK,GAA6B,IAAI,CAAA;IACtC,MAAM,CAAoB;IAC1B,IAAI,CAAoB;IAEhC,YAAY,OAAiB,EAAE;QAC7B,IAAI,CAAC,IAAI,GAAG;YACV,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,eAAe;YAC9C,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,eAAe;YAC9C,YAAY,EAAE,IAAI,CAAC,YAAY,IAAI,iBAAiB;YACpD,aAAa,EAAE,IAAI,CAAC,aAAa,IAAI,wBAAwB;YAC7D,YAAY,EAAE,IAAI,CAAC,YAAY,IAAI,oBAAoB;SACxD,CAAA;IACH,CAAC;IAED,KAAK,CAAC,cAAc;QAClB,IAAI,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC,KAAK,CAAA;QAEjC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,CAAA;QAC3C,IAAI,MAAM,EAAE,CAAC;YACX,IAAI,CAAC,KAAK,GAAG,MAAM,CAAA;YACnB,IAAI,CAAC,MAAM,GAAG,QAAQ,CAAA;YACtB,OAAO,MAAM,CAAA;QACf,CAAC;QAED,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAA;QACjD,IAAI,SAAS,EAAE,CAAC;YACd,IAAI,CAAC,KAAK,GAAG,SAAS,CAAA;YACtB,IAAI,CAAC,MAAM,GAAG,YAAY,CAAA;YAC1B,MAAM,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,CAAA;YACrC,OAAO,SAAS,CAAA;QAClB,CAAC;QAED,OAAO,IAAI,CAAC,KAAK,EAAE,CAAA;IACrB,CAAC;IAED,KAAK,CAAC,KAAK;QACT,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,CAAA;QAClD,IAAI,CAAC,KAAK,GAAG,WAAW,CAAA;QACxB,IAAI,CAAC,MAAM,GAAG,SAAS,CAAA;QACvB,MAAM,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,CAAA;QACvC,OAAO,WAAW,CAAA;IACpB,CAAC;IAED,KAAK,CAAC,MAAM;QACV,IAAI,CAAC,KAAK,GAAG,IAAI,CAAA;QACjB,IAAI,CAAC,MAAM,GAAG,SAAS,CAAA;QACvB,MAAM,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,CAAA;IAChC,CAAC;IAED,KAAK,CAAC,OAAO;QACX,IAAI,CAAC,KAAK,GAAG,IAAI,CAAA;QACjB,OAAO,IAAI,CAAC,cAAc,EAAE,CAAA;IAC9B,CAAC;IAED,KAAK,CAAC,MAAM;QACV,IAAI,CAAC;YACH,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,cAAc,EAAE,CAAA;YAC/C,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,CAAA;QAC1E,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,CAAA;QAC9B,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { StoredCredentials } from './config-store.js';
|
|
2
|
+
export interface BrowserLoginOptions {
|
|
3
|
+
openBrowser?: (url: string) => void;
|
|
4
|
+
timeoutMs?: number;
|
|
5
|
+
fetchImpl?: typeof fetch;
|
|
6
|
+
}
|
|
7
|
+
export declare function buildAuthorizeUrl(port: number): {
|
|
8
|
+
url: string;
|
|
9
|
+
state: string;
|
|
10
|
+
};
|
|
11
|
+
export declare function exchangeCodeForToken(code: string, fetchImpl?: typeof fetch): Promise<{
|
|
12
|
+
accessToken: string;
|
|
13
|
+
idToken: string;
|
|
14
|
+
tokenType: string;
|
|
15
|
+
}>;
|
|
16
|
+
export declare function fetchEncryptedApiKey(accessToken: string, idToken: string, fetchImpl?: typeof fetch): Promise<string>;
|
|
17
|
+
export declare function authorizeWithBrowser(options?: BrowserLoginOptions): Promise<StoredCredentials>;
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { createServer } from 'node:http';
|
|
3
|
+
import { URLSearchParams } from 'node:url';
|
|
4
|
+
import { decryptApiKey } from './codefree-o.js';
|
|
5
|
+
const BASE_URL = 'https://www.srdcloud.cn';
|
|
6
|
+
const CLIENT_ID = '2220476020a3tfk6llee';
|
|
7
|
+
const CLIENT_SECRET = '2220476020b1qOplkzHnKCsRJHPUjZldR5c9oxvY';
|
|
8
|
+
const REDIRECT_URI = 'https://www.srdcloud.cn/login/oauth-srdcloud-redirect';
|
|
9
|
+
export function buildAuthorizeUrl(port) {
|
|
10
|
+
const callbackUrl = `http://127.0.0.1:${port}/oauth2callback?from=cli`;
|
|
11
|
+
const state = Buffer.from(callbackUrl, 'utf8').toString('base64');
|
|
12
|
+
const params = new URLSearchParams({
|
|
13
|
+
client_id: CLIENT_ID,
|
|
14
|
+
redirect_uri: REDIRECT_URI,
|
|
15
|
+
response_type: 'code',
|
|
16
|
+
state
|
|
17
|
+
});
|
|
18
|
+
return { url: `${BASE_URL}/login/oauth/authorize?${params.toString()}`, state };
|
|
19
|
+
}
|
|
20
|
+
export async function exchangeCodeForToken(code, fetchImpl = fetch) {
|
|
21
|
+
const body = new URLSearchParams({
|
|
22
|
+
client_id: CLIENT_ID,
|
|
23
|
+
client_secret: CLIENT_SECRET,
|
|
24
|
+
code,
|
|
25
|
+
redirect_uri: REDIRECT_URI
|
|
26
|
+
});
|
|
27
|
+
const response = await fetchImpl(`${BASE_URL}/login/oauth/access_token`, {
|
|
28
|
+
method: 'POST',
|
|
29
|
+
headers: { Accept: '*/*', 'User-Agent': 'node' },
|
|
30
|
+
body
|
|
31
|
+
});
|
|
32
|
+
if (!response.ok)
|
|
33
|
+
throw new Error(`token exchange failed: ${response.status} ${await response.text()}`);
|
|
34
|
+
const parsed = new URLSearchParams(await response.text());
|
|
35
|
+
const accessToken = parsed.get('access_token') ?? '';
|
|
36
|
+
const idToken = parsed.get('id_token') ?? '';
|
|
37
|
+
const tokenType = parsed.get('token_type') ?? '';
|
|
38
|
+
if (!accessToken || !idToken)
|
|
39
|
+
throw new Error('token exchange response missing access_token or id_token');
|
|
40
|
+
return { accessToken, idToken, tokenType };
|
|
41
|
+
}
|
|
42
|
+
export async function fetchEncryptedApiKey(accessToken, idToken, fetchImpl = fetch) {
|
|
43
|
+
const response = await fetchImpl(`${BASE_URL}/api/acbackend/usermanager/v1/users/apikey`, {
|
|
44
|
+
method: 'GET',
|
|
45
|
+
headers: {
|
|
46
|
+
Accept: '*/*',
|
|
47
|
+
'User-Agent': 'node',
|
|
48
|
+
Authorization: `Bearer ${accessToken}`,
|
|
49
|
+
userId: idToken
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
if (!response.ok)
|
|
53
|
+
throw new Error(`apikey fetch failed: ${response.status} ${await response.text()}`);
|
|
54
|
+
const data = await response.json();
|
|
55
|
+
if (!data.apikey)
|
|
56
|
+
throw new Error('apikey response missing apikey');
|
|
57
|
+
return data.apikey;
|
|
58
|
+
}
|
|
59
|
+
function defaultOpenBrowser(url) {
|
|
60
|
+
const command = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'cmd' : 'xdg-open';
|
|
61
|
+
const args = process.platform === 'win32' ? ['/c', 'start', '', url] : [url];
|
|
62
|
+
const child = spawn(command, args, { detached: true, stdio: 'ignore' });
|
|
63
|
+
child.unref();
|
|
64
|
+
}
|
|
65
|
+
function escapeHtml(value) {
|
|
66
|
+
return value
|
|
67
|
+
.replaceAll('&', '&')
|
|
68
|
+
.replaceAll('<', '<')
|
|
69
|
+
.replaceAll('>', '>')
|
|
70
|
+
.replaceAll('"', '"')
|
|
71
|
+
.replaceAll("'", ''');
|
|
72
|
+
}
|
|
73
|
+
function startCallbackServer(timeoutMs) {
|
|
74
|
+
return new Promise((resolve, reject) => {
|
|
75
|
+
let settled = false;
|
|
76
|
+
let timeout;
|
|
77
|
+
let resolveCode;
|
|
78
|
+
let rejectCode;
|
|
79
|
+
const codePromise = new Promise((resolveInner, rejectInner) => {
|
|
80
|
+
resolveCode = resolveInner;
|
|
81
|
+
rejectCode = rejectInner;
|
|
82
|
+
});
|
|
83
|
+
const server = createServer((request, response) => {
|
|
84
|
+
const url = new URL(request.url ?? '/', 'http://127.0.0.1');
|
|
85
|
+
if (url.pathname !== '/oauth2callback') {
|
|
86
|
+
response.writeHead(404);
|
|
87
|
+
response.end('not found');
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const code = url.searchParams.get('code');
|
|
91
|
+
const error = url.searchParams.get('error');
|
|
92
|
+
if (error) {
|
|
93
|
+
response.writeHead(400, { 'content-type': 'text/html; charset=utf-8' });
|
|
94
|
+
response.end(`<h2>Authorization failed</h2><p>${escapeHtml(error)}</p>`);
|
|
95
|
+
if (!settled) {
|
|
96
|
+
settled = true;
|
|
97
|
+
clearTimeout(timeout);
|
|
98
|
+
rejectCode(new Error(`OAuth error: ${error}`));
|
|
99
|
+
}
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
if (!code) {
|
|
103
|
+
response.writeHead(400, { 'content-type': 'text/html; charset=utf-8' });
|
|
104
|
+
response.end('<h2>Authorization failed</h2><p>Missing code</p>');
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
response.writeHead(200, { 'content-type': 'text/html; charset=utf-8' });
|
|
108
|
+
response.end('<h2>Authorization successful</h2><p>You can close this tab.</p>');
|
|
109
|
+
if (!settled) {
|
|
110
|
+
settled = true;
|
|
111
|
+
clearTimeout(timeout);
|
|
112
|
+
resolveCode(code);
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
server.on('error', reject);
|
|
116
|
+
server.listen(0, '127.0.0.1', () => {
|
|
117
|
+
const port = server.address().port;
|
|
118
|
+
timeout = setTimeout(() => {
|
|
119
|
+
if (!settled) {
|
|
120
|
+
settled = true;
|
|
121
|
+
rejectCode(new Error('OAuth callback timed out'));
|
|
122
|
+
}
|
|
123
|
+
}, timeoutMs);
|
|
124
|
+
timeout.unref();
|
|
125
|
+
resolve({
|
|
126
|
+
port,
|
|
127
|
+
codePromise,
|
|
128
|
+
close: () => new Promise((done) => server.close(() => done()))
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
export async function authorizeWithBrowser(options = {}) {
|
|
134
|
+
const timeoutMs = options.timeoutMs ?? 300_000;
|
|
135
|
+
const callback = await startCallbackServer(timeoutMs);
|
|
136
|
+
const codePromise = callback.codePromise;
|
|
137
|
+
const { url } = buildAuthorizeUrl(callback.port);
|
|
138
|
+
try {
|
|
139
|
+
;
|
|
140
|
+
(options.openBrowser ?? defaultOpenBrowser)(url);
|
|
141
|
+
const code = await codePromise;
|
|
142
|
+
const token = await exchangeCodeForToken(code, options.fetchImpl ?? fetch);
|
|
143
|
+
const encryptedApiKey = await fetchEncryptedApiKey(token.accessToken, token.idToken, options.fetchImpl ?? fetch);
|
|
144
|
+
return {
|
|
145
|
+
userId: token.idToken,
|
|
146
|
+
apiKey: decryptApiKey(encryptedApiKey),
|
|
147
|
+
sessionId: token.idToken,
|
|
148
|
+
updatedAt: Date.now()
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
finally {
|
|
152
|
+
await callback.close();
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=browser-login.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-login.js","sourceRoot":"","sources":["../../src/auth/browser-login.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { StoredCredentials } from './config-store.js';
|
|
2
|
+
export declare function getCodeFreeOCredentialPath(homeDir?: string): string;
|
|
3
|
+
export declare function decryptApiKey(encrypted: string): string;
|
|
4
|
+
export declare function loadCodeFreeOCredentials(path?: string): Promise<StoredCredentials | null>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { createDecipheriv } from 'node:crypto';
|
|
5
|
+
const AES_KEY = Buffer.from('Xtpa6sS&+D.NAo%CP8LA:7pk', 'utf8');
|
|
6
|
+
const AES_IV = Buffer.from('%1KJIrl3!XUxr04V', 'utf8');
|
|
7
|
+
export function getCodeFreeOCredentialPath(homeDir = homedir()) {
|
|
8
|
+
return join(homeDir, '.codefree-o', '.local', 'share', 'codefree.json');
|
|
9
|
+
}
|
|
10
|
+
export function decryptApiKey(encrypted) {
|
|
11
|
+
const decipher = createDecipheriv('aes-192-cbc', AES_KEY, AES_IV);
|
|
12
|
+
return Buffer.concat([decipher.update(Buffer.from(encrypted, 'base64')), decipher.final()]).toString('utf8');
|
|
13
|
+
}
|
|
14
|
+
export async function loadCodeFreeOCredentials(path = getCodeFreeOCredentialPath()) {
|
|
15
|
+
try {
|
|
16
|
+
const parsed = JSON.parse(await readFile(path, 'utf8'));
|
|
17
|
+
if (!parsed.userId || !parsed.encryptedApiKey)
|
|
18
|
+
return null;
|
|
19
|
+
return {
|
|
20
|
+
userId: parsed.userId,
|
|
21
|
+
apiKey: decryptApiKey(parsed.encryptedApiKey),
|
|
22
|
+
sessionId: parsed.sessionId ?? '',
|
|
23
|
+
updatedAt: Date.now()
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=codefree-o.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codefree-o.js","sourceRoot":"","sources":["../../src/auth/codefree-o.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAG9C,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,0BAA0B,EAAE,MAAM,CAAC,CAAA;AAC/D,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,kBAAkB,EAAE,MAAM,CAAC,CAAA;AAQtD,MAAM,UAAU,0BAA0B,CAAC,OAAO,GAAG,OAAO,EAAE;IAC5D,OAAO,IAAI,CAAC,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,OAAO,EAAE,eAAe,CAAC,CAAA;AACzE,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,SAAiB;IAC7C,MAAM,QAAQ,GAAG,gBAAgB,CAAC,aAAa,EAAE,OAAO,EAAE,MAAM,CAAC,CAAA;IACjE,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;AAC9G,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAAC,IAAI,GAAG,0BAA0B,EAAE;IAChF,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAkB,CAAA;QACxE,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,eAAe;YAAE,OAAO,IAAI,CAAA;QAC1D,OAAO;YACL,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,eAAe,CAAC;YAC7C,SAAS,EAAE,MAAM,CAAC,SAAS,IAAI,EAAE;YACjC,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;SACtB,CAAA;IACH,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface StoredCredentials {
|
|
2
|
+
userId: string;
|
|
3
|
+
apiKey: string;
|
|
4
|
+
sessionId: string;
|
|
5
|
+
updatedAt: number;
|
|
6
|
+
}
|
|
7
|
+
export declare function getConfigPath(homeDir?: string): string;
|
|
8
|
+
export declare function loadCredentials(path?: string): Promise<StoredCredentials | null>;
|
|
9
|
+
export declare function saveCredentials(credentials: StoredCredentials, path?: string): Promise<void>;
|
|
10
|
+
export declare function deleteCredentials(path?: string): Promise<void>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { mkdir, readFile, rm, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
4
|
+
function isStoredCredentials(value) {
|
|
5
|
+
if (!value || typeof value !== 'object')
|
|
6
|
+
return false;
|
|
7
|
+
const data = value;
|
|
8
|
+
return (typeof data.userId === 'string' && data.userId.length > 0 &&
|
|
9
|
+
typeof data.apiKey === 'string' && data.apiKey.length > 0 &&
|
|
10
|
+
typeof data.sessionId === 'string' &&
|
|
11
|
+
typeof data.updatedAt === 'number');
|
|
12
|
+
}
|
|
13
|
+
export function getConfigPath(homeDir = homedir()) {
|
|
14
|
+
return join(homeDir, '.codefree-proxy', 'config.json');
|
|
15
|
+
}
|
|
16
|
+
export async function loadCredentials(path = getConfigPath()) {
|
|
17
|
+
try {
|
|
18
|
+
const raw = await readFile(path, 'utf8');
|
|
19
|
+
const parsed = JSON.parse(raw);
|
|
20
|
+
return isStoredCredentials(parsed) ? parsed : null;
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
const code = error.code;
|
|
24
|
+
if (code === 'ENOENT')
|
|
25
|
+
return null;
|
|
26
|
+
throw error;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export async function saveCredentials(credentials, path = getConfigPath()) {
|
|
30
|
+
await mkdir(dirname(path), { recursive: true });
|
|
31
|
+
await writeFile(path, `${JSON.stringify(credentials, null, 2)}\n`, 'utf8');
|
|
32
|
+
}
|
|
33
|
+
export async function deleteCredentials(path = getConfigPath()) {
|
|
34
|
+
await rm(path, { force: true });
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=config-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-store.js","sourceRoot":"","sources":["../../src/auth/config-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AACjE,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACjC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AASzC,SAAS,mBAAmB,CAAC,KAAc;IACzC,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAA;IACrD,MAAM,IAAI,GAAG,KAAgC,CAAA;IAC7C,OAAO,CACL,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC;QACzD,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC;QACzD,OAAO,IAAI,CAAC,SAAS,KAAK,QAAQ;QAClC,OAAO,IAAI,CAAC,SAAS,KAAK,QAAQ,CACnC,CAAA;AACH,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,OAAO,GAAG,OAAO,EAAE;IAC/C,OAAO,IAAI,CAAC,OAAO,EAAE,iBAAiB,EAAE,aAAa,CAAC,CAAA;AACxD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,IAAI,GAAG,aAAa,EAAE;IAC1D,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;QACxC,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QACvC,OAAO,mBAAmB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAA;IACpD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,GAAI,KAA+B,CAAC,IAAI,CAAA;QAClD,IAAI,IAAI,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAA;QAClC,MAAM,KAAK,CAAA;IACb,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,WAA8B,EAAE,IAAI,GAAG,aAAa,EAAE;IAC1F,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAC/C,MAAM,SAAS,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;AAC5E,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,IAAI,GAAG,aAAa,EAAE;IAC5D,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;AACjC,CAAC"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
export type CliCommand = 'serve' | 'login' | 'logout' | 'status';
|
|
3
|
+
export interface CliOptions {
|
|
4
|
+
command: CliCommand;
|
|
5
|
+
host: string;
|
|
6
|
+
port: number;
|
|
7
|
+
timeoutMs: number;
|
|
8
|
+
}
|
|
9
|
+
export declare function parseArgs(argv: string[]): CliOptions;
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { buildServer } from './server.js';
|
|
3
|
+
import { AuthManager } from './auth/auth-manager.js';
|
|
4
|
+
import { SUPPORTED_MODELS } from './proxy/model-map.js';
|
|
5
|
+
export function parseArgs(argv) {
|
|
6
|
+
const options = { command: 'serve', host: '127.0.0.1', port: 8788, timeoutMs: 120_000 };
|
|
7
|
+
const args = [...argv];
|
|
8
|
+
if (args[0] === 'login' || args[0] === 'logout' || args[0] === 'status') {
|
|
9
|
+
options.command = args.shift();
|
|
10
|
+
}
|
|
11
|
+
while (args.length > 0) {
|
|
12
|
+
const arg = args.shift();
|
|
13
|
+
if (arg === '--host')
|
|
14
|
+
options.host = args.shift() ?? options.host;
|
|
15
|
+
else if (arg === '--port')
|
|
16
|
+
options.port = Number(args.shift() ?? options.port);
|
|
17
|
+
else if (arg === '--timeout')
|
|
18
|
+
options.timeoutMs = Number(args.shift() ?? options.timeoutMs / 1000) * 1000;
|
|
19
|
+
else
|
|
20
|
+
throw new Error(`unknown argument: ${arg}`);
|
|
21
|
+
}
|
|
22
|
+
return options;
|
|
23
|
+
}
|
|
24
|
+
async function main() {
|
|
25
|
+
const options = parseArgs(process.argv.slice(2));
|
|
26
|
+
const auth = new AuthManager();
|
|
27
|
+
if (options.command === 'login') {
|
|
28
|
+
const credentials = await auth.login();
|
|
29
|
+
console.log(`Authorized user: ${credentials.userId}`);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (options.command === 'logout') {
|
|
33
|
+
await auth.logout();
|
|
34
|
+
console.log('Credentials removed');
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (options.command === 'status') {
|
|
38
|
+
console.log(JSON.stringify(await auth.status(), null, 2));
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const credentials = await auth.getCredentials();
|
|
42
|
+
const app = buildServer({ auth, timeoutMs: options.timeoutMs });
|
|
43
|
+
await app.listen({ host: options.host, port: options.port });
|
|
44
|
+
console.log(`OpenAI Base URL: http://${options.host}:${options.port}/v1`);
|
|
45
|
+
console.log('API Key: any value or omit');
|
|
46
|
+
console.log(`Authorized user: ${credentials.userId}`);
|
|
47
|
+
console.log(`Models: ${SUPPORTED_MODELS.join(', ')}`);
|
|
48
|
+
}
|
|
49
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
50
|
+
main().catch((error) => {
|
|
51
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
52
|
+
process.exit(1);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAA;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAWvD,MAAM,UAAU,SAAS,CAAC,IAAc;IACtC,MAAM,OAAO,GAAe,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAA;IACnG,MAAM,IAAI,GAAG,CAAC,GAAG,IAAI,CAAC,CAAA;IACtB,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,OAAO,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ,EAAE,CAAC;QACxE,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,KAAK,EAAgB,CAAA;IAC9C,CAAC;IACD,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,EAAE,CAAA;QACxB,IAAI,GAAG,KAAK,QAAQ;YAAE,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,KAAK,EAAE,IAAI,OAAO,CAAC,IAAI,CAAA;aAC5D,IAAI,GAAG,KAAK,QAAQ;YAAE,OAAO,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;aACzE,IAAI,GAAG,KAAK,WAAW;YAAE,OAAO,CAAC,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,IAAI,CAAA;;YACpG,MAAM,IAAI,KAAK,CAAC,qBAAqB,GAAG,EAAE,CAAC,CAAA;IAClD,CAAC;IACD,OAAO,OAAO,CAAA;AAChB,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;IAChD,MAAM,IAAI,GAAG,IAAI,WAAW,EAAE,CAAA;IAE9B,IAAI,OAAO,CAAC,OAAO,KAAK,OAAO,EAAE,CAAC;QAChC,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,KAAK,EAAE,CAAA;QACtC,OAAO,CAAC,GAAG,CAAC,oBAAoB,WAAW,CAAC,MAAM,EAAE,CAAC,CAAA;QACrD,OAAM;IACR,CAAC;IAED,IAAI,OAAO,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;QACjC,MAAM,IAAI,CAAC,MAAM,EAAE,CAAA;QACnB,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAA;QAClC,OAAM;IACR,CAAC;IAED,IAAI,OAAO,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;QACjC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,IAAI,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAA;QACzD,OAAM;IACR,CAAC;IAED,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,cAAc,EAAE,CAAA;IAC/C,MAAM,GAAG,GAAG,WAAW,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC,CAAA;IAC/D,MAAM,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,CAAA;IAC5D,OAAO,CAAC,GAAG,CAAC,2BAA2B,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,IAAI,KAAK,CAAC,CAAA;IACzE,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAA;IACzC,OAAO,CAAC,GAAG,CAAC,oBAAoB,WAAW,CAAC,MAAM,EAAE,CAAC,CAAA;IACrD,OAAO,CAAC,GAAG,CAAC,WAAW,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;AACvD,CAAC;AAED,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,UAAU,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;IACpD,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;QACrB,OAAO,CAAC,KAAK,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAA;QACrE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC,CAAC,CAAA;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function transformOpenAIStream(input: AsyncIterable<string>): AsyncIterable<string>;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
function sse(data) {
|
|
2
|
+
if (data === '[DONE]')
|
|
3
|
+
return 'data: [DONE]\n\n';
|
|
4
|
+
const choices = Array.isArray(data.choices) ? data.choices : [];
|
|
5
|
+
for (const choice of choices) {
|
|
6
|
+
if (choice.finish_reason === '')
|
|
7
|
+
choice.finish_reason = null;
|
|
8
|
+
}
|
|
9
|
+
return `data: ${JSON.stringify(data)}\n\n`;
|
|
10
|
+
}
|
|
11
|
+
function mergeTool(buffers, tool) {
|
|
12
|
+
const index = Number(tool.index ?? 0);
|
|
13
|
+
const current = buffers.get(index) ?? { index, id: '', type: 'function', function: { name: '', arguments: '' } };
|
|
14
|
+
if (typeof tool.id === 'string')
|
|
15
|
+
current.id = tool.id;
|
|
16
|
+
if (typeof tool.type === 'string')
|
|
17
|
+
current.type = tool.type;
|
|
18
|
+
const fn = tool.function;
|
|
19
|
+
if (fn && typeof fn.name === 'string')
|
|
20
|
+
current.function.name = fn.name;
|
|
21
|
+
if (fn && typeof fn.arguments === 'string')
|
|
22
|
+
current.function.arguments += fn.arguments;
|
|
23
|
+
buffers.set(index, current);
|
|
24
|
+
}
|
|
25
|
+
function completedToolEvents(source, buffers) {
|
|
26
|
+
const events = [];
|
|
27
|
+
for (const tool of [...buffers.values()].sort((a, b) => a.index - b.index)) {
|
|
28
|
+
events.push(sse({
|
|
29
|
+
id: source.id,
|
|
30
|
+
object: source.object ?? 'chat.completion.chunk',
|
|
31
|
+
created: source.created,
|
|
32
|
+
model: source.model,
|
|
33
|
+
choices: [{ index: 0, delta: { role: 'assistant', tool_calls: [tool] }, finish_reason: null }]
|
|
34
|
+
}));
|
|
35
|
+
}
|
|
36
|
+
events.push(sse({
|
|
37
|
+
id: source.id,
|
|
38
|
+
object: source.object ?? 'chat.completion.chunk',
|
|
39
|
+
created: source.created,
|
|
40
|
+
model: source.model,
|
|
41
|
+
choices: [{ index: 0, delta: { role: 'assistant' }, finish_reason: 'tool_calls' }]
|
|
42
|
+
}));
|
|
43
|
+
buffers.clear();
|
|
44
|
+
return events;
|
|
45
|
+
}
|
|
46
|
+
export async function* transformOpenAIStream(input) {
|
|
47
|
+
const buffers = new Map();
|
|
48
|
+
let sentDone = false;
|
|
49
|
+
for await (const chunk of input) {
|
|
50
|
+
const events = chunk.split('\n\n').filter(Boolean);
|
|
51
|
+
for (const event of events) {
|
|
52
|
+
const lines = event.split('\n');
|
|
53
|
+
const dataLine = lines.find((part) => part.startsWith('data:'));
|
|
54
|
+
// Forward non-data SSE lines (event:, id:, retry:, comments) as-is
|
|
55
|
+
const nonDataLines = lines.filter((part) => part.trim() !== '' && !part.startsWith('data:'));
|
|
56
|
+
for (const ndl of nonDataLines)
|
|
57
|
+
yield ndl + '\n';
|
|
58
|
+
if (!dataLine)
|
|
59
|
+
continue;
|
|
60
|
+
const payload = dataLine.slice(5).trim();
|
|
61
|
+
if (payload === '[DONE]') {
|
|
62
|
+
if (buffers.size > 0)
|
|
63
|
+
for (const out of completedToolEvents({}, buffers))
|
|
64
|
+
yield out;
|
|
65
|
+
sentDone = true;
|
|
66
|
+
yield sse('[DONE]');
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
let data;
|
|
70
|
+
try {
|
|
71
|
+
data = JSON.parse(payload);
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
yield `data: ${payload}\n\n`;
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
const choices = Array.isArray(data.choices) ? data.choices : [];
|
|
78
|
+
if (choices.length === 0) {
|
|
79
|
+
if (buffers.size > 0)
|
|
80
|
+
for (const out of completedToolEvents(data, buffers))
|
|
81
|
+
yield out;
|
|
82
|
+
yield sse(data);
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
const passthrough = [];
|
|
86
|
+
let emitted = false;
|
|
87
|
+
for (const choice of choices) {
|
|
88
|
+
const delta = (choice.delta ?? {});
|
|
89
|
+
const tools = Array.isArray(delta.tool_calls) ? delta.tool_calls : [];
|
|
90
|
+
const finish = choice.finish_reason;
|
|
91
|
+
if (tools.length > 0) {
|
|
92
|
+
for (const tool of tools)
|
|
93
|
+
mergeTool(buffers, tool);
|
|
94
|
+
if (typeof delta.content === 'string' && delta.content.length > 0) {
|
|
95
|
+
passthrough.push({ ...choice, delta: { role: delta.role, content: delta.content } });
|
|
96
|
+
}
|
|
97
|
+
if (finish === 'tool_calls') {
|
|
98
|
+
for (const out of completedToolEvents(data, buffers))
|
|
99
|
+
yield out;
|
|
100
|
+
emitted = true;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
else if (finish === 'tool_calls' && buffers.size > 0) {
|
|
104
|
+
for (const out of completedToolEvents(data, buffers))
|
|
105
|
+
yield out;
|
|
106
|
+
emitted = true;
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
passthrough.push(choice);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (passthrough.length > 0)
|
|
113
|
+
yield sse({ ...data, choices: passthrough });
|
|
114
|
+
if (!emitted && passthrough.length === 0)
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (!sentDone)
|
|
119
|
+
yield sse('[DONE]');
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=zed-tool-call-stream.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zed-tool-call-stream.js","sourceRoot":"","sources":["../../src/compat/zed-tool-call-stream.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-map.js","sourceRoot":"","sources":["../../src/proxy/model-map.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,aAAa,GAAG,SAAS,CAAA;AAEtC,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,SAAS,EAAE,SAAS,EAAE,mBAAmB,CAAU,CAAA;AAEpF,MAAM,UAAU,QAAQ,CAAC,KAAc;IACrC,OAAO,gBAAgB,CAAC,QAAQ,CAAC,KAAwC,CAAC,CAAC,CAAC,CAAC,KAAe,CAAC,CAAC,CAAC,aAAa,CAAA;AAC9G,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { FastifyInstance } from 'fastify';
|
|
2
|
+
import type { StoredCredentials } from '../auth/config-store.js';
|
|
3
|
+
export interface RouteAuth {
|
|
4
|
+
getCredentials(): Promise<StoredCredentials>;
|
|
5
|
+
refresh(): Promise<StoredCredentials>;
|
|
6
|
+
login(): Promise<StoredCredentials>;
|
|
7
|
+
logout(): Promise<void>;
|
|
8
|
+
status(): Promise<unknown>;
|
|
9
|
+
}
|
|
10
|
+
export interface OpenAIRouteOptions {
|
|
11
|
+
auth: RouteAuth;
|
|
12
|
+
upstreamFetch?: typeof fetch;
|
|
13
|
+
timeoutMs?: number;
|
|
14
|
+
}
|
|
15
|
+
export declare function registerOpenAIRoutes(app: FastifyInstance, options: OpenAIRouteOptions): void;
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { transformOpenAIStream } from '../compat/zed-tool-call-stream.js';
|
|
2
|
+
import { SUPPORTED_MODELS, mapModel } from './model-map.js';
|
|
3
|
+
import { postUpstreamJson } from './upstream-client.js';
|
|
4
|
+
function logRequest(body) {
|
|
5
|
+
const model = body.model ?? 'none';
|
|
6
|
+
const stream = body.stream ?? false;
|
|
7
|
+
const msgCount = Array.isArray(body.messages) ? body.messages.length : 0;
|
|
8
|
+
const hasTools = Array.isArray(body.tools) && body.tools.length > 0;
|
|
9
|
+
console.log(`[debug] model=${model} stream=${stream} messages=${msgCount} tools=${hasTools}`);
|
|
10
|
+
}
|
|
11
|
+
function errorMessage(error) {
|
|
12
|
+
return error instanceof Error ? error.message : String(error);
|
|
13
|
+
}
|
|
14
|
+
function sendError(reply, statusCode, type, error) {
|
|
15
|
+
reply.code(statusCode);
|
|
16
|
+
return { error: { message: errorMessage(error), type } };
|
|
17
|
+
}
|
|
18
|
+
function sendOrStreamError(reply, statusCode, type, error, stream) {
|
|
19
|
+
if (!stream)
|
|
20
|
+
return sendError(reply, statusCode, type, error);
|
|
21
|
+
reply.raw.writeHead(200, {
|
|
22
|
+
'content-type': 'text/event-stream',
|
|
23
|
+
'cache-control': 'no-cache',
|
|
24
|
+
connection: 'keep-alive'
|
|
25
|
+
});
|
|
26
|
+
reply.raw.write(`data: ${JSON.stringify({ error: { message: errorMessage(error), type } })}\n\n`);
|
|
27
|
+
reply.raw.write('data: [DONE]\n\n');
|
|
28
|
+
reply.raw.end();
|
|
29
|
+
return reply;
|
|
30
|
+
}
|
|
31
|
+
function requestBody(body) {
|
|
32
|
+
if (!body || typeof body !== 'object' || Array.isArray(body)) {
|
|
33
|
+
throw new Error('Request body must be a JSON object');
|
|
34
|
+
}
|
|
35
|
+
return body;
|
|
36
|
+
}
|
|
37
|
+
async function* responseToChunks(response) {
|
|
38
|
+
const reader = response.body?.getReader();
|
|
39
|
+
if (!reader)
|
|
40
|
+
return;
|
|
41
|
+
const decoder = new TextDecoder();
|
|
42
|
+
let buffer = '';
|
|
43
|
+
while (true) {
|
|
44
|
+
const result = await reader.read();
|
|
45
|
+
if (result.done) {
|
|
46
|
+
if (buffer.trim())
|
|
47
|
+
yield buffer;
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
buffer += decoder.decode(result.value, { stream: true });
|
|
51
|
+
const boundary = '\n\n';
|
|
52
|
+
let idx;
|
|
53
|
+
while ((idx = buffer.indexOf(boundary)) !== -1) {
|
|
54
|
+
const part = buffer.slice(0, idx);
|
|
55
|
+
buffer = buffer.slice(idx + boundary.length);
|
|
56
|
+
if (part.trim())
|
|
57
|
+
yield part;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
export function registerOpenAIRoutes(app, options) {
|
|
62
|
+
app.setErrorHandler((error, _request, reply) => {
|
|
63
|
+
const statusCode = error.statusCode === 400 ? 400 : 500;
|
|
64
|
+
const type = statusCode === 400 ? 'invalid_request_error' : 'api_error';
|
|
65
|
+
reply.code(statusCode).send({ error: { message: errorMessage(error), type } });
|
|
66
|
+
});
|
|
67
|
+
app.get('/health', async () => ({ status: 'ok' }));
|
|
68
|
+
app.get('/v1/models', async () => ({
|
|
69
|
+
object: 'list',
|
|
70
|
+
data: SUPPORTED_MODELS.map((id) => ({ id, object: 'model', owned_by: 'srdcloud' }))
|
|
71
|
+
}));
|
|
72
|
+
app.get('/auth/status', async (_request, reply) => {
|
|
73
|
+
try {
|
|
74
|
+
return await options.auth.status();
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
return sendError(reply, 401, 'auth_error', error);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
app.post('/auth/refresh', async (_request, reply) => {
|
|
81
|
+
try {
|
|
82
|
+
return await options.auth.refresh();
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
return sendError(reply, 401, 'auth_error', error);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
app.post('/v1/chat/completions', async (request, reply) => {
|
|
89
|
+
let body;
|
|
90
|
+
try {
|
|
91
|
+
body = requestBody(request.body);
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
return sendError(reply, 400, 'invalid_request_error', error);
|
|
95
|
+
}
|
|
96
|
+
logRequest(body);
|
|
97
|
+
const mappedModel = mapModel(typeof body.model === 'string' ? body.model : undefined);
|
|
98
|
+
const upstreamBody = { ...body, model: mappedModel };
|
|
99
|
+
const stream = body.stream === true;
|
|
100
|
+
let credentials;
|
|
101
|
+
try {
|
|
102
|
+
credentials = await options.auth.getCredentials();
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
return sendOrStreamError(reply, 401, 'auth_error', error, stream);
|
|
106
|
+
}
|
|
107
|
+
let upstream;
|
|
108
|
+
try {
|
|
109
|
+
upstream = await postUpstreamJson(upstreamBody, credentials, { fetchImpl: options.upstreamFetch, timeoutMs: options.timeoutMs });
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
return sendOrStreamError(reply, 502, 'api_error', error, stream);
|
|
113
|
+
}
|
|
114
|
+
if (upstream.status === 401 || upstream.status === 403) {
|
|
115
|
+
try {
|
|
116
|
+
credentials = await options.auth.refresh();
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
return sendOrStreamError(reply, 401, 'auth_error', error, stream);
|
|
120
|
+
}
|
|
121
|
+
try {
|
|
122
|
+
upstream = await postUpstreamJson(upstreamBody, credentials, { fetchImpl: options.upstreamFetch, timeoutMs: options.timeoutMs });
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
return sendOrStreamError(reply, 502, 'api_error', error, stream);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (!upstream.ok) {
|
|
129
|
+
const message = await upstream.text();
|
|
130
|
+
console.log(`[debug] upstream non-ok: status=${upstream.status} body=${message.slice(0, 200)}`);
|
|
131
|
+
if (!stream) {
|
|
132
|
+
reply.code(upstream.status);
|
|
133
|
+
return { error: { message, type: 'api_error' } };
|
|
134
|
+
}
|
|
135
|
+
return sendOrStreamError(reply, upstream.status, 'api_error', new Error(message), true);
|
|
136
|
+
}
|
|
137
|
+
if (!stream) {
|
|
138
|
+
reply.header('content-type', 'application/json');
|
|
139
|
+
try {
|
|
140
|
+
const text = await upstream.text();
|
|
141
|
+
const data = JSON.parse(text);
|
|
142
|
+
const choices = Array.isArray(data.choices) ? data.choices : [];
|
|
143
|
+
const message = choices[0]?.message;
|
|
144
|
+
const toolCalls = Array.isArray(message?.tool_calls) ? message.tool_calls : [];
|
|
145
|
+
if (toolCalls.length > 0) {
|
|
146
|
+
console.log(`[debug] nonstream tool_calls=${toolCalls.map((tc) => { const fn = tc.function; return [fn?.name ?? '', fn?.arguments ?? '']; })}`);
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
const content = typeof message?.content === 'string' ? message.content.slice(0, 300) : '';
|
|
150
|
+
console.log(`[debug] nonstream no_tool_call finish=${choices[0]?.finish_reason ?? null} content=${JSON.stringify(content)} keys=${Object.keys(data).join(',')}`);
|
|
151
|
+
}
|
|
152
|
+
return data;
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
return sendError(reply, 502, 'api_error', error);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
reply.raw.writeHead(200, {
|
|
159
|
+
'content-type': 'text/event-stream',
|
|
160
|
+
'cache-control': 'no-cache',
|
|
161
|
+
connection: 'keep-alive'
|
|
162
|
+
});
|
|
163
|
+
try {
|
|
164
|
+
for await (const chunk of transformOpenAIStream(responseToChunks(upstream))) {
|
|
165
|
+
reply.raw.write(chunk);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
console.log(`[debug] stream transform error: ${errorMessage(error)}`);
|
|
170
|
+
reply.raw.write(`data: ${JSON.stringify({ error: { message: errorMessage(error), type: 'api_error' } })}\n\n`);
|
|
171
|
+
}
|
|
172
|
+
reply.raw.write('data: [DONE]\n\n');
|
|
173
|
+
reply.raw.end();
|
|
174
|
+
return reply;
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
//# sourceMappingURL=openai-routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openai-routes.js","sourceRoot":"","sources":["../../src/proxy/openai-routes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { StoredCredentials } from '../auth/config-store.js';
|
|
2
|
+
export declare const UPSTREAM_URL = "https://www.srdcloud.cn/api/acbackend/codechat/v1/completions";
|
|
3
|
+
export declare const CLIENT_VERSION = "0.3.6";
|
|
4
|
+
export interface UpstreamOptions {
|
|
5
|
+
fetchImpl?: typeof fetch;
|
|
6
|
+
timeoutMs?: number;
|
|
7
|
+
}
|
|
8
|
+
export declare function buildUpstreamHeaders(credentials: StoredCredentials, model: string): Record<string, string>;
|
|
9
|
+
export declare function postUpstreamJson(body: Record<string, unknown>, credentials: StoredCredentials, options?: UpstreamOptions): Promise<Response>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export const UPSTREAM_URL = 'https://www.srdcloud.cn/api/acbackend/codechat/v1/completions';
|
|
2
|
+
export const CLIENT_VERSION = '0.3.6';
|
|
3
|
+
export function buildUpstreamHeaders(credentials, model) {
|
|
4
|
+
return {
|
|
5
|
+
'content-type': 'application/json',
|
|
6
|
+
accept: 'text/event-stream',
|
|
7
|
+
apiKey: credentials.apiKey,
|
|
8
|
+
authorization: 'Bearer codefree',
|
|
9
|
+
userId: credentials.userId,
|
|
10
|
+
subService: 'cli_chat',
|
|
11
|
+
clientType: 'codefree-cli',
|
|
12
|
+
clientVersion: CLIENT_VERSION,
|
|
13
|
+
modelName: model
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export async function postUpstreamJson(body, credentials, options = {}) {
|
|
17
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
18
|
+
const model = body.model;
|
|
19
|
+
const controller = new AbortController();
|
|
20
|
+
const timeout = setTimeout(() => controller.abort(), options.timeoutMs ?? 120_000);
|
|
21
|
+
try {
|
|
22
|
+
return await fetchImpl(UPSTREAM_URL, {
|
|
23
|
+
method: 'POST',
|
|
24
|
+
headers: buildUpstreamHeaders(credentials, model ?? ''),
|
|
25
|
+
body: JSON.stringify(body),
|
|
26
|
+
signal: controller.signal
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
finally {
|
|
30
|
+
clearTimeout(timeout);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=upstream-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upstream-client.js","sourceRoot":"","sources":["../../src/proxy/upstream-client.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,YAAY,GAAG,+DAA+D,CAAA;AAC3F,MAAM,CAAC,MAAM,cAAc,GAAG,OAAO,CAAA;AAOrC,MAAM,UAAU,oBAAoB,CAAC,WAA8B,EAAE,KAAa;IAChF,OAAO;QACL,cAAc,EAAE,kBAAkB;QAClC,MAAM,EAAE,mBAAmB;QAC3B,MAAM,EAAE,WAAW,CAAC,MAAM;QAC1B,aAAa,EAAE,iBAAiB;QAChC,MAAM,EAAE,WAAW,CAAC,MAAM;QAC1B,UAAU,EAAE,UAAU;QACtB,UAAU,EAAE,cAAc;QAC1B,aAAa,EAAE,cAAc;QAC7B,SAAS,EAAE,KAAK;KACjB,CAAA;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,IAA6B,EAAE,WAA8B,EAAE,UAA2B,EAAE;IACjI,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,KAAK,CAAA;IAC5C,MAAM,KAAK,GAAG,IAAI,CAAC,KAA2B,CAAA;IAC9C,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAA;IACxC,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,CAAA;IAClF,IAAI,CAAC;QACH,OAAO,MAAM,SAAS,CAAC,YAAY,EAAE;YACnC,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,oBAAoB,CAAC,WAAW,EAAE,KAAK,IAAI,EAAE,CAAC;YACvD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;YAC1B,MAAM,EAAE,UAAU,CAAC,MAAM;SAC1B,CAAC,CAAA;IACJ,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,OAAO,CAAC,CAAA;IACvB,CAAC;AACH,CAAC"}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type FastifyInstance } from 'fastify';
|
|
2
|
+
import { type RouteAuth } from './proxy/openai-routes.js';
|
|
3
|
+
export interface ServerOptions {
|
|
4
|
+
auth?: RouteAuth;
|
|
5
|
+
upstreamFetch?: typeof fetch;
|
|
6
|
+
timeoutMs?: number;
|
|
7
|
+
}
|
|
8
|
+
export declare function buildServer(options?: ServerOptions): FastifyInstance;
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import Fastify from 'fastify';
|
|
2
|
+
import { AuthManager } from './auth/auth-manager.js';
|
|
3
|
+
import { registerOpenAIRoutes } from './proxy/openai-routes.js';
|
|
4
|
+
export function buildServer(options = {}) {
|
|
5
|
+
const app = Fastify({ logger: true });
|
|
6
|
+
registerOpenAIRoutes(app, {
|
|
7
|
+
auth: options.auth ?? new AuthManager(),
|
|
8
|
+
upstreamFetch: options.upstreamFetch,
|
|
9
|
+
timeoutMs: options.timeoutMs
|
|
10
|
+
});
|
|
11
|
+
return app;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,OAAiC,MAAM,SAAS,CAAA;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAA;AACpD,OAAO,EAAE,oBAAoB,EAAkB,MAAM,0BAA0B,CAAA;AAQ/E,MAAM,UAAU,WAAW,CAAC,UAAyB,EAAE;IACrD,MAAM,GAAG,GAAG,OAAO,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAA;IACrC,oBAAoB,CAAC,GAAG,EAAE;QACxB,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,IAAI,WAAW,EAAE;QACvC,aAAa,EAAE,OAAO,CAAC,aAAa;QACpC,SAAS,EAAE,OAAO,CAAC,SAAS;KAC7B,CAAC,CAAA;IACF,OAAO,GAAG,CAAA;AACZ,CAAC"}
|
package/dist/version.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,WAAW,GAAG,gBAAgB,CAAA;AAC3C,MAAM,CAAC,MAAM,cAAc,GAAG,OAAO,CAAA"}
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "codefree-proxy",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "OpenAI-compatible local proxy for SRD Cloud CodeFree",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Maxwell",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"openai",
|
|
9
|
+
"proxy",
|
|
10
|
+
"fastify",
|
|
11
|
+
"llm",
|
|
12
|
+
"api",
|
|
13
|
+
"codefree",
|
|
14
|
+
"chat-completions"
|
|
15
|
+
],
|
|
16
|
+
"type": "module",
|
|
17
|
+
"bin": {
|
|
18
|
+
"codefree-proxy": "dist/cli.js"
|
|
19
|
+
},
|
|
20
|
+
"files": ["dist", "README.md", "LICENSE"],
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=18"
|
|
23
|
+
},
|
|
24
|
+
"scripts": {
|
|
25
|
+
"dev": "tsx src/cli.ts",
|
|
26
|
+
"build": "tsc -p tsconfig.json",
|
|
27
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
28
|
+
"test": "vitest run"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"fastify": "^4.28.1"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/node": "^20.14.10",
|
|
35
|
+
"tsx": "^4.16.2",
|
|
36
|
+
"typescript": "^5.5.3",
|
|
37
|
+
"vitest": "^1.6.0"
|
|
38
|
+
}
|
|
39
|
+
}
|