ai-zero-token 1.0.1 → 1.0.3
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/CHANGELOG.md +14 -0
- package/README.md +235 -69
- package/dist/api.js +0 -1
- package/dist/cli/commands/ask.js +131 -5
- package/dist/cli/commands/clear.js +0 -1
- package/dist/cli/commands/help.js +17 -11
- package/dist/cli/commands/login.js +0 -1
- package/dist/cli/commands/models.js +14 -4
- package/dist/cli/commands/serve.js +41 -4
- package/dist/cli/commands/start.js +10 -0
- package/dist/cli/commands/status.js +1 -1
- package/dist/cli/index.js +5 -2
- package/dist/cli/shared.js +57 -6
- package/dist/cli.js +0 -1
- package/dist/core/context.js +10 -2
- package/dist/core/models/openai-codex-models.js +89 -1
- package/dist/core/providers/http-client.js +137 -14
- package/dist/core/providers/openai-codex/chat.js +217 -24
- package/dist/core/providers/openai-codex/oauth.js +15 -4
- package/dist/core/providers/openai-codex/pkce.js +0 -1
- package/dist/core/services/auth-service.js +125 -16
- package/dist/core/services/chat-service.js +24 -14
- package/dist/core/services/config-service.js +4 -5
- package/dist/core/services/image-service.js +405 -0
- package/dist/core/services/model-service.js +35 -8
- package/dist/core/services/version-service.js +97 -0
- package/dist/core/store/profile-store.js +79 -6
- package/dist/core/store/settings-store.js +1 -2
- package/dist/core/types.js +0 -1
- package/dist/http.js +0 -1
- package/dist/models.js +0 -1
- package/dist/oauth.js +0 -1
- package/dist/pkce.js +0 -1
- package/dist/server/admin-page.js +3165 -0
- package/dist/server/app.js +599 -40
- package/dist/server/index.js +0 -1
- package/dist/store.js +0 -1
- package/docs/API_USAGE.md +120 -0
- package/package.json +14 -3
- package/dist/api.js.map +0 -1
- package/dist/cli/commands/ask.js.map +0 -1
- package/dist/cli/commands/clear.js.map +0 -1
- package/dist/cli/commands/help.js.map +0 -1
- package/dist/cli/commands/login.js.map +0 -1
- package/dist/cli/commands/models.js.map +0 -1
- package/dist/cli/commands/serve.js.map +0 -1
- package/dist/cli/commands/status.js.map +0 -1
- package/dist/cli/index.js.map +0 -1
- package/dist/cli/shared.js.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/core/context.js.map +0 -1
- package/dist/core/models/openai-codex-models.js.map +0 -1
- package/dist/core/providers/http-client.js.map +0 -1
- package/dist/core/providers/openai-codex/chat.js.map +0 -1
- package/dist/core/providers/openai-codex/oauth.js.map +0 -1
- package/dist/core/providers/openai-codex/pkce.js.map +0 -1
- package/dist/core/services/auth-service.js.map +0 -1
- package/dist/core/services/chat-service.js.map +0 -1
- package/dist/core/services/config-service.js.map +0 -1
- package/dist/core/services/model-service.js.map +0 -1
- package/dist/core/store/profile-store.js.map +0 -1
- package/dist/core/store/settings-store.js.map +0 -1
- package/dist/core/types.js.map +0 -1
- package/dist/http.js.map +0 -1
- package/dist/models.js.map +0 -1
- package/dist/oauth.js.map +0 -1
- package/dist/pkce.js.map +0 -1
- package/dist/server/app.js.map +0 -1
- package/dist/server/index.js.map +0 -1
- package/dist/store.js.map +0 -1
|
@@ -2,6 +2,101 @@
|
|
|
2
2
|
import { DEFAULT_CODEX_MODEL } from "../../models/openai-codex-models.js";
|
|
3
3
|
import { requestText } from "../http-client.js";
|
|
4
4
|
const CODEX_RESPONSES_URL = "https://chatgpt.com/backend-api/codex/responses";
|
|
5
|
+
const URL_KEY_RE = /(url|uri|href|download|preview|thumbnail|image|asset|file)/i;
|
|
6
|
+
const REFERENCE_KEY_RE = /(image|asset|file|media|blob|artifact|download|preview|thumbnail)/i;
|
|
7
|
+
const REFERENCE_VALUE_RE = /^(file|asset|image|img|media|blob)-[\w-]+$/i;
|
|
8
|
+
function parseOptionalNumber(value) {
|
|
9
|
+
if (typeof value !== "string") {
|
|
10
|
+
return void 0;
|
|
11
|
+
}
|
|
12
|
+
const trimmed = value.trim();
|
|
13
|
+
if (!trimmed) {
|
|
14
|
+
return void 0;
|
|
15
|
+
}
|
|
16
|
+
const parsed = Number(trimmed);
|
|
17
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
18
|
+
}
|
|
19
|
+
function parseOptionalBoolean(value) {
|
|
20
|
+
if (typeof value !== "string") {
|
|
21
|
+
return void 0;
|
|
22
|
+
}
|
|
23
|
+
const trimmed = value.trim().toLowerCase();
|
|
24
|
+
if (trimmed === "true") {
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
if (trimmed === "false") {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
return void 0;
|
|
31
|
+
}
|
|
32
|
+
function parseOptionalText(value) {
|
|
33
|
+
if (typeof value !== "string") {
|
|
34
|
+
return void 0;
|
|
35
|
+
}
|
|
36
|
+
const trimmed = value.trim();
|
|
37
|
+
return trimmed ? trimmed : void 0;
|
|
38
|
+
}
|
|
39
|
+
function extractCodexQuotaSnapshot(headers, requestId) {
|
|
40
|
+
const activeLimit = parseOptionalText(headers["x-codex-active-limit"]);
|
|
41
|
+
const planType = parseOptionalText(headers["x-codex-plan-type"]);
|
|
42
|
+
const primaryUsedPercent = parseOptionalNumber(headers["x-codex-primary-used-percent"]);
|
|
43
|
+
const secondaryUsedPercent = parseOptionalNumber(headers["x-codex-secondary-used-percent"]);
|
|
44
|
+
const primaryWindowMinutes = parseOptionalNumber(headers["x-codex-primary-window-minutes"]);
|
|
45
|
+
const secondaryWindowMinutes = parseOptionalNumber(headers["x-codex-secondary-window-minutes"]);
|
|
46
|
+
const primaryResetAfterSeconds = parseOptionalNumber(headers["x-codex-primary-reset-after-seconds"]);
|
|
47
|
+
const secondaryResetAfterSeconds = parseOptionalNumber(headers["x-codex-secondary-reset-after-seconds"]);
|
|
48
|
+
const primaryResetAt = parseOptionalNumber(headers["x-codex-primary-reset-at"]);
|
|
49
|
+
const secondaryResetAt = parseOptionalNumber(headers["x-codex-secondary-reset-at"]);
|
|
50
|
+
const primaryOverSecondaryLimitPercent = parseOptionalNumber(
|
|
51
|
+
headers["x-codex-primary-over-secondary-limit-percent"]
|
|
52
|
+
);
|
|
53
|
+
const creditsHasCredits = parseOptionalBoolean(headers["x-codex-credits-has-credits"]);
|
|
54
|
+
const creditsUnlimited = parseOptionalBoolean(headers["x-codex-credits-unlimited"]);
|
|
55
|
+
const creditsBalance = parseOptionalText(headers["x-codex-credits-balance"]);
|
|
56
|
+
const promoCampaignId = parseOptionalText(headers["x-codex-promo-campaign-id"]);
|
|
57
|
+
const promoMessage = parseOptionalText(headers["x-codex-promo-message"]);
|
|
58
|
+
const hasQuotaData = [
|
|
59
|
+
activeLimit,
|
|
60
|
+
planType,
|
|
61
|
+
primaryUsedPercent,
|
|
62
|
+
secondaryUsedPercent,
|
|
63
|
+
primaryWindowMinutes,
|
|
64
|
+
secondaryWindowMinutes,
|
|
65
|
+
primaryResetAfterSeconds,
|
|
66
|
+
secondaryResetAfterSeconds,
|
|
67
|
+
primaryResetAt,
|
|
68
|
+
secondaryResetAt,
|
|
69
|
+
primaryOverSecondaryLimitPercent,
|
|
70
|
+
creditsHasCredits,
|
|
71
|
+
creditsUnlimited,
|
|
72
|
+
creditsBalance,
|
|
73
|
+
promoCampaignId,
|
|
74
|
+
promoMessage
|
|
75
|
+
].some((value) => typeof value !== "undefined");
|
|
76
|
+
if (!hasQuotaData) {
|
|
77
|
+
return void 0;
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
capturedAt: Date.now(),
|
|
81
|
+
sourceRequestId: requestId,
|
|
82
|
+
activeLimit,
|
|
83
|
+
planType,
|
|
84
|
+
primaryUsedPercent,
|
|
85
|
+
secondaryUsedPercent,
|
|
86
|
+
primaryWindowMinutes,
|
|
87
|
+
secondaryWindowMinutes,
|
|
88
|
+
primaryResetAfterSeconds,
|
|
89
|
+
secondaryResetAfterSeconds,
|
|
90
|
+
primaryResetAt,
|
|
91
|
+
secondaryResetAt,
|
|
92
|
+
primaryOverSecondaryLimitPercent,
|
|
93
|
+
creditsHasCredits,
|
|
94
|
+
creditsUnlimited,
|
|
95
|
+
creditsBalance,
|
|
96
|
+
promoCampaignId,
|
|
97
|
+
promoMessage
|
|
98
|
+
};
|
|
99
|
+
}
|
|
5
100
|
function extractOutputText(payload) {
|
|
6
101
|
if (!payload || typeof payload !== "object") {
|
|
7
102
|
return "";
|
|
@@ -43,12 +138,92 @@ function parseSseEvents(body) {
|
|
|
43
138
|
}
|
|
44
139
|
return events;
|
|
45
140
|
}
|
|
46
|
-
function
|
|
141
|
+
function pushArtifactCandidate(items, dedupe, candidate) {
|
|
142
|
+
const signature = `${candidate.source}:${candidate.path}:${candidate.key}:${candidate.kind}:${candidate.value}`;
|
|
143
|
+
if (dedupe.has(signature)) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
dedupe.add(signature);
|
|
147
|
+
items.push(candidate);
|
|
148
|
+
}
|
|
149
|
+
function collectArtifactCandidates(value, source, path = [], items = [], dedupe = /* @__PURE__ */ new Set()) {
|
|
150
|
+
if (typeof value === "string") {
|
|
151
|
+
const key = path[path.length - 1] ?? "";
|
|
152
|
+
const joinedPath = path.join(".");
|
|
153
|
+
const trimmed = value.trim();
|
|
154
|
+
if (!trimmed) {
|
|
155
|
+
return items;
|
|
156
|
+
}
|
|
157
|
+
if (/^https?:\/\//i.test(trimmed)) {
|
|
158
|
+
pushArtifactCandidate(items, dedupe, {
|
|
159
|
+
source,
|
|
160
|
+
path: joinedPath,
|
|
161
|
+
key,
|
|
162
|
+
kind: "url",
|
|
163
|
+
value: trimmed
|
|
164
|
+
});
|
|
165
|
+
return items;
|
|
166
|
+
}
|
|
167
|
+
if (REFERENCE_KEY_RE.test(key) || REFERENCE_VALUE_RE.test(trimmed)) {
|
|
168
|
+
pushArtifactCandidate(items, dedupe, {
|
|
169
|
+
source,
|
|
170
|
+
path: joinedPath,
|
|
171
|
+
key,
|
|
172
|
+
kind: "reference",
|
|
173
|
+
value: trimmed
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
return items;
|
|
177
|
+
}
|
|
178
|
+
if (Array.isArray(value)) {
|
|
179
|
+
value.forEach((item, index) => {
|
|
180
|
+
collectArtifactCandidates(item, source, [...path, String(index)], items, dedupe);
|
|
181
|
+
});
|
|
182
|
+
return items;
|
|
183
|
+
}
|
|
184
|
+
if (!value || typeof value !== "object") {
|
|
185
|
+
return items;
|
|
186
|
+
}
|
|
187
|
+
for (const [key, nested] of Object.entries(value)) {
|
|
188
|
+
const nextPath = [...path, key];
|
|
189
|
+
if (typeof nested === "string" && (URL_KEY_RE.test(key) || /^https?:\/\//i.test(nested))) {
|
|
190
|
+
collectArtifactCandidates(nested, source, nextPath, items, dedupe);
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
collectArtifactCandidates(nested, source, nextPath, items, dedupe);
|
|
194
|
+
}
|
|
195
|
+
return items;
|
|
196
|
+
}
|
|
197
|
+
function buildDefaultRequestBody(params) {
|
|
198
|
+
const body = {
|
|
199
|
+
model: params.model ?? DEFAULT_CODEX_MODEL,
|
|
200
|
+
store: false,
|
|
201
|
+
stream: true,
|
|
202
|
+
instructions: params.system ?? "",
|
|
203
|
+
text: { verbosity: "medium" },
|
|
204
|
+
include: ["reasoning.encrypted_content"],
|
|
205
|
+
tool_choice: "auto",
|
|
206
|
+
parallel_tool_calls: true
|
|
207
|
+
};
|
|
208
|
+
if (typeof params.prompt === "string" && params.prompt.trim()) {
|
|
209
|
+
body.input = [
|
|
210
|
+
{
|
|
211
|
+
role: "user",
|
|
212
|
+
content: [{ type: "input_text", text: params.prompt }]
|
|
213
|
+
}
|
|
214
|
+
];
|
|
215
|
+
}
|
|
216
|
+
return body;
|
|
217
|
+
}
|
|
218
|
+
function extractCodexText(body, requestBody) {
|
|
47
219
|
const events = parseSseEvents(body);
|
|
48
220
|
let responsePayload;
|
|
49
221
|
let accumulated = "";
|
|
50
222
|
for (const event of events) {
|
|
51
|
-
if (
|
|
223
|
+
if (typeof event.response !== "undefined") {
|
|
224
|
+
responsePayload = event.response;
|
|
225
|
+
}
|
|
226
|
+
if (event.type === "response.completed" || event.type === "response.done" || event.type === "response.incomplete" || event.type === "response.failed") {
|
|
52
227
|
responsePayload = event.response;
|
|
53
228
|
}
|
|
54
229
|
if (typeof event.delta === "string" && event.delta) {
|
|
@@ -56,12 +231,39 @@ function extractCodexText(body) {
|
|
|
56
231
|
}
|
|
57
232
|
}
|
|
58
233
|
const completedText = extractOutputText(responsePayload);
|
|
234
|
+
const artifacts = [
|
|
235
|
+
...collectArtifactCandidates(responsePayload, "response"),
|
|
236
|
+
...collectArtifactCandidates(events, "event")
|
|
237
|
+
];
|
|
59
238
|
if (completedText) {
|
|
60
|
-
return {
|
|
239
|
+
return {
|
|
240
|
+
text: completedText,
|
|
241
|
+
raw: {
|
|
242
|
+
request: requestBody,
|
|
243
|
+
response: responsePayload ?? null,
|
|
244
|
+
events
|
|
245
|
+
},
|
|
246
|
+
artifacts
|
|
247
|
+
};
|
|
61
248
|
}
|
|
62
|
-
return {
|
|
249
|
+
return {
|
|
250
|
+
text: accumulated.trim(),
|
|
251
|
+
raw: {
|
|
252
|
+
request: requestBody,
|
|
253
|
+
response: responsePayload ?? null,
|
|
254
|
+
events
|
|
255
|
+
},
|
|
256
|
+
artifacts
|
|
257
|
+
};
|
|
63
258
|
}
|
|
64
259
|
async function askOpenAICodex(params) {
|
|
260
|
+
const requestBody = {
|
|
261
|
+
...buildDefaultRequestBody(params),
|
|
262
|
+
...params.bodyOverride ?? {}
|
|
263
|
+
};
|
|
264
|
+
if (typeof requestBody.input === "undefined") {
|
|
265
|
+
throw new Error("Codex \u8BF7\u6C42\u7F3A\u5C11 input\u3002\u8BF7\u63D0\u4F9B prompt \u6216\u5728\u5B9E\u9A8C\u8BF7\u6C42\u4F53\u91CC\u663E\u5F0F\u4F20\u5165 input\u3002");
|
|
266
|
+
}
|
|
65
267
|
const response = await requestText({
|
|
66
268
|
method: "POST",
|
|
67
269
|
url: CODEX_RESPONSES_URL,
|
|
@@ -74,29 +276,20 @@ async function askOpenAICodex(params) {
|
|
|
74
276
|
Originator: "pi",
|
|
75
277
|
"User-Agent": "pi (bun demo)"
|
|
76
278
|
},
|
|
77
|
-
body: JSON.stringify(
|
|
78
|
-
model: params.model ?? DEFAULT_CODEX_MODEL,
|
|
79
|
-
store: false,
|
|
80
|
-
stream: true,
|
|
81
|
-
instructions: params.system ?? "",
|
|
82
|
-
input: [
|
|
83
|
-
{
|
|
84
|
-
role: "user",
|
|
85
|
-
content: [{ type: "input_text", text: params.prompt }]
|
|
86
|
-
}
|
|
87
|
-
],
|
|
88
|
-
text: { verbosity: "medium" },
|
|
89
|
-
include: ["reasoning.encrypted_content"],
|
|
90
|
-
tool_choice: "auto",
|
|
91
|
-
parallel_tool_calls: true
|
|
92
|
-
})
|
|
279
|
+
body: JSON.stringify(requestBody)
|
|
93
280
|
});
|
|
281
|
+
const quota = extractCodexQuotaSnapshot(response.headers, response.requestId);
|
|
94
282
|
if (response.status < 200 || response.status >= 300) {
|
|
95
|
-
|
|
283
|
+
const error = new Error(`\u8C03\u7528 Responses API \u5931\u8D25: HTTP ${response.status} via ${response.transport} ${response.body}`);
|
|
284
|
+
error.quota = quota;
|
|
285
|
+
throw error;
|
|
96
286
|
}
|
|
97
|
-
return
|
|
287
|
+
return {
|
|
288
|
+
...extractCodexText(response.body, requestBody),
|
|
289
|
+
quota
|
|
290
|
+
};
|
|
98
291
|
}
|
|
99
292
|
export {
|
|
100
|
-
askOpenAICodex
|
|
293
|
+
askOpenAICodex,
|
|
294
|
+
extractCodexQuotaSnapshot
|
|
101
295
|
};
|
|
102
|
-
//# sourceMappingURL=chat.js.map
|
|
@@ -12,6 +12,7 @@ const TOKEN_URL = "https://auth.openai.com/oauth/token";
|
|
|
12
12
|
const REDIRECT_URI = "http://localhost:1455/auth/callback";
|
|
13
13
|
const SCOPE = "openid profile email offline_access";
|
|
14
14
|
const JWT_CLAIM_PATH = "https://api.openai.com/auth";
|
|
15
|
+
const PROFILE_CLAIM_PATH = "https://api.openai.com/profile";
|
|
15
16
|
const SUCCESS_HTML = `<!doctype html>
|
|
16
17
|
<html lang="zh-CN">
|
|
17
18
|
<head>
|
|
@@ -41,6 +42,18 @@ function decodeJwtPayload(token) {
|
|
|
41
42
|
return null;
|
|
42
43
|
}
|
|
43
44
|
}
|
|
45
|
+
function extractEmailFromPayload(payload) {
|
|
46
|
+
const profileClaim = payload?.[PROFILE_CLAIM_PATH];
|
|
47
|
+
const nestedEmail = profileClaim?.email;
|
|
48
|
+
if (typeof nestedEmail === "string" && nestedEmail.trim()) {
|
|
49
|
+
return nestedEmail.trim();
|
|
50
|
+
}
|
|
51
|
+
const topLevelEmail = payload?.email;
|
|
52
|
+
if (typeof topLevelEmail === "string" && topLevelEmail.trim()) {
|
|
53
|
+
return topLevelEmail.trim();
|
|
54
|
+
}
|
|
55
|
+
return void 0;
|
|
56
|
+
}
|
|
44
57
|
function parseAuthorizationInput(value) {
|
|
45
58
|
const trimmed = value.trim();
|
|
46
59
|
if (!trimmed) {
|
|
@@ -74,11 +87,10 @@ function extractProfile(accessToken, refreshToken, expires) {
|
|
|
74
87
|
if (typeof accountId !== "string" || !accountId.trim()) {
|
|
75
88
|
throw new Error("\u65E0\u6CD5\u4ECE access token \u4E2D\u63D0\u53D6 accountId\u3002");
|
|
76
89
|
}
|
|
77
|
-
const email =
|
|
78
|
-
const profileKey = email ?? accountId;
|
|
90
|
+
const email = extractEmailFromPayload(payload);
|
|
79
91
|
return {
|
|
80
92
|
provider: "openai-codex",
|
|
81
|
-
profileId: `openai-codex:${
|
|
93
|
+
profileId: `openai-codex:${accountId}`,
|
|
82
94
|
mode: "oauth_account",
|
|
83
95
|
access: accessToken,
|
|
84
96
|
refresh: refreshToken,
|
|
@@ -281,4 +293,3 @@ export {
|
|
|
281
293
|
loginOpenAICodex,
|
|
282
294
|
refreshOpenAICodexToken
|
|
283
295
|
};
|
|
284
|
-
//# sourceMappingURL=oauth.js.map
|
|
@@ -2,57 +2,166 @@
|
|
|
2
2
|
import {
|
|
3
3
|
clearStore,
|
|
4
4
|
getActiveProfile,
|
|
5
|
-
|
|
5
|
+
listProfiles,
|
|
6
|
+
removeProfile,
|
|
7
|
+
saveProfile,
|
|
8
|
+
setActiveProfile,
|
|
9
|
+
updateProfile
|
|
6
10
|
} from "../store/profile-store.js";
|
|
7
11
|
import {
|
|
8
12
|
loginOpenAICodex,
|
|
9
13
|
refreshOpenAICodexToken
|
|
10
14
|
} from "../providers/openai-codex/oauth.js";
|
|
15
|
+
import { askOpenAICodex } from "../providers/openai-codex/chat.js";
|
|
11
16
|
class AuthService {
|
|
12
17
|
constructor(configService) {
|
|
13
18
|
this.configService = configService;
|
|
14
19
|
}
|
|
20
|
+
maskSecret(value) {
|
|
21
|
+
if (value.length <= 12) {
|
|
22
|
+
return value;
|
|
23
|
+
}
|
|
24
|
+
return `${value.slice(0, 8)}...${value.slice(-6)}`;
|
|
25
|
+
}
|
|
26
|
+
toManagedProfile(profile) {
|
|
27
|
+
return {
|
|
28
|
+
...profile,
|
|
29
|
+
mode: "oauth_account"
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
toProfileSummary(profile, activeProfileId) {
|
|
33
|
+
return {
|
|
34
|
+
provider: profile.provider,
|
|
35
|
+
profileId: profile.profileId,
|
|
36
|
+
accountId: profile.accountId,
|
|
37
|
+
email: profile.email,
|
|
38
|
+
quota: profile.quota,
|
|
39
|
+
expiresAt: profile.expires,
|
|
40
|
+
accessTokenPreview: this.maskSecret(profile.access),
|
|
41
|
+
refreshTokenPreview: this.maskSecret(profile.refresh),
|
|
42
|
+
isActive: profile.profileId === activeProfileId
|
|
43
|
+
};
|
|
44
|
+
}
|
|
15
45
|
async login(provider) {
|
|
16
46
|
if (provider !== "openai-codex") {
|
|
17
47
|
throw new Error(`\u6682\u4E0D\u652F\u6301 provider: ${provider}`);
|
|
18
48
|
}
|
|
19
49
|
const profile = await loginOpenAICodex();
|
|
20
50
|
await saveProfile(profile);
|
|
21
|
-
return
|
|
22
|
-
...profile,
|
|
23
|
-
mode: "oauth_account"
|
|
24
|
-
};
|
|
51
|
+
return this.toManagedProfile(profile);
|
|
25
52
|
}
|
|
26
53
|
async getActiveProfile(provider = "openai-codex") {
|
|
27
54
|
const profile = await getActiveProfile();
|
|
28
55
|
if (!profile || profile.provider !== provider) {
|
|
29
56
|
return null;
|
|
30
57
|
}
|
|
31
|
-
return
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
58
|
+
return this.toManagedProfile(profile);
|
|
59
|
+
}
|
|
60
|
+
async listProfiles(provider = "openai-codex") {
|
|
61
|
+
const [profiles, activeProfile] = await Promise.all([
|
|
62
|
+
listProfiles(),
|
|
63
|
+
this.getActiveProfile(provider)
|
|
64
|
+
]);
|
|
65
|
+
const activeProfileId = activeProfile?.profileId;
|
|
66
|
+
return profiles.filter((profile) => profile.provider === provider).sort((left, right) => {
|
|
67
|
+
if (left.profileId === activeProfileId) {
|
|
68
|
+
return -1;
|
|
69
|
+
}
|
|
70
|
+
if (right.profileId === activeProfileId) {
|
|
71
|
+
return 1;
|
|
72
|
+
}
|
|
73
|
+
return right.expires - left.expires;
|
|
74
|
+
}).map((profile) => this.toProfileSummary(profile, activeProfileId));
|
|
75
|
+
}
|
|
76
|
+
async activateProfile(profileId, provider = "openai-codex") {
|
|
77
|
+
const profiles = await listProfiles();
|
|
78
|
+
const target = profiles.find((profile) => profile.profileId === profileId && profile.provider === provider);
|
|
79
|
+
if (!target) {
|
|
80
|
+
throw new Error(`\u6CA1\u6709\u627E\u5230\u53EF\u5207\u6362\u7684\u8D26\u53F7: ${profileId}`);
|
|
81
|
+
}
|
|
82
|
+
const activated = await setActiveProfile(profileId);
|
|
83
|
+
if (!activated) {
|
|
84
|
+
throw new Error(`\u5207\u6362\u8D26\u53F7\u5931\u8D25: ${profileId}`);
|
|
85
|
+
}
|
|
86
|
+
return this.toManagedProfile(activated);
|
|
87
|
+
}
|
|
88
|
+
async removeProfile(profileId, provider = "openai-codex") {
|
|
89
|
+
const profiles = await listProfiles();
|
|
90
|
+
const target = profiles.find((profile) => profile.profileId === profileId && profile.provider === provider);
|
|
91
|
+
if (!target) {
|
|
92
|
+
throw new Error(`\u6CA1\u6709\u627E\u5230\u8981\u5220\u9664\u7684\u8D26\u53F7: ${profileId}`);
|
|
93
|
+
}
|
|
94
|
+
await removeProfile(profileId);
|
|
35
95
|
}
|
|
36
96
|
async requireUsableProfile(provider = "openai-codex") {
|
|
37
97
|
const profile = await this.getActiveProfile(provider);
|
|
38
98
|
if (!profile) {
|
|
39
|
-
throw new Error(`\u8FD8\u6CA1\u6709\u767B\u5F55 ${provider}\u3002\u5148\u8FD0\u884C
|
|
99
|
+
throw new Error(`\u8FD8\u6CA1\u6709\u767B\u5F55 ${provider}\u3002\u5148\u8FD0\u884C azt login`);
|
|
40
100
|
}
|
|
41
101
|
if (Date.now() < profile.expires) {
|
|
42
102
|
return profile;
|
|
43
103
|
}
|
|
44
104
|
const refreshed = await refreshOpenAICodexToken(profile);
|
|
45
105
|
await saveProfile(refreshed);
|
|
46
|
-
return
|
|
47
|
-
...refreshed,
|
|
48
|
-
mode: "oauth_account"
|
|
49
|
-
};
|
|
106
|
+
return this.toManagedProfile(refreshed);
|
|
50
107
|
}
|
|
51
108
|
async logoutAll() {
|
|
52
109
|
await clearStore();
|
|
53
110
|
}
|
|
111
|
+
async syncActiveProfileQuota(provider = "openai-codex", options) {
|
|
112
|
+
let profile;
|
|
113
|
+
try {
|
|
114
|
+
profile = await this.requireUsableProfile(provider);
|
|
115
|
+
} catch (error) {
|
|
116
|
+
if (options?.suppressErrors) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
throw error;
|
|
120
|
+
}
|
|
121
|
+
const model = await this.configService.getDefaultModel(provider);
|
|
122
|
+
try {
|
|
123
|
+
const result = await askOpenAICodex({
|
|
124
|
+
profile,
|
|
125
|
+
model,
|
|
126
|
+
system: "Reply with OK only.",
|
|
127
|
+
prompt: "ping",
|
|
128
|
+
bodyOverride: {
|
|
129
|
+
text: { verbosity: "low" }
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
await this.updateProfileQuota(profile.profileId, result.quota, provider);
|
|
133
|
+
} catch (error) {
|
|
134
|
+
const quota = error.quota;
|
|
135
|
+
await this.updateProfileQuota(profile.profileId, quota, provider);
|
|
136
|
+
if (!options?.suppressErrors) {
|
|
137
|
+
throw error;
|
|
138
|
+
}
|
|
139
|
+
console.warn("[auth] sync active profile quota failed", {
|
|
140
|
+
provider,
|
|
141
|
+
profileId: profile.profileId,
|
|
142
|
+
error: error instanceof Error ? error.message : String(error)
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
async updateProfileQuota(profileId, quota, provider = "openai-codex") {
|
|
147
|
+
if (!quota) {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
await updateProfile(profileId, (profile) => {
|
|
151
|
+
if (profile.provider !== provider) {
|
|
152
|
+
return profile;
|
|
153
|
+
}
|
|
154
|
+
return {
|
|
155
|
+
...profile,
|
|
156
|
+
quota
|
|
157
|
+
};
|
|
158
|
+
});
|
|
159
|
+
}
|
|
54
160
|
async getStatus() {
|
|
55
|
-
const profile = await
|
|
161
|
+
const [profile, profiles] = await Promise.all([
|
|
162
|
+
this.getActiveProfile(),
|
|
163
|
+
this.listProfiles()
|
|
164
|
+
]);
|
|
56
165
|
const defaultModel = await this.configService.getDefaultModel();
|
|
57
166
|
const server = await this.configService.getServerConfig();
|
|
58
167
|
return {
|
|
@@ -62,6 +171,7 @@ class AuthService {
|
|
|
62
171
|
defaultModel,
|
|
63
172
|
loggedIn: Boolean(profile),
|
|
64
173
|
expiresAt: profile?.expires,
|
|
174
|
+
profileCount: profiles.length,
|
|
65
175
|
serverHost: server.host,
|
|
66
176
|
serverPort: server.port
|
|
67
177
|
};
|
|
@@ -70,4 +180,3 @@ class AuthService {
|
|
|
70
180
|
export {
|
|
71
181
|
AuthService
|
|
72
182
|
};
|
|
73
|
-
//# sourceMappingURL=auth-service.js.map
|
|
@@ -6,23 +6,33 @@ class ChatService {
|
|
|
6
6
|
}
|
|
7
7
|
async chat(request) {
|
|
8
8
|
const provider = request.provider ?? "openai-codex";
|
|
9
|
-
const model = await this.deps.modelService.resolveModel(provider, request.model
|
|
10
|
-
|
|
11
|
-
const result = await askOpenAICodex({
|
|
12
|
-
profile,
|
|
13
|
-
prompt: request.input,
|
|
14
|
-
model,
|
|
15
|
-
system: request.system
|
|
9
|
+
const model = await this.deps.modelService.resolveModel(provider, request.model, {
|
|
10
|
+
allowUnknown: request.experimental?.allowUnknownModel
|
|
16
11
|
});
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
12
|
+
const profile = await this.deps.authService.requireUsableProfile(provider);
|
|
13
|
+
try {
|
|
14
|
+
const result = await askOpenAICodex({
|
|
15
|
+
profile,
|
|
16
|
+
prompt: request.input,
|
|
17
|
+
model,
|
|
18
|
+
system: request.system,
|
|
19
|
+
bodyOverride: request.experimental?.codexBody
|
|
20
|
+
});
|
|
21
|
+
await this.deps.authService.updateProfileQuota(profile.profileId, result.quota, provider);
|
|
22
|
+
return {
|
|
23
|
+
provider,
|
|
24
|
+
model,
|
|
25
|
+
text: result.text,
|
|
26
|
+
raw: result.raw,
|
|
27
|
+
artifacts: result.artifacts
|
|
28
|
+
};
|
|
29
|
+
} catch (error) {
|
|
30
|
+
const quota = error.quota;
|
|
31
|
+
await this.deps.authService.updateProfileQuota(profile.profileId, quota, provider);
|
|
32
|
+
throw error;
|
|
33
|
+
}
|
|
23
34
|
}
|
|
24
35
|
}
|
|
25
36
|
export {
|
|
26
37
|
ChatService
|
|
27
38
|
};
|
|
28
|
-
//# sourceMappingURL=chat-service.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { getPreferredCodexModel, hasCodexModel } from "../models/openai-codex-models.js";
|
|
3
3
|
import {
|
|
4
4
|
createDefaultSettings,
|
|
5
5
|
loadSettings,
|
|
@@ -23,14 +23,14 @@ class ConfigService {
|
|
|
23
23
|
if (provider !== "openai-codex") {
|
|
24
24
|
throw new Error(`\u6682\u4E0D\u652F\u6301 provider: ${provider}`);
|
|
25
25
|
}
|
|
26
|
-
return
|
|
26
|
+
return await hasCodexModel(settings.defaultModel) ? settings.defaultModel : getPreferredCodexModel();
|
|
27
27
|
}
|
|
28
28
|
async setDefaultModel(model, provider = "openai-codex") {
|
|
29
29
|
if (provider !== "openai-codex") {
|
|
30
30
|
throw new Error(`\u6682\u4E0D\u652F\u6301 provider: ${provider}`);
|
|
31
31
|
}
|
|
32
|
-
if (!
|
|
33
|
-
throw new Error(`\u5F53\u524D
|
|
32
|
+
if (!await hasCodexModel(model)) {
|
|
33
|
+
throw new Error(`\u5F53\u524D\u7F51\u5173\u672A\u627E\u5230\u53EF\u7528\u6A21\u578B: ${model}`);
|
|
34
34
|
}
|
|
35
35
|
const settings = await this.getSettings();
|
|
36
36
|
const next = {
|
|
@@ -66,4 +66,3 @@ class ConfigService {
|
|
|
66
66
|
export {
|
|
67
67
|
ConfigService
|
|
68
68
|
};
|
|
69
|
-
//# sourceMappingURL=config-service.js.map
|