ai-zero-token 2.0.5 → 2.0.6
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 +16 -0
- package/README.md +15 -12
- package/README.zh-CN.md +15 -12
- package/admin-ui/dist/assets/StatCard-7TEzqn2i.js +1 -0
- package/admin-ui/dist/assets/accounts-bCDKXGg9.js +4 -0
- package/admin-ui/dist/assets/{docs-Dh0aFha_.js → docs--eK_2fzC.js} +1 -1
- package/admin-ui/dist/assets/{image-bed-C1M7-0q1.js → image-bed-7wBZ1GhS.js} +1 -1
- package/admin-ui/dist/assets/index-C22_3Mxq.css +1 -0
- package/admin-ui/dist/assets/index-CdFYy5j6.js +10 -0
- package/admin-ui/dist/assets/{launch-pB7YlWFI.js → launch-BiD1Khtg.js} +1 -1
- package/admin-ui/dist/assets/{logs-B7McijSi.js → logs-BdoKDqh2.js} +1 -1
- package/admin-ui/dist/assets/{network-detect-Bx3XmXPk.js → network-detect-BvKns5nQ.js} +1 -1
- package/admin-ui/dist/assets/overview-wm6M45fu.js +1 -0
- package/admin-ui/dist/assets/settings-DOOu7Kd8.js +5 -0
- package/admin-ui/dist/assets/{tester-BG-up8qP.js → tester-NrARmlis.js} +1 -1
- package/admin-ui/dist/assets/usage-CdWRVMDV.js +1 -0
- package/admin-ui/dist/index.html +2 -2
- package/dist/core/context.js +3 -0
- package/dist/core/providers/http-client.js +21 -2
- package/dist/core/providers/openai-codex/chat.js +2 -1
- package/dist/core/providers/openai-codex/chatgpt-web-image.js +1404 -0
- package/dist/core/services/auth-service.js +51 -4
- package/dist/core/services/config-service.js +9 -0
- package/dist/core/services/image-service.js +31 -1
- package/dist/core/services/usage-service.js +349 -0
- package/dist/core/store/codex-auth-store.js +149 -15
- package/dist/core/store/settings-store.js +8 -2
- package/dist/core/store/state-paths.js +17 -1
- package/dist/server/app.js +848 -50
- package/docs/API_USAGE.md +33 -3
- package/package.json +1 -1
- package/admin-ui/dist/assets/accounts-ABMyXo4H.js +0 -4
- package/admin-ui/dist/assets/index--rNjdmzf.js +0 -10
- package/admin-ui/dist/assets/index-DjtN30PC.css +0 -1
- package/admin-ui/dist/assets/overview-CV0H2Nsq.js +0 -1
- package/admin-ui/dist/assets/settings-ynCIdUvZ.js +0 -7
|
@@ -1,17 +1,25 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { execFile } from "node:child_process";
|
|
2
3
|
import fs from "node:fs/promises";
|
|
3
4
|
import os from "node:os";
|
|
4
5
|
import path from "node:path";
|
|
6
|
+
import { promisify } from "node:util";
|
|
7
|
+
const execFileAsync = promisify(execFile);
|
|
5
8
|
function getCodexHomeDir() {
|
|
6
9
|
return process.env.CODEX_HOME || path.join(os.homedir(), ".codex");
|
|
7
10
|
}
|
|
8
|
-
const
|
|
11
|
+
const OPENAI_CODEX_PROVIDER_ID = "openai";
|
|
12
|
+
const LEGACY_CODEX_PROVIDER_ID = "ai-zero-token";
|
|
13
|
+
const DEFAULT_CODEX_PROVIDER_ID = OPENAI_CODEX_PROVIDER_ID;
|
|
9
14
|
function getCodexAuthPath() {
|
|
10
15
|
return path.join(getCodexHomeDir(), "auth.json");
|
|
11
16
|
}
|
|
12
17
|
function getCodexConfigPath() {
|
|
13
18
|
return path.join(getCodexHomeDir(), "config.toml");
|
|
14
19
|
}
|
|
20
|
+
function getCodexStateDbPath() {
|
|
21
|
+
return path.join(getCodexHomeDir(), "state_5.sqlite");
|
|
22
|
+
}
|
|
15
23
|
function createBackupSuffix() {
|
|
16
24
|
return (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
17
25
|
}
|
|
@@ -26,6 +34,65 @@ function validateProviderId(providerId) {
|
|
|
26
34
|
throw new Error("Codex providerId \u53EA\u80FD\u5305\u542B\u5B57\u6BCD\u3001\u6570\u5B57\u3001\u4E0B\u5212\u7EBF\u548C\u77ED\u6A2A\u7EBF\u3002");
|
|
27
35
|
}
|
|
28
36
|
}
|
|
37
|
+
async function fileExists(targetPath) {
|
|
38
|
+
try {
|
|
39
|
+
await fs.access(targetPath);
|
|
40
|
+
return true;
|
|
41
|
+
} catch {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function sqliteQuote(value) {
|
|
46
|
+
return `'${value.replace(/'/g, "''")}'`;
|
|
47
|
+
}
|
|
48
|
+
async function runSqlite(dbPath, sql) {
|
|
49
|
+
const { stdout } = await execFileAsync("sqlite3", [dbPath, sql], {
|
|
50
|
+
timeout: 15e3,
|
|
51
|
+
maxBuffer: 1024 * 1024
|
|
52
|
+
});
|
|
53
|
+
return stdout.trim();
|
|
54
|
+
}
|
|
55
|
+
async function migrateLegacyCodexHistoryProvider() {
|
|
56
|
+
const dbPath = getCodexStateDbPath();
|
|
57
|
+
if (!await fileExists(dbPath)) {
|
|
58
|
+
return {
|
|
59
|
+
path: dbPath,
|
|
60
|
+
migratedCount: 0,
|
|
61
|
+
skipped: true
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
const countRaw = await runSqlite(
|
|
66
|
+
dbPath,
|
|
67
|
+
`select count(*) from threads where model_provider=${sqliteQuote(LEGACY_CODEX_PROVIDER_ID)};`
|
|
68
|
+
);
|
|
69
|
+
const migratedCount = Number.parseInt(countRaw, 10);
|
|
70
|
+
if (!Number.isFinite(migratedCount) || migratedCount <= 0) {
|
|
71
|
+
return {
|
|
72
|
+
path: dbPath,
|
|
73
|
+
migratedCount: 0,
|
|
74
|
+
skipped: true
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
const backupPath = `${dbPath}.azt-backup-${createBackupSuffix()}`;
|
|
78
|
+
await runSqlite(dbPath, `.backup ${sqliteQuote(backupPath)}`);
|
|
79
|
+
await runSqlite(
|
|
80
|
+
dbPath,
|
|
81
|
+
`update threads set model_provider=${sqliteQuote(OPENAI_CODEX_PROVIDER_ID)} where model_provider=${sqliteQuote(LEGACY_CODEX_PROVIDER_ID)};`
|
|
82
|
+
);
|
|
83
|
+
return {
|
|
84
|
+
path: dbPath,
|
|
85
|
+
backupPath,
|
|
86
|
+
migratedCount
|
|
87
|
+
};
|
|
88
|
+
} catch (error) {
|
|
89
|
+
return {
|
|
90
|
+
path: dbPath,
|
|
91
|
+
migratedCount: 0,
|
|
92
|
+
error: error instanceof Error ? error.message : String(error)
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
}
|
|
29
96
|
function normalizeCodexProviderBaseUrl(value) {
|
|
30
97
|
let normalized = value.trim();
|
|
31
98
|
if (!normalized) {
|
|
@@ -79,17 +146,21 @@ function findFirstTableLine(lines) {
|
|
|
79
146
|
const index = lines.findIndex((line) => /^\s*\[/.test(line));
|
|
80
147
|
return index === -1 ? lines.length : index;
|
|
81
148
|
}
|
|
82
|
-
function
|
|
149
|
+
function parseRootString(raw, key) {
|
|
83
150
|
const lines = raw.split(/\r?\n/);
|
|
84
151
|
const firstTableLine = findFirstTableLine(lines);
|
|
152
|
+
const keyPattern = new RegExp(`^\\s*${escapeRegExp(key)}\\s*=\\s*(.+)$`);
|
|
85
153
|
for (let index = 0; index < firstTableLine; index += 1) {
|
|
86
|
-
const match =
|
|
154
|
+
const match = keyPattern.exec(lines[index] ?? "");
|
|
87
155
|
if (match) {
|
|
88
156
|
return parseTomlStringValue(match[1] ?? "");
|
|
89
157
|
}
|
|
90
158
|
}
|
|
91
159
|
return void 0;
|
|
92
160
|
}
|
|
161
|
+
function parseRootModelProvider(raw) {
|
|
162
|
+
return parseRootString(raw, "model_provider");
|
|
163
|
+
}
|
|
93
164
|
function parseGatewayProviderTable(raw, providerId) {
|
|
94
165
|
const lines = raw.split(/\r?\n/);
|
|
95
166
|
const tablePattern = new RegExp(`^\\s*\\[\\s*model_providers\\.${escapeRegExp(providerId)}\\s*\\]\\s*$`);
|
|
@@ -106,16 +177,17 @@ function parseGatewayProviderTable(raw, providerId) {
|
|
|
106
177
|
}
|
|
107
178
|
return { exists: true, baseUrl };
|
|
108
179
|
}
|
|
109
|
-
function
|
|
180
|
+
function upsertRootString(raw, key, value) {
|
|
110
181
|
if (!raw.trim()) {
|
|
111
|
-
return
|
|
182
|
+
return `${key} = ${formatTomlString(value)}
|
|
112
183
|
`;
|
|
113
184
|
}
|
|
114
185
|
const lines = raw.split(/\r?\n/);
|
|
115
186
|
const firstTableLine = findFirstTableLine(lines);
|
|
116
|
-
const nextLine =
|
|
187
|
+
const nextLine = `${key} = ${formatTomlString(value)}`;
|
|
188
|
+
const keyPattern = new RegExp(`^\\s*${escapeRegExp(key)}\\s*=`);
|
|
117
189
|
for (let index = 0; index < firstTableLine; index += 1) {
|
|
118
|
-
if (
|
|
190
|
+
if (keyPattern.test(lines[index])) {
|
|
119
191
|
lines[index] = nextLine;
|
|
120
192
|
return lines.join("\n");
|
|
121
193
|
}
|
|
@@ -123,6 +195,9 @@ function upsertRootModelProvider(raw, providerId) {
|
|
|
123
195
|
lines.splice(firstTableLine, 0, nextLine, "");
|
|
124
196
|
return lines.join("\n");
|
|
125
197
|
}
|
|
198
|
+
function upsertRootModelProvider(raw, providerId) {
|
|
199
|
+
return upsertRootString(raw, "model_provider", providerId);
|
|
200
|
+
}
|
|
126
201
|
function buildGatewayProviderBlock(providerId, baseUrl) {
|
|
127
202
|
return [
|
|
128
203
|
"# AI Zero Token managed Codex provider",
|
|
@@ -152,14 +227,27 @@ function upsertGatewayProviderTable(raw, providerId, baseUrl) {
|
|
|
152
227
|
return lines.join("\n").replace(/\s+$/g, "\n");
|
|
153
228
|
}
|
|
154
229
|
function applyGatewayProviderConfig(raw, providerId, baseUrl) {
|
|
155
|
-
|
|
230
|
+
const sanitizedRaw = removeOpenAIGatewayConfig(raw).raw;
|
|
231
|
+
return upsertGatewayProviderTable(upsertRootModelProvider(sanitizedRaw, providerId), providerId, baseUrl);
|
|
156
232
|
}
|
|
157
|
-
function
|
|
233
|
+
function applyOpenAIGatewayConfig(raw, baseUrl) {
|
|
234
|
+
const withoutLegacyProvider = removeGatewayProviderConfig(raw, LEGACY_CODEX_PROVIDER_ID).raw;
|
|
235
|
+
return upsertRootString(
|
|
236
|
+
upsertRootModelProvider(withoutLegacyProvider, OPENAI_CODEX_PROVIDER_ID),
|
|
237
|
+
"openai_base_url",
|
|
238
|
+
baseUrl
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
function removeRootString(raw, key, expectedValue) {
|
|
158
242
|
const lines = raw.split(/\r?\n/);
|
|
159
243
|
const firstTableLine = findFirstTableLine(lines);
|
|
244
|
+
const keyPattern = new RegExp(`^\\s*${escapeRegExp(key)}\\s*=\\s*(.+)$`);
|
|
160
245
|
for (let index = 0; index < firstTableLine; index += 1) {
|
|
161
|
-
const match =
|
|
162
|
-
if (!match
|
|
246
|
+
const match = keyPattern.exec(lines[index] ?? "");
|
|
247
|
+
if (!match) {
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
if (typeof expectedValue === "string" && parseTomlStringValue(match[1] ?? "") !== expectedValue) {
|
|
163
251
|
continue;
|
|
164
252
|
}
|
|
165
253
|
lines.splice(index, 1);
|
|
@@ -170,6 +258,9 @@ function removeRootModelProvider(raw, providerId) {
|
|
|
170
258
|
}
|
|
171
259
|
return { raw, removed: false };
|
|
172
260
|
}
|
|
261
|
+
function removeRootModelProvider(raw, providerId) {
|
|
262
|
+
return removeRootString(raw, "model_provider", providerId);
|
|
263
|
+
}
|
|
173
264
|
function removeGatewayProviderTable(raw, providerId) {
|
|
174
265
|
const lines = raw.split(/\r?\n/);
|
|
175
266
|
const tablePattern = new RegExp(`^\\s*\\[\\s*model_providers\\.${escapeRegExp(providerId)}\\s*\\]\\s*$`);
|
|
@@ -196,6 +287,15 @@ function removeGatewayProviderConfig(raw, providerId) {
|
|
|
196
287
|
removed: rootRemoved.removed || tableRemoved.removed
|
|
197
288
|
};
|
|
198
289
|
}
|
|
290
|
+
function removeOpenAIGatewayConfig(raw) {
|
|
291
|
+
const openAIBaseRemoved = removeRootString(raw, "openai_base_url");
|
|
292
|
+
const openAIProviderRemoved = removeRootModelProvider(openAIBaseRemoved.raw, OPENAI_CODEX_PROVIDER_ID);
|
|
293
|
+
const legacyRemoved = removeGatewayProviderConfig(openAIProviderRemoved.raw, LEGACY_CODEX_PROVIDER_ID);
|
|
294
|
+
return {
|
|
295
|
+
raw: legacyRemoved.raw,
|
|
296
|
+
removed: openAIBaseRemoved.removed || openAIProviderRemoved.removed || legacyRemoved.removed
|
|
297
|
+
};
|
|
298
|
+
}
|
|
199
299
|
function isRecord(value) {
|
|
200
300
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
201
301
|
}
|
|
@@ -209,7 +309,8 @@ async function readCodexAuth() {
|
|
|
209
309
|
}
|
|
210
310
|
}
|
|
211
311
|
async function getCodexGatewayProviderStatus(params) {
|
|
212
|
-
const
|
|
312
|
+
const requestedProviderId = params?.providerId?.trim();
|
|
313
|
+
const providerId = requestedProviderId || DEFAULT_CODEX_PROVIDER_ID;
|
|
213
314
|
validateProviderId(providerId);
|
|
214
315
|
const configPath = getCodexConfigPath();
|
|
215
316
|
let raw = "";
|
|
@@ -224,6 +325,30 @@ async function getCodexGatewayProviderStatus(params) {
|
|
|
224
325
|
};
|
|
225
326
|
}
|
|
226
327
|
const modelProvider = parseRootModelProvider(raw);
|
|
328
|
+
if (providerId === OPENAI_CODEX_PROVIDER_ID) {
|
|
329
|
+
const openAIBaseUrl = parseRootString(raw, "openai_base_url");
|
|
330
|
+
if (openAIBaseUrl && (!modelProvider || modelProvider === OPENAI_CODEX_PROVIDER_ID)) {
|
|
331
|
+
return {
|
|
332
|
+
path: configPath,
|
|
333
|
+
providerId: OPENAI_CODEX_PROVIDER_ID,
|
|
334
|
+
exists: true,
|
|
335
|
+
active: !modelProvider || modelProvider === OPENAI_CODEX_PROVIDER_ID,
|
|
336
|
+
baseUrl: openAIBaseUrl,
|
|
337
|
+
modelProvider
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
const legacyTable = parseGatewayProviderTable(raw, LEGACY_CODEX_PROVIDER_ID);
|
|
341
|
+
if (!requestedProviderId && legacyTable.exists) {
|
|
342
|
+
return {
|
|
343
|
+
path: configPath,
|
|
344
|
+
providerId: LEGACY_CODEX_PROVIDER_ID,
|
|
345
|
+
exists: true,
|
|
346
|
+
active: modelProvider === LEGACY_CODEX_PROVIDER_ID,
|
|
347
|
+
baseUrl: legacyTable.baseUrl,
|
|
348
|
+
modelProvider
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
}
|
|
227
352
|
const table = parseGatewayProviderTable(raw, providerId);
|
|
228
353
|
return {
|
|
229
354
|
path: configPath,
|
|
@@ -321,7 +446,8 @@ async function applyGatewayToCodexProviderConfig(params) {
|
|
|
321
446
|
raw = "";
|
|
322
447
|
backupPath = void 0;
|
|
323
448
|
}
|
|
324
|
-
const
|
|
449
|
+
const useOpenAIProvider = providerId === OPENAI_CODEX_PROVIDER_ID;
|
|
450
|
+
const next = useOpenAIProvider ? applyOpenAIGatewayConfig(raw, baseUrl) : applyGatewayProviderConfig(raw, providerId, baseUrl);
|
|
325
451
|
const tmpPath = `${configPath}.tmp-${process.pid}`;
|
|
326
452
|
await fs.writeFile(tmpPath, next, {
|
|
327
453
|
encoding: "utf8",
|
|
@@ -333,7 +459,8 @@ async function applyGatewayToCodexProviderConfig(params) {
|
|
|
333
459
|
path: configPath,
|
|
334
460
|
backupPath,
|
|
335
461
|
providerId,
|
|
336
|
-
baseUrl
|
|
462
|
+
baseUrl,
|
|
463
|
+
historyMigration: useOpenAIProvider ? await migrateLegacyCodexHistoryProvider() : void 0
|
|
337
464
|
};
|
|
338
465
|
}
|
|
339
466
|
async function removeGatewayFromCodexProviderConfig(params) {
|
|
@@ -350,7 +477,14 @@ async function removeGatewayFromCodexProviderConfig(params) {
|
|
|
350
477
|
removed: false
|
|
351
478
|
};
|
|
352
479
|
}
|
|
353
|
-
const
|
|
480
|
+
const sanitized = removeOpenAIGatewayConfig(raw);
|
|
481
|
+
const next = providerId === OPENAI_CODEX_PROVIDER_ID || providerId === LEGACY_CODEX_PROVIDER_ID ? sanitized : (() => {
|
|
482
|
+
const providerRemoved = removeGatewayProviderConfig(sanitized.raw, providerId);
|
|
483
|
+
return {
|
|
484
|
+
raw: providerRemoved.raw,
|
|
485
|
+
removed: sanitized.removed || providerRemoved.removed
|
|
486
|
+
};
|
|
487
|
+
})();
|
|
354
488
|
if (!next.removed) {
|
|
355
489
|
return {
|
|
356
490
|
path: configPath,
|
|
@@ -21,7 +21,10 @@ function createDefaultSettings() {
|
|
|
21
21
|
excludedProfileIds: []
|
|
22
22
|
},
|
|
23
23
|
runtime: {
|
|
24
|
-
quotaSyncConcurrency:
|
|
24
|
+
quotaSyncConcurrency: 3
|
|
25
|
+
},
|
|
26
|
+
image: {
|
|
27
|
+
freeAccountWebGenerationEnabled: false
|
|
25
28
|
},
|
|
26
29
|
server: {
|
|
27
30
|
host: "0.0.0.0",
|
|
@@ -47,6 +50,9 @@ function normalizeSettings(parsed) {
|
|
|
47
50
|
runtime: {
|
|
48
51
|
quotaSyncConcurrency: normalizeQuotaSyncConcurrency(parsed.runtime?.quotaSyncConcurrency, defaults.runtime.quotaSyncConcurrency)
|
|
49
52
|
},
|
|
53
|
+
image: {
|
|
54
|
+
freeAccountWebGenerationEnabled: parsed.image?.freeAccountWebGenerationEnabled ?? defaults.image.freeAccountWebGenerationEnabled
|
|
55
|
+
},
|
|
50
56
|
server: {
|
|
51
57
|
host: parsed.server?.host ?? defaults.server.host,
|
|
52
58
|
port: parsed.server?.port ?? defaults.server.port
|
|
@@ -63,7 +69,7 @@ async function loadSettings() {
|
|
|
63
69
|
return createDefaultSettings();
|
|
64
70
|
}
|
|
65
71
|
}
|
|
66
|
-
function normalizeQuotaSyncConcurrency(value, fallback =
|
|
72
|
+
function normalizeQuotaSyncConcurrency(value, fallback = 3) {
|
|
67
73
|
const parsed = typeof value === "number" ? value : typeof value === "string" ? Number.parseInt(value, 10) : fallback;
|
|
68
74
|
if (!Number.isFinite(parsed)) {
|
|
69
75
|
return fallback;
|
|
@@ -34,6 +34,18 @@ function getStorePath() {
|
|
|
34
34
|
function getSettingsPath() {
|
|
35
35
|
return path.join(stateDir, "settings.json");
|
|
36
36
|
}
|
|
37
|
+
function getUsageDir() {
|
|
38
|
+
return path.join(stateDir, "usage");
|
|
39
|
+
}
|
|
40
|
+
function getUsageEventsDir() {
|
|
41
|
+
return path.join(getUsageDir(), "events");
|
|
42
|
+
}
|
|
43
|
+
function getUsageDailyPath() {
|
|
44
|
+
return path.join(getUsageDir(), "daily.json");
|
|
45
|
+
}
|
|
46
|
+
function getUsageLifetimePath() {
|
|
47
|
+
return path.join(getUsageDir(), "lifetime.json");
|
|
48
|
+
}
|
|
37
49
|
async function ensureStateMigrated() {
|
|
38
50
|
if (!migrationPromise) {
|
|
39
51
|
migrationPromise = (async () => {
|
|
@@ -50,5 +62,9 @@ export {
|
|
|
50
62
|
ensureStateMigrated,
|
|
51
63
|
getSettingsPath,
|
|
52
64
|
getStateDir,
|
|
53
|
-
getStorePath
|
|
65
|
+
getStorePath,
|
|
66
|
+
getUsageDailyPath,
|
|
67
|
+
getUsageDir,
|
|
68
|
+
getUsageEventsDir,
|
|
69
|
+
getUsageLifetimePath
|
|
54
70
|
};
|