@redonvn/cli 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/README.md +99 -0
- package/bin/redai.js +22 -0
- package/dist/auth/client-id.d.ts +8 -0
- package/dist/auth/client-id.js +41 -0
- package/dist/auth/client-id.js.map +1 -0
- package/dist/auth/store.d.ts +16 -0
- package/dist/auth/store.js +39 -0
- package/dist/auth/store.js.map +1 -0
- package/dist/cli/commands/login.d.ts +6 -0
- package/dist/cli/commands/login.js +82 -0
- package/dist/cli/commands/login.js.map +1 -0
- package/dist/cli/commands/logout.d.ts +1 -0
- package/dist/cli/commands/logout.js +18 -0
- package/dist/cli/commands/logout.js.map +1 -0
- package/dist/cli/commands/oauth.d.ts +11 -0
- package/dist/cli/commands/oauth.js +311 -0
- package/dist/cli/commands/oauth.js.map +1 -0
- package/dist/cli/commands/serve.d.ts +7 -0
- package/dist/cli/commands/serve.js +24 -0
- package/dist/cli/commands/serve.js.map +1 -0
- package/dist/cli/commands/start.d.ts +1 -0
- package/dist/cli/commands/start.js +26 -0
- package/dist/cli/commands/start.js.map +1 -0
- package/dist/cli/commands/status.d.ts +1 -0
- package/dist/cli/commands/status.js +51 -0
- package/dist/cli/commands/status.js.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +127 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli-router/detect.d.ts +13 -0
- package/dist/cli-router/detect.js +58 -0
- package/dist/cli-router/detect.js.map +1 -0
- package/dist/config.d.ts +12 -0
- package/dist/config.js +24 -0
- package/dist/config.js.map +1 -0
- package/dist/daemon/router.d.ts +17 -0
- package/dist/daemon/router.js +39 -0
- package/dist/daemon/router.js.map +1 -0
- package/dist/daemon/tunnel.d.ts +10 -0
- package/dist/daemon/tunnel.js +156 -0
- package/dist/daemon/tunnel.js.map +1 -0
- package/dist/llm/llm-request.d.ts +9 -0
- package/dist/llm/llm-request.js +98 -0
- package/dist/llm/llm-request.js.map +1 -0
- package/dist/llm/oauth/antigravity-oauth.d.ts +22 -0
- package/dist/llm/oauth/antigravity-oauth.js +128 -0
- package/dist/llm/oauth/antigravity-oauth.js.map +1 -0
- package/dist/llm/oauth/callback-server.d.ts +18 -0
- package/dist/llm/oauth/callback-server.js +78 -0
- package/dist/llm/oauth/callback-server.js.map +1 -0
- package/dist/llm/oauth/claude-oauth.d.ts +29 -0
- package/dist/llm/oauth/claude-oauth.js +115 -0
- package/dist/llm/oauth/claude-oauth.js.map +1 -0
- package/dist/llm/oauth/codex-oauth.d.ts +21 -0
- package/dist/llm/oauth/codex-oauth.js +137 -0
- package/dist/llm/oauth/codex-oauth.js.map +1 -0
- package/dist/llm/oauth/gemini-oauth.d.ts +26 -0
- package/dist/llm/oauth/gemini-oauth.js +132 -0
- package/dist/llm/oauth/gemini-oauth.js.map +1 -0
- package/dist/llm/oauth/iflow-oauth.d.ts +26 -0
- package/dist/llm/oauth/iflow-oauth.js +151 -0
- package/dist/llm/oauth/iflow-oauth.js.map +1 -0
- package/dist/llm/oauth/kimi-oauth.d.ts +16 -0
- package/dist/llm/oauth/kimi-oauth.js +126 -0
- package/dist/llm/oauth/kimi-oauth.js.map +1 -0
- package/dist/llm/oauth/open-browser.d.ts +5 -0
- package/dist/llm/oauth/open-browser.js +32 -0
- package/dist/llm/oauth/open-browser.js.map +1 -0
- package/dist/llm/oauth/pkce.d.ts +12 -0
- package/dist/llm/oauth/pkce.js +27 -0
- package/dist/llm/oauth/pkce.js.map +1 -0
- package/dist/llm/oauth/qwen-oauth.d.ts +27 -0
- package/dist/llm/oauth/qwen-oauth.js +138 -0
- package/dist/llm/oauth/qwen-oauth.js.map +1 -0
- package/dist/llm/oauth/store.d.ts +34 -0
- package/dist/llm/oauth/store.js +72 -0
- package/dist/llm/oauth/store.js.map +1 -0
- package/dist/llm/oauth/xai-oauth.d.ts +28 -0
- package/dist/llm/oauth/xai-oauth.js +132 -0
- package/dist/llm/oauth/xai-oauth.js.map +1 -0
- package/dist/llm/providers/antigravity.d.ts +23 -0
- package/dist/llm/providers/antigravity.js +103 -0
- package/dist/llm/providers/antigravity.js.map +1 -0
- package/dist/llm/providers/claude.d.ts +36 -0
- package/dist/llm/providers/claude.js +148 -0
- package/dist/llm/providers/claude.js.map +1 -0
- package/dist/llm/providers/codex.d.ts +23 -0
- package/dist/llm/providers/codex.js +122 -0
- package/dist/llm/providers/codex.js.map +1 -0
- package/dist/llm/providers/gemini.d.ts +23 -0
- package/dist/llm/providers/gemini.js +112 -0
- package/dist/llm/providers/gemini.js.map +1 -0
- package/dist/llm/providers/generic-client.d.ts +45 -0
- package/dist/llm/providers/generic-client.js +98 -0
- package/dist/llm/providers/generic-client.js.map +1 -0
- package/dist/llm/providers/iflow.d.ts +8 -0
- package/dist/llm/providers/iflow.js +15 -0
- package/dist/llm/providers/iflow.js.map +1 -0
- package/dist/llm/providers/kimi.d.ts +8 -0
- package/dist/llm/providers/kimi.js +16 -0
- package/dist/llm/providers/kimi.js.map +1 -0
- package/dist/llm/providers/qwen.d.ts +8 -0
- package/dist/llm/providers/qwen.js +15 -0
- package/dist/llm/providers/qwen.js.map +1 -0
- package/dist/llm/providers/xai.d.ts +8 -0
- package/dist/llm/providers/xai.js +15 -0
- package/dist/llm/providers/xai.js.map +1 -0
- package/dist/llm/selector.d.ts +14 -0
- package/dist/llm/selector.js +81 -0
- package/dist/llm/selector.js.map +1 -0
- package/dist/server/http-server.d.ts +22 -0
- package/dist/server/http-server.js +194 -0
- package/dist/server/http-server.js.map +1 -0
- package/dist/tools/bash.d.ts +7 -0
- package/dist/tools/bash.js +61 -0
- package/dist/tools/bash.js.map +1 -0
- package/dist/tools/edit.d.ts +4 -0
- package/dist/tools/edit.js +35 -0
- package/dist/tools/edit.js.map +1 -0
- package/dist/tools/glob.d.ts +5 -0
- package/dist/tools/glob.js +27 -0
- package/dist/tools/glob.js.map +1 -0
- package/dist/tools/grep.d.ts +5 -0
- package/dist/tools/grep.js +63 -0
- package/dist/tools/grep.js.map +1 -0
- package/dist/tools/read.d.ts +6 -0
- package/dist/tools/read.js +25 -0
- package/dist/tools/read.js.map +1 -0
- package/dist/tools/registry.d.ts +8 -0
- package/dist/tools/registry.js +43 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/run-cli.d.ts +7 -0
- package/dist/tools/run-cli.js +83 -0
- package/dist/tools/run-cli.js.map +1 -0
- package/dist/tools/webfetch.d.ts +7 -0
- package/dist/tools/webfetch.js +26 -0
- package/dist/tools/webfetch.js.map +1 -0
- package/dist/tools/write.d.ts +5 -0
- package/dist/tools/write.js +29 -0
- package/dist/tools/write.js.map +1 -0
- package/package.json +67 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.waitForCallback = waitForCallback;
|
|
7
|
+
const http_1 = __importDefault(require("http"));
|
|
8
|
+
const url_1 = require("url");
|
|
9
|
+
const CALLBACK_HTML = `<!doctype html>
|
|
10
|
+
<html lang="en"><head><meta charset="utf-8"><title>RedAI CLI — Authentication</title>
|
|
11
|
+
<style>body{font-family:system-ui,sans-serif;display:grid;place-items:center;height:100vh;margin:0;background:#0d1117;color:#c9d1d9}
|
|
12
|
+
.card{background:#161b22;padding:32px 40px;border:1px solid #30363d;border-radius:12px;text-align:center;max-width:520px}
|
|
13
|
+
h1{margin:0 0 8px;color:#3fb950}
|
|
14
|
+
p{margin:0;color:#8b949e}</style></head>
|
|
15
|
+
<body><div class="card"><h1>✓ Authentication successful</h1>
|
|
16
|
+
<p>You can close this window and return to your terminal.</p></div></body></html>`;
|
|
17
|
+
/**
|
|
18
|
+
* Khởi 1 HTTP server tạm thời lắng nghe redirect từ provider OAuth.
|
|
19
|
+
* Resolve khi nhận được `code`, reject nếu timeout / lỗi.
|
|
20
|
+
*
|
|
21
|
+
* Phải bind đúng port mà provider whitelist (vd Claude: 54545).
|
|
22
|
+
*/
|
|
23
|
+
function waitForCallback(opts) {
|
|
24
|
+
const pathname = opts.pathname ?? '/callback';
|
|
25
|
+
const timeoutMs = opts.timeoutMs ?? 5 * 60 * 1000;
|
|
26
|
+
let resolve;
|
|
27
|
+
let reject;
|
|
28
|
+
const promise = new Promise((res, rej) => {
|
|
29
|
+
resolve = res;
|
|
30
|
+
reject = rej;
|
|
31
|
+
});
|
|
32
|
+
const server = http_1.default.createServer((req, res) => {
|
|
33
|
+
try {
|
|
34
|
+
const url = new url_1.URL(req.url || '/', `http://localhost:${opts.port}`);
|
|
35
|
+
if (url.pathname !== pathname) {
|
|
36
|
+
res.writeHead(404).end('Not found');
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const code = url.searchParams.get('code');
|
|
40
|
+
const error = url.searchParams.get('error');
|
|
41
|
+
const state = url.searchParams.get('state');
|
|
42
|
+
if (error) {
|
|
43
|
+
res.writeHead(400, { 'Content-Type': 'text/plain; charset=utf-8' });
|
|
44
|
+
res.end(`OAuth error: ${error}`);
|
|
45
|
+
reject(new Error(`OAuth error: ${error}`));
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (!code) {
|
|
49
|
+
res.writeHead(400, { 'Content-Type': 'text/plain; charset=utf-8' });
|
|
50
|
+
res.end('Missing code');
|
|
51
|
+
reject(new Error('Missing authorization code in callback'));
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
55
|
+
res.end(CALLBACK_HTML);
|
|
56
|
+
resolve({ code, state });
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
60
|
+
}
|
|
61
|
+
finally {
|
|
62
|
+
setTimeout(() => server.close(), 100);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
const timer = setTimeout(() => {
|
|
66
|
+
server.close();
|
|
67
|
+
reject(new Error(`OAuth callback timeout after ${timeoutMs}ms`));
|
|
68
|
+
}, timeoutMs);
|
|
69
|
+
timer.unref?.();
|
|
70
|
+
server.on('error', (err) => reject(err));
|
|
71
|
+
server.listen(opts.port, '127.0.0.1');
|
|
72
|
+
const cancel = () => {
|
|
73
|
+
clearTimeout(timer);
|
|
74
|
+
server.close();
|
|
75
|
+
};
|
|
76
|
+
return { promise, cancel };
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=callback-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"callback-server.js","sourceRoot":"","sources":["../../../src/llm/oauth/callback-server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { PKCECodes } from './pkce';
|
|
2
|
+
import { OAuthTokenStorage } from './store';
|
|
3
|
+
/**
|
|
4
|
+
* Claude OAuth flow — port từ CLIProxyAPI `internal/auth/claude/anthropic_auth.go`.
|
|
5
|
+
*
|
|
6
|
+
* Constants giống y nguyên Claude Code CLI để Anthropic nhận diện là Claude Code:
|
|
7
|
+
* - ClientID: 9d1c250a-e61b-44d9-88ed-5944d1962f5e
|
|
8
|
+
* - Scope: user:profile user:inference user:sessions:claude_code
|
|
9
|
+
* user:mcp_servers user:file_upload
|
|
10
|
+
* - Redirect: http://localhost:54545/callback
|
|
11
|
+
*/
|
|
12
|
+
export declare const CLAUDE_AUTH_URL = "https://claude.ai/oauth/authorize";
|
|
13
|
+
export declare const CLAUDE_TOKEN_URL = "https://api.anthropic.com/v1/oauth/token";
|
|
14
|
+
export declare const CLAUDE_CLIENT_ID = "9d1c250a-e61b-44d9-88ed-5944d1962f5e";
|
|
15
|
+
export declare const CLAUDE_REDIRECT_URI = "http://localhost:54545/callback";
|
|
16
|
+
export declare const CLAUDE_SCOPE = "user:profile user:inference user:sessions:claude_code user:mcp_servers user:file_upload";
|
|
17
|
+
export interface ClaudeAuthSession {
|
|
18
|
+
authUrl: string;
|
|
19
|
+
state: string;
|
|
20
|
+
pkce: PKCECodes;
|
|
21
|
+
}
|
|
22
|
+
/** Khởi tạo OAuth session — trả URL để mở browser. */
|
|
23
|
+
export declare function startClaudeAuth(): ClaudeAuthSession;
|
|
24
|
+
/**
|
|
25
|
+
* Đổi authorization code lấy access + refresh token.
|
|
26
|
+
*/
|
|
27
|
+
export declare function exchangeCodeForTokens(authCode: string, pkce: PKCECodes, state: string, account?: string): Promise<OAuthTokenStorage>;
|
|
28
|
+
/** Refresh access token bằng refresh_token (không cần PKCE). */
|
|
29
|
+
export declare function refreshClaudeToken(current: OAuthTokenStorage): Promise<OAuthTokenStorage>;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CLAUDE_SCOPE = exports.CLAUDE_REDIRECT_URI = exports.CLAUDE_CLIENT_ID = exports.CLAUDE_TOKEN_URL = exports.CLAUDE_AUTH_URL = void 0;
|
|
4
|
+
exports.startClaudeAuth = startClaudeAuth;
|
|
5
|
+
exports.exchangeCodeForTokens = exchangeCodeForTokens;
|
|
6
|
+
exports.refreshClaudeToken = refreshClaudeToken;
|
|
7
|
+
const undici_1 = require("undici");
|
|
8
|
+
const pkce_1 = require("./pkce");
|
|
9
|
+
/**
|
|
10
|
+
* Claude OAuth flow — port từ CLIProxyAPI `internal/auth/claude/anthropic_auth.go`.
|
|
11
|
+
*
|
|
12
|
+
* Constants giống y nguyên Claude Code CLI để Anthropic nhận diện là Claude Code:
|
|
13
|
+
* - ClientID: 9d1c250a-e61b-44d9-88ed-5944d1962f5e
|
|
14
|
+
* - Scope: user:profile user:inference user:sessions:claude_code
|
|
15
|
+
* user:mcp_servers user:file_upload
|
|
16
|
+
* - Redirect: http://localhost:54545/callback
|
|
17
|
+
*/
|
|
18
|
+
exports.CLAUDE_AUTH_URL = 'https://claude.ai/oauth/authorize';
|
|
19
|
+
exports.CLAUDE_TOKEN_URL = 'https://api.anthropic.com/v1/oauth/token';
|
|
20
|
+
exports.CLAUDE_CLIENT_ID = '9d1c250a-e61b-44d9-88ed-5944d1962f5e';
|
|
21
|
+
exports.CLAUDE_REDIRECT_URI = 'http://localhost:54545/callback';
|
|
22
|
+
exports.CLAUDE_SCOPE = 'user:profile user:inference user:sessions:claude_code user:mcp_servers user:file_upload';
|
|
23
|
+
/** Khởi tạo OAuth session — trả URL để mở browser. */
|
|
24
|
+
function startClaudeAuth() {
|
|
25
|
+
const pkce = (0, pkce_1.generatePKCECodes)();
|
|
26
|
+
const state = (0, pkce_1.generateRandomState)();
|
|
27
|
+
const params = new URLSearchParams({
|
|
28
|
+
code: 'true',
|
|
29
|
+
client_id: exports.CLAUDE_CLIENT_ID,
|
|
30
|
+
response_type: 'code',
|
|
31
|
+
redirect_uri: exports.CLAUDE_REDIRECT_URI,
|
|
32
|
+
scope: exports.CLAUDE_SCOPE,
|
|
33
|
+
code_challenge: pkce.codeChallenge,
|
|
34
|
+
code_challenge_method: 'S256',
|
|
35
|
+
state,
|
|
36
|
+
});
|
|
37
|
+
return {
|
|
38
|
+
authUrl: `${exports.CLAUDE_AUTH_URL}?${params.toString()}`,
|
|
39
|
+
state,
|
|
40
|
+
pkce,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Anthropic callback có format đặc biệt: code có thể chứa `#<state>` fragment.
|
|
45
|
+
* Pattern này copy từ `parseCodeAndState()` trong upstream.
|
|
46
|
+
*/
|
|
47
|
+
function parseCodeAndState(code) {
|
|
48
|
+
const parts = code.split('#');
|
|
49
|
+
return { code: parts[0], stateInline: parts[1] };
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Đổi authorization code lấy access + refresh token.
|
|
53
|
+
*/
|
|
54
|
+
async function exchangeCodeForTokens(authCode, pkce, state, account = 'default') {
|
|
55
|
+
const { code, stateInline } = parseCodeAndState(authCode);
|
|
56
|
+
const reqBody = {
|
|
57
|
+
code,
|
|
58
|
+
state: stateInline || state,
|
|
59
|
+
grant_type: 'authorization_code',
|
|
60
|
+
client_id: exports.CLAUDE_CLIENT_ID,
|
|
61
|
+
redirect_uri: exports.CLAUDE_REDIRECT_URI,
|
|
62
|
+
code_verifier: pkce.codeVerifier,
|
|
63
|
+
};
|
|
64
|
+
const res = await (0, undici_1.request)(exports.CLAUDE_TOKEN_URL, {
|
|
65
|
+
method: 'POST',
|
|
66
|
+
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
|
|
67
|
+
body: JSON.stringify(reqBody),
|
|
68
|
+
bodyTimeout: 30000,
|
|
69
|
+
headersTimeout: 15000,
|
|
70
|
+
});
|
|
71
|
+
const bodyText = await res.body.text();
|
|
72
|
+
if (res.statusCode !== 200) {
|
|
73
|
+
throw new Error(`Token exchange failed (HTTP ${res.statusCode}): ${bodyText}`);
|
|
74
|
+
}
|
|
75
|
+
const parsed = JSON.parse(bodyText);
|
|
76
|
+
const now = Date.now();
|
|
77
|
+
return {
|
|
78
|
+
type: 'claude',
|
|
79
|
+
account,
|
|
80
|
+
accessToken: parsed.access_token,
|
|
81
|
+
refreshToken: parsed.refresh_token,
|
|
82
|
+
email: parsed.account?.email_address,
|
|
83
|
+
expiresAt: now + parsed.expires_in * 1000,
|
|
84
|
+
lastRefresh: now,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
/** Refresh access token bằng refresh_token (không cần PKCE). */
|
|
88
|
+
async function refreshClaudeToken(current) {
|
|
89
|
+
const res = await (0, undici_1.request)(exports.CLAUDE_TOKEN_URL, {
|
|
90
|
+
method: 'POST',
|
|
91
|
+
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
|
|
92
|
+
body: JSON.stringify({
|
|
93
|
+
client_id: exports.CLAUDE_CLIENT_ID,
|
|
94
|
+
grant_type: 'refresh_token',
|
|
95
|
+
refresh_token: current.refreshToken,
|
|
96
|
+
}),
|
|
97
|
+
bodyTimeout: 30000,
|
|
98
|
+
headersTimeout: 15000,
|
|
99
|
+
});
|
|
100
|
+
const bodyText = await res.body.text();
|
|
101
|
+
if (res.statusCode !== 200) {
|
|
102
|
+
throw new Error(`Claude refresh failed (HTTP ${res.statusCode}): ${bodyText}`);
|
|
103
|
+
}
|
|
104
|
+
const parsed = JSON.parse(bodyText);
|
|
105
|
+
const now = Date.now();
|
|
106
|
+
return {
|
|
107
|
+
...current,
|
|
108
|
+
accessToken: parsed.access_token,
|
|
109
|
+
refreshToken: parsed.refresh_token ?? current.refreshToken,
|
|
110
|
+
expiresAt: now + parsed.expires_in * 1000,
|
|
111
|
+
lastRefresh: now,
|
|
112
|
+
email: parsed.account?.email_address ?? current.email,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=claude-oauth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-oauth.js","sourceRoot":"","sources":["../../../src/llm/oauth/claude-oauth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { PKCECodes } from './pkce';
|
|
2
|
+
import { OAuthTokenStorage } from './store';
|
|
3
|
+
/**
|
|
4
|
+
* Codex (Sign in with ChatGPT) OAuth flow — port từ CLIProxyAPI
|
|
5
|
+
* `internal/auth/codex/openai_auth.go`.
|
|
6
|
+
*
|
|
7
|
+
* Constants giống Codex CLI để OpenAI nhận diện là Codex CLI.
|
|
8
|
+
*/
|
|
9
|
+
export declare const CODEX_AUTH_URL = "https://auth.openai.com/oauth/authorize";
|
|
10
|
+
export declare const CODEX_TOKEN_URL = "https://auth.openai.com/oauth/token";
|
|
11
|
+
export declare const CODEX_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
12
|
+
export declare const CODEX_REDIRECT_URI = "http://localhost:1455/auth/callback";
|
|
13
|
+
export declare const CODEX_SCOPE = "openid email profile offline_access";
|
|
14
|
+
export interface CodexAuthSession {
|
|
15
|
+
authUrl: string;
|
|
16
|
+
state: string;
|
|
17
|
+
pkce: PKCECodes;
|
|
18
|
+
}
|
|
19
|
+
export declare function startCodexAuth(): CodexAuthSession;
|
|
20
|
+
export declare function exchangeCodexCodeForTokens(code: string, pkce: PKCECodes, account?: string): Promise<OAuthTokenStorage>;
|
|
21
|
+
export declare function refreshCodexToken(current: OAuthTokenStorage): Promise<OAuthTokenStorage>;
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CODEX_SCOPE = exports.CODEX_REDIRECT_URI = exports.CODEX_CLIENT_ID = exports.CODEX_TOKEN_URL = exports.CODEX_AUTH_URL = void 0;
|
|
4
|
+
exports.startCodexAuth = startCodexAuth;
|
|
5
|
+
exports.exchangeCodexCodeForTokens = exchangeCodexCodeForTokens;
|
|
6
|
+
exports.refreshCodexToken = refreshCodexToken;
|
|
7
|
+
const undici_1 = require("undici");
|
|
8
|
+
const pkce_1 = require("./pkce");
|
|
9
|
+
/**
|
|
10
|
+
* Codex (Sign in with ChatGPT) OAuth flow — port từ CLIProxyAPI
|
|
11
|
+
* `internal/auth/codex/openai_auth.go`.
|
|
12
|
+
*
|
|
13
|
+
* Constants giống Codex CLI để OpenAI nhận diện là Codex CLI.
|
|
14
|
+
*/
|
|
15
|
+
exports.CODEX_AUTH_URL = 'https://auth.openai.com/oauth/authorize';
|
|
16
|
+
exports.CODEX_TOKEN_URL = 'https://auth.openai.com/oauth/token';
|
|
17
|
+
exports.CODEX_CLIENT_ID = 'app_EMoamEEZ73f0CkXaXp7hrann';
|
|
18
|
+
exports.CODEX_REDIRECT_URI = 'http://localhost:1455/auth/callback';
|
|
19
|
+
exports.CODEX_SCOPE = 'openid email profile offline_access';
|
|
20
|
+
function startCodexAuth() {
|
|
21
|
+
const pkce = (0, pkce_1.generatePKCECodes)();
|
|
22
|
+
const state = (0, pkce_1.generateRandomState)();
|
|
23
|
+
const params = new URLSearchParams({
|
|
24
|
+
client_id: exports.CODEX_CLIENT_ID,
|
|
25
|
+
response_type: 'code',
|
|
26
|
+
redirect_uri: exports.CODEX_REDIRECT_URI,
|
|
27
|
+
scope: exports.CODEX_SCOPE,
|
|
28
|
+
state,
|
|
29
|
+
code_challenge: pkce.codeChallenge,
|
|
30
|
+
code_challenge_method: 'S256',
|
|
31
|
+
prompt: 'login',
|
|
32
|
+
id_token_add_organizations: 'true',
|
|
33
|
+
codex_cli_simplified_flow: 'true',
|
|
34
|
+
});
|
|
35
|
+
return {
|
|
36
|
+
authUrl: `${exports.CODEX_AUTH_URL}?${params.toString()}`,
|
|
37
|
+
state,
|
|
38
|
+
pkce,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Decode JWT payload (id_token) — không verify signature, chỉ extract claims
|
|
43
|
+
* như account_id và email để lưu metadata.
|
|
44
|
+
*/
|
|
45
|
+
function decodeJwtPayload(jwt) {
|
|
46
|
+
try {
|
|
47
|
+
const parts = jwt.split('.');
|
|
48
|
+
if (parts.length < 2)
|
|
49
|
+
return null;
|
|
50
|
+
const padded = parts[1].replace(/-/g, '+').replace(/_/g, '/');
|
|
51
|
+
const json = Buffer.from(padded, 'base64').toString('utf8');
|
|
52
|
+
return JSON.parse(json);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
async function exchangeCodexCodeForTokens(code, pkce, account = 'default') {
|
|
59
|
+
// Codex dùng x-www-form-urlencoded (khác Claude dùng JSON)
|
|
60
|
+
const data = new URLSearchParams({
|
|
61
|
+
grant_type: 'authorization_code',
|
|
62
|
+
client_id: exports.CODEX_CLIENT_ID,
|
|
63
|
+
code,
|
|
64
|
+
redirect_uri: exports.CODEX_REDIRECT_URI,
|
|
65
|
+
code_verifier: pkce.codeVerifier,
|
|
66
|
+
});
|
|
67
|
+
const res = await (0, undici_1.request)(exports.CODEX_TOKEN_URL, {
|
|
68
|
+
method: 'POST',
|
|
69
|
+
headers: {
|
|
70
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
71
|
+
Accept: 'application/json',
|
|
72
|
+
},
|
|
73
|
+
body: data.toString(),
|
|
74
|
+
headersTimeout: 15000,
|
|
75
|
+
bodyTimeout: 30000,
|
|
76
|
+
});
|
|
77
|
+
const text = await res.body.text();
|
|
78
|
+
if (res.statusCode !== 200) {
|
|
79
|
+
throw new Error(`Codex token exchange failed (HTTP ${res.statusCode}): ${text}`);
|
|
80
|
+
}
|
|
81
|
+
const parsed = JSON.parse(text);
|
|
82
|
+
const claims = decodeJwtPayload(parsed.id_token);
|
|
83
|
+
// ChatGPT account ID nằm trong claims.https://api.openai.com/auth.chatgpt_account_id
|
|
84
|
+
// hoặc claims.https://api.openai.com/auth.user_id (tuỳ flow)
|
|
85
|
+
const authClaims = claims?.['https://api.openai.com/auth'] ?? {};
|
|
86
|
+
const accountId = typeof authClaims.chatgpt_account_id === 'string'
|
|
87
|
+
? authClaims.chatgpt_account_id
|
|
88
|
+
: typeof authClaims.user_id === 'string'
|
|
89
|
+
? authClaims.user_id
|
|
90
|
+
: undefined;
|
|
91
|
+
const email = typeof claims?.email === 'string' ? claims.email : undefined;
|
|
92
|
+
const now = Date.now();
|
|
93
|
+
return {
|
|
94
|
+
type: 'codex',
|
|
95
|
+
account,
|
|
96
|
+
accessToken: parsed.access_token,
|
|
97
|
+
refreshToken: parsed.refresh_token,
|
|
98
|
+
idToken: parsed.id_token,
|
|
99
|
+
email,
|
|
100
|
+
expiresAt: now + parsed.expires_in * 1000,
|
|
101
|
+
lastRefresh: now,
|
|
102
|
+
metadata: accountId ? { accountId } : undefined,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
async function refreshCodexToken(current) {
|
|
106
|
+
const data = new URLSearchParams({
|
|
107
|
+
client_id: exports.CODEX_CLIENT_ID,
|
|
108
|
+
grant_type: 'refresh_token',
|
|
109
|
+
refresh_token: current.refreshToken,
|
|
110
|
+
scope: 'openid profile email',
|
|
111
|
+
});
|
|
112
|
+
const res = await (0, undici_1.request)(exports.CODEX_TOKEN_URL, {
|
|
113
|
+
method: 'POST',
|
|
114
|
+
headers: {
|
|
115
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
116
|
+
Accept: 'application/json',
|
|
117
|
+
},
|
|
118
|
+
body: data.toString(),
|
|
119
|
+
headersTimeout: 15000,
|
|
120
|
+
bodyTimeout: 30000,
|
|
121
|
+
});
|
|
122
|
+
const text = await res.body.text();
|
|
123
|
+
if (res.statusCode !== 200) {
|
|
124
|
+
throw new Error(`Codex refresh failed (HTTP ${res.statusCode}): ${text}`);
|
|
125
|
+
}
|
|
126
|
+
const parsed = JSON.parse(text);
|
|
127
|
+
const now = Date.now();
|
|
128
|
+
return {
|
|
129
|
+
...current,
|
|
130
|
+
accessToken: parsed.access_token,
|
|
131
|
+
refreshToken: parsed.refresh_token ?? current.refreshToken,
|
|
132
|
+
idToken: parsed.id_token ?? current.idToken,
|
|
133
|
+
expiresAt: now + parsed.expires_in * 1000,
|
|
134
|
+
lastRefresh: now,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=codex-oauth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex-oauth.js","sourceRoot":"","sources":["../../../src/llm/oauth/codex-oauth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { OAuthTokenStorage } from './store';
|
|
2
|
+
/**
|
|
3
|
+
* Gemini (Code Assist) OAuth flow — port từ CLIProxyAPI
|
|
4
|
+
* `internal/auth/gemini/gemini_auth.go`.
|
|
5
|
+
*
|
|
6
|
+
* Khác Claude/Codex: dùng Google OAuth 2.0 chuẩn với client_id + client_secret
|
|
7
|
+
* (PUBLIC client của Gemini CLI), KHÔNG dùng PKCE.
|
|
8
|
+
*
|
|
9
|
+
* Hai constants này là PUBLIC client của Gemini CLI — đã được Google công bố
|
|
10
|
+
* (xem gemini-cli source code) và CLIProxyAPI hardcode tương tự.
|
|
11
|
+
*/
|
|
12
|
+
export declare const GEMINI_AUTH_URL = "https://accounts.google.com/o/oauth2/auth";
|
|
13
|
+
export declare const GEMINI_TOKEN_URL = "https://oauth2.googleapis.com/token";
|
|
14
|
+
export declare const GEMINI_USERINFO_URL = "https://www.googleapis.com/oauth2/v1/userinfo?alt=json";
|
|
15
|
+
export declare const GEMINI_CLIENT_ID = "681255809395-oo8ft2oprdrnp9e3aqf6av3hmdib135j.apps.googleusercontent.com";
|
|
16
|
+
export declare const GEMINI_CLIENT_SECRET = "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";
|
|
17
|
+
export declare const GEMINI_DEFAULT_CALLBACK_PORT = 8085;
|
|
18
|
+
export declare const GEMINI_SCOPES: string[];
|
|
19
|
+
export interface GeminiAuthSession {
|
|
20
|
+
authUrl: string;
|
|
21
|
+
state: string;
|
|
22
|
+
redirectUri: string;
|
|
23
|
+
}
|
|
24
|
+
export declare function startGeminiAuth(callbackPort?: number): GeminiAuthSession;
|
|
25
|
+
export declare function exchangeGeminiCodeForTokens(code: string, redirectUri: string, account?: string): Promise<OAuthTokenStorage>;
|
|
26
|
+
export declare function refreshGeminiToken(current: OAuthTokenStorage): Promise<OAuthTokenStorage>;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GEMINI_SCOPES = exports.GEMINI_DEFAULT_CALLBACK_PORT = exports.GEMINI_CLIENT_SECRET = exports.GEMINI_CLIENT_ID = exports.GEMINI_USERINFO_URL = exports.GEMINI_TOKEN_URL = exports.GEMINI_AUTH_URL = void 0;
|
|
4
|
+
exports.startGeminiAuth = startGeminiAuth;
|
|
5
|
+
exports.exchangeGeminiCodeForTokens = exchangeGeminiCodeForTokens;
|
|
6
|
+
exports.refreshGeminiToken = refreshGeminiToken;
|
|
7
|
+
const undici_1 = require("undici");
|
|
8
|
+
const pkce_1 = require("./pkce");
|
|
9
|
+
/**
|
|
10
|
+
* Gemini (Code Assist) OAuth flow — port từ CLIProxyAPI
|
|
11
|
+
* `internal/auth/gemini/gemini_auth.go`.
|
|
12
|
+
*
|
|
13
|
+
* Khác Claude/Codex: dùng Google OAuth 2.0 chuẩn với client_id + client_secret
|
|
14
|
+
* (PUBLIC client của Gemini CLI), KHÔNG dùng PKCE.
|
|
15
|
+
*
|
|
16
|
+
* Hai constants này là PUBLIC client của Gemini CLI — đã được Google công bố
|
|
17
|
+
* (xem gemini-cli source code) và CLIProxyAPI hardcode tương tự.
|
|
18
|
+
*/
|
|
19
|
+
exports.GEMINI_AUTH_URL = 'https://accounts.google.com/o/oauth2/auth';
|
|
20
|
+
exports.GEMINI_TOKEN_URL = 'https://oauth2.googleapis.com/token';
|
|
21
|
+
exports.GEMINI_USERINFO_URL = 'https://www.googleapis.com/oauth2/v1/userinfo?alt=json';
|
|
22
|
+
exports.GEMINI_CLIENT_ID = '681255809395-oo8ft2oprdrnp9e3aqf6av3hmdib135j.apps.googleusercontent.com';
|
|
23
|
+
exports.GEMINI_CLIENT_SECRET = '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';
|
|
24
|
+
exports.GEMINI_DEFAULT_CALLBACK_PORT = 8085;
|
|
25
|
+
exports.GEMINI_SCOPES = [
|
|
26
|
+
'https://www.googleapis.com/auth/cloud-platform',
|
|
27
|
+
'https://www.googleapis.com/auth/userinfo.email',
|
|
28
|
+
'https://www.googleapis.com/auth/userinfo.profile',
|
|
29
|
+
];
|
|
30
|
+
function startGeminiAuth(callbackPort = exports.GEMINI_DEFAULT_CALLBACK_PORT) {
|
|
31
|
+
const state = (0, pkce_1.generateRandomState)();
|
|
32
|
+
const redirectUri = `http://localhost:${callbackPort}/oauth2callback`;
|
|
33
|
+
const params = new URLSearchParams({
|
|
34
|
+
client_id: exports.GEMINI_CLIENT_ID,
|
|
35
|
+
redirect_uri: redirectUri,
|
|
36
|
+
response_type: 'code',
|
|
37
|
+
scope: exports.GEMINI_SCOPES.join(' '),
|
|
38
|
+
state,
|
|
39
|
+
access_type: 'offline',
|
|
40
|
+
prompt: 'consent',
|
|
41
|
+
});
|
|
42
|
+
return {
|
|
43
|
+
authUrl: `${exports.GEMINI_AUTH_URL}?${params.toString()}`,
|
|
44
|
+
state,
|
|
45
|
+
redirectUri,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
async function exchangeGeminiCodeForTokens(code, redirectUri, account = 'default') {
|
|
49
|
+
const data = new URLSearchParams({
|
|
50
|
+
code,
|
|
51
|
+
client_id: exports.GEMINI_CLIENT_ID,
|
|
52
|
+
client_secret: exports.GEMINI_CLIENT_SECRET,
|
|
53
|
+
redirect_uri: redirectUri,
|
|
54
|
+
grant_type: 'authorization_code',
|
|
55
|
+
});
|
|
56
|
+
const res = await (0, undici_1.request)(exports.GEMINI_TOKEN_URL, {
|
|
57
|
+
method: 'POST',
|
|
58
|
+
headers: {
|
|
59
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
60
|
+
Accept: 'application/json',
|
|
61
|
+
},
|
|
62
|
+
body: data.toString(),
|
|
63
|
+
headersTimeout: 15000,
|
|
64
|
+
bodyTimeout: 30000,
|
|
65
|
+
});
|
|
66
|
+
const text = await res.body.text();
|
|
67
|
+
if (res.statusCode !== 200) {
|
|
68
|
+
throw new Error(`Gemini token exchange failed (HTTP ${res.statusCode}): ${text}`);
|
|
69
|
+
}
|
|
70
|
+
const parsed = JSON.parse(text);
|
|
71
|
+
// Fetch userinfo để lưu email
|
|
72
|
+
const email = await fetchGeminiUserInfo(parsed.access_token).catch(() => undefined);
|
|
73
|
+
const now = Date.now();
|
|
74
|
+
return {
|
|
75
|
+
type: 'gemini',
|
|
76
|
+
account,
|
|
77
|
+
accessToken: parsed.access_token,
|
|
78
|
+
refreshToken: parsed.refresh_token ?? '',
|
|
79
|
+
idToken: parsed.id_token,
|
|
80
|
+
email,
|
|
81
|
+
expiresAt: now + parsed.expires_in * 1000,
|
|
82
|
+
lastRefresh: now,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
async function fetchGeminiUserInfo(accessToken) {
|
|
86
|
+
const res = await (0, undici_1.request)(exports.GEMINI_USERINFO_URL, {
|
|
87
|
+
method: 'GET',
|
|
88
|
+
headers: { Authorization: `Bearer ${accessToken}` },
|
|
89
|
+
headersTimeout: 10000,
|
|
90
|
+
bodyTimeout: 10000,
|
|
91
|
+
});
|
|
92
|
+
if (res.statusCode !== 200)
|
|
93
|
+
return undefined;
|
|
94
|
+
const info = JSON.parse(await res.body.text());
|
|
95
|
+
return info.email;
|
|
96
|
+
}
|
|
97
|
+
async function refreshGeminiToken(current) {
|
|
98
|
+
if (!current.refreshToken) {
|
|
99
|
+
throw new Error('Gemini refresh token missing — please re-login');
|
|
100
|
+
}
|
|
101
|
+
const data = new URLSearchParams({
|
|
102
|
+
client_id: exports.GEMINI_CLIENT_ID,
|
|
103
|
+
client_secret: exports.GEMINI_CLIENT_SECRET,
|
|
104
|
+
grant_type: 'refresh_token',
|
|
105
|
+
refresh_token: current.refreshToken,
|
|
106
|
+
});
|
|
107
|
+
const res = await (0, undici_1.request)(exports.GEMINI_TOKEN_URL, {
|
|
108
|
+
method: 'POST',
|
|
109
|
+
headers: {
|
|
110
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
111
|
+
Accept: 'application/json',
|
|
112
|
+
},
|
|
113
|
+
body: data.toString(),
|
|
114
|
+
headersTimeout: 15000,
|
|
115
|
+
bodyTimeout: 30000,
|
|
116
|
+
});
|
|
117
|
+
const text = await res.body.text();
|
|
118
|
+
if (res.statusCode !== 200) {
|
|
119
|
+
throw new Error(`Gemini refresh failed (HTTP ${res.statusCode}): ${text}`);
|
|
120
|
+
}
|
|
121
|
+
const parsed = JSON.parse(text);
|
|
122
|
+
const now = Date.now();
|
|
123
|
+
return {
|
|
124
|
+
...current,
|
|
125
|
+
accessToken: parsed.access_token,
|
|
126
|
+
refreshToken: parsed.refresh_token ?? current.refreshToken,
|
|
127
|
+
idToken: parsed.id_token ?? current.idToken,
|
|
128
|
+
expiresAt: now + parsed.expires_in * 1000,
|
|
129
|
+
lastRefresh: now,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=gemini-oauth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gemini-oauth.js","sourceRoot":"","sources":["../../../src/llm/oauth/gemini-oauth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { PKCECodes } from './pkce';
|
|
2
|
+
import { OAuthTokenStorage } from './store';
|
|
3
|
+
/**
|
|
4
|
+
* iFlow OAuth — port từ `internal/auth/iflow/iflow_auth.go`.
|
|
5
|
+
* Có 2 path: OAuth PKCE thường + Cookie (BXAuth) flow.
|
|
6
|
+
*/
|
|
7
|
+
export declare const IFLOW_AUTH_URL = "https://iflow.cn/oauth";
|
|
8
|
+
export declare const IFLOW_TOKEN_URL = "https://iflow.cn/oauth/token";
|
|
9
|
+
export declare const IFLOW_API_KEY_URL = "https://platform.iflow.cn/api/openapi/apikey";
|
|
10
|
+
export declare const IFLOW_CLIENT_ID = "10009311001";
|
|
11
|
+
export declare const IFLOW_DEFAULT_CALLBACK_PORT = 11451;
|
|
12
|
+
export declare const IFLOW_DEFAULT_API_BASE = "https://apis.iflow.cn/v1";
|
|
13
|
+
export interface IFlowAuthSession {
|
|
14
|
+
authUrl: string;
|
|
15
|
+
state: string;
|
|
16
|
+
pkce: PKCECodes;
|
|
17
|
+
redirectUri: string;
|
|
18
|
+
}
|
|
19
|
+
export declare function startIFlowAuth(callbackPort?: number): IFlowAuthSession;
|
|
20
|
+
export declare function exchangeIFlowCodeForTokens(code: string, pkce: PKCECodes, redirectUri: string, account?: string): Promise<OAuthTokenStorage>;
|
|
21
|
+
export declare function refreshIFlowToken(current: OAuthTokenStorage): Promise<OAuthTokenStorage>;
|
|
22
|
+
/**
|
|
23
|
+
* Cookie flow: user paste cookie BXAuth từ iflow.cn (đã đăng nhập browser).
|
|
24
|
+
* Gọi platform.iflow.cn/api/openapi/apikey để đổi cookie lấy API key.
|
|
25
|
+
*/
|
|
26
|
+
export declare function exchangeIFlowCookieForApiKey(cookie: string, account?: string): Promise<OAuthTokenStorage>;
|