ai-zero-token 2.0.5 → 2.0.7
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 +24 -0
- package/README.md +20 -17
- package/README.zh-CN.md +20 -17
- package/admin-ui/dist/assets/StatCard-7TEzqn2i.js +1 -0
- package/admin-ui/dist/assets/accounts-D3tsDc3k.js +4 -0
- package/admin-ui/dist/assets/{docs-Dh0aFha_.js → docs-BO-aSEzh.js} +1 -1
- package/admin-ui/dist/assets/{image-bed-C1M7-0q1.js → image-bed-Dql7Vqd9.js} +1 -1
- package/admin-ui/dist/assets/index-C22_3Mxq.css +1 -0
- package/admin-ui/dist/assets/index-CCiBaGwU.js +10 -0
- package/admin-ui/dist/assets/{launch-pB7YlWFI.js → launch-DXLo-NIM.js} +1 -1
- package/admin-ui/dist/assets/{logs-B7McijSi.js → logs-Cwn8-rDu.js} +1 -1
- package/admin-ui/dist/assets/{network-detect-Bx3XmXPk.js → network-detect-vzWfL-Tz.js} +1 -1
- package/admin-ui/dist/assets/overview-B_yad8ge.js +1 -0
- package/admin-ui/dist/assets/{profiles-DMOjJORP.js → profiles-C5SmQvju.js} +1 -1
- package/admin-ui/dist/assets/settings-BdRWcKJb.js +5 -0
- package/admin-ui/dist/assets/{tester-BG-up8qP.js → tester-BKoMSoCz.js} +1 -1
- package/admin-ui/dist/assets/usage-B-qQxXzQ.js +1 -0
- package/admin-ui/dist/index.html +3 -3
- package/dist/cli/commands/help.js +1 -1
- package/dist/cli/commands/models.js +3 -2
- package/dist/core/context.js +4 -1
- package/dist/core/models/openai-codex-models.js +106 -1
- package/dist/core/providers/http-client.js +160 -11
- 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 +154 -10
- package/dist/core/services/chat-service.js +16 -18
- package/dist/core/services/config-service.js +9 -0
- package/dist/core/services/image-service.js +31 -1
- package/dist/core/services/model-service.js +22 -8
- 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 +1023 -69
- package/dist/server/index.js +1 -1
- package/docs/API_USAGE.md +34 -4
- package/docs/DESKTOP_RELEASE.md +12 -1
- 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
|
@@ -7,6 +7,9 @@ import { PassThrough, Readable } from "node:stream";
|
|
|
7
7
|
import { loadSettings } from "../store/settings-store.js";
|
|
8
8
|
const CURL_STATUS_MARKER = "\n__CURL_STATUS__:";
|
|
9
9
|
const CURL_HEADERS_MARKER = "\n__CURL_HEADERS__:";
|
|
10
|
+
const DEFAULT_CURL_STREAM_HEADER_TIMEOUT_MS = 18e4;
|
|
11
|
+
const MIN_CURL_STREAM_HEADER_TIMEOUT_MS = 3e4;
|
|
12
|
+
const MAX_CURL_STREAM_HEADER_TIMEOUT_MS = 6e5;
|
|
10
13
|
let requestSequence = 0;
|
|
11
14
|
function nextRequestId() {
|
|
12
15
|
requestSequence += 1;
|
|
@@ -29,6 +32,17 @@ function safeConsole(method, message, meta) {
|
|
|
29
32
|
} catch {
|
|
30
33
|
}
|
|
31
34
|
}
|
|
35
|
+
function getCurlStreamHeaderTimeoutMs(timeoutMs) {
|
|
36
|
+
if (typeof timeoutMs === "number" && Number.isFinite(timeoutMs) && timeoutMs > 0) {
|
|
37
|
+
return Math.max(1e3, timeoutMs);
|
|
38
|
+
}
|
|
39
|
+
const configured = process.env.AZT_CURL_STREAM_HEADER_TIMEOUT_MS?.trim();
|
|
40
|
+
const parsed = configured ? Number.parseInt(configured, 10) : NaN;
|
|
41
|
+
if (Number.isFinite(parsed) && parsed > 0) {
|
|
42
|
+
return Math.min(MAX_CURL_STREAM_HEADER_TIMEOUT_MS, Math.max(MIN_CURL_STREAM_HEADER_TIMEOUT_MS, parsed));
|
|
43
|
+
}
|
|
44
|
+
return DEFAULT_CURL_STREAM_HEADER_TIMEOUT_MS;
|
|
45
|
+
}
|
|
32
46
|
function logHttpTiming(params) {
|
|
33
47
|
safeConsole("info", "[http] request timing", {
|
|
34
48
|
requestId: params.requestId,
|
|
@@ -42,6 +56,60 @@ function logHttpTiming(params) {
|
|
|
42
56
|
totalMs: params.timing.totalMs
|
|
43
57
|
});
|
|
44
58
|
}
|
|
59
|
+
function createHttpTransportError(message, params) {
|
|
60
|
+
const error = new Error(message);
|
|
61
|
+
error.code = params.code;
|
|
62
|
+
error.elapsedMs = roundMs(params.elapsedMs);
|
|
63
|
+
error.isTransient = params.transient ?? true;
|
|
64
|
+
error.method = params.method;
|
|
65
|
+
error.requestId = params.requestId;
|
|
66
|
+
error.statusCode = params.statusCode;
|
|
67
|
+
error.transport = params.transport;
|
|
68
|
+
error.upstreamConnectionError = params.upstreamConnectionError ?? true;
|
|
69
|
+
error.url = params.url;
|
|
70
|
+
return error;
|
|
71
|
+
}
|
|
72
|
+
function classifyCurlRequestError(stderr, exitCode) {
|
|
73
|
+
const normalized = stderr.toLowerCase();
|
|
74
|
+
if (exitCode === 35 || normalized.includes("ssl_connect") || normalized.includes("ssl_error_syscall")) {
|
|
75
|
+
return {
|
|
76
|
+
code: "curl_request_tls_failed",
|
|
77
|
+
statusCode: 502
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
if (exitCode === 6 || normalized.includes("could not resolve host") || normalized.includes("name lookup timed out")) {
|
|
81
|
+
return {
|
|
82
|
+
code: "curl_request_dns_failed",
|
|
83
|
+
statusCode: 502
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
if (exitCode === 7 || normalized.includes("failed to connect") || normalized.includes("connection refused")) {
|
|
87
|
+
return {
|
|
88
|
+
code: "curl_request_connect_failed",
|
|
89
|
+
statusCode: 502
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
if (exitCode === 28 || normalized.includes("operation timed out") || normalized.includes("timed out")) {
|
|
93
|
+
return {
|
|
94
|
+
code: "curl_request_timeout",
|
|
95
|
+
statusCode: 504
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
code: "curl_request_failed",
|
|
100
|
+
statusCode: 502
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function isTransientHttpError(error) {
|
|
104
|
+
if (!error || typeof error !== "object") {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
const details = error;
|
|
108
|
+
if (details.isTransient === true || details.upstreamConnectionError === true) {
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
return typeof details.code === "string" && (details.code === "curl_stream_closed_before_headers" || details.code === "curl_stream_header_timeout" || details.code === "curl_stream_body_failed" || details.code === "curl_request_tls_failed" || details.code === "curl_request_dns_failed" || details.code === "curl_request_connect_failed" || details.code === "curl_request_timeout" || details.code === "curl_request_failed");
|
|
112
|
+
}
|
|
45
113
|
function isElectronRuntime() {
|
|
46
114
|
return typeof process.versions.electron === "string";
|
|
47
115
|
}
|
|
@@ -69,12 +137,7 @@ function normalizeCurlHeaders(value) {
|
|
|
69
137
|
})
|
|
70
138
|
);
|
|
71
139
|
}
|
|
72
|
-
function
|
|
73
|
-
const blocks = raw.replace(/\r\n/g, "\n").split(/\n\n+/).map((block2) => block2.trim()).filter((block2) => /^HTTP\//i.test(block2));
|
|
74
|
-
const block = blocks[blocks.length - 1];
|
|
75
|
-
if (!block) {
|
|
76
|
-
return null;
|
|
77
|
-
}
|
|
140
|
+
function parseCurlHeaderBlock(block) {
|
|
78
141
|
const lines = block.split("\n");
|
|
79
142
|
const statusMatch = /^HTTP(?:\/\S+)?\s+(\d{3})/i.exec(lines[0]?.trim() ?? "");
|
|
80
143
|
const status = statusMatch ? Number.parseInt(statusMatch[1], 10) : NaN;
|
|
@@ -96,6 +159,33 @@ function parseCurlHeaderDump(raw) {
|
|
|
96
159
|
}
|
|
97
160
|
return { status, headers };
|
|
98
161
|
}
|
|
162
|
+
function parseCurlHeaderDump(raw) {
|
|
163
|
+
const normalized = raw.replace(/\r\n/g, "\n");
|
|
164
|
+
const blocks = [];
|
|
165
|
+
let blockStart = 0;
|
|
166
|
+
while (blockStart < normalized.length) {
|
|
167
|
+
const separatorIndex = normalized.indexOf("\n\n", blockStart);
|
|
168
|
+
if (separatorIndex === -1) {
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
const block = normalized.slice(blockStart, separatorIndex).trim();
|
|
172
|
+
if (/^HTTP\//i.test(block)) {
|
|
173
|
+
blocks.push(block);
|
|
174
|
+
}
|
|
175
|
+
blockStart = separatorIndex + 2;
|
|
176
|
+
while (normalized[blockStart] === "\n") {
|
|
177
|
+
blockStart += 1;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
for (let index = blocks.length - 1; index >= 0; index -= 1) {
|
|
181
|
+
const parsed = parseCurlHeaderBlock(blocks[index]);
|
|
182
|
+
if (!parsed || parsed.status >= 100 && parsed.status < 200) {
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
return parsed;
|
|
186
|
+
}
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
99
189
|
async function runCurlRequest(init, params) {
|
|
100
190
|
const requestId = params?.requestId ?? nextRequestId();
|
|
101
191
|
const startedAt = performance.now();
|
|
@@ -152,7 +242,20 @@ async function runCurlRequest(init, params) {
|
|
|
152
242
|
});
|
|
153
243
|
phases.waitForCurlMs = performance.now() - startedAt - phases.spawnCurlMs;
|
|
154
244
|
if (exitCode !== 0) {
|
|
155
|
-
|
|
245
|
+
const stderrText = stderr.trim();
|
|
246
|
+
const classification = classifyCurlRequestError(stderrText, exitCode);
|
|
247
|
+
throw createHttpTransportError(
|
|
248
|
+
stderrText || `curl \u8BF7\u6C42\u5931\u8D25\uFF0C\u9000\u51FA\u7801 ${exitCode}\uFF08requestId=${requestId}\uFF09\u3002`,
|
|
249
|
+
{
|
|
250
|
+
code: classification.code,
|
|
251
|
+
elapsedMs: performance.now() - startedAt,
|
|
252
|
+
method: init.method,
|
|
253
|
+
requestId,
|
|
254
|
+
statusCode: classification.statusCode,
|
|
255
|
+
transport: "curl",
|
|
256
|
+
url: init.url
|
|
257
|
+
}
|
|
258
|
+
);
|
|
156
259
|
}
|
|
157
260
|
const parseStartedAt = performance.now();
|
|
158
261
|
const statusMarkerIndex = stdout.lastIndexOf(CURL_STATUS_MARKER);
|
|
@@ -210,16 +313,45 @@ async function waitForCurlHeaders(params) {
|
|
|
210
313
|
const raw = await fs.readFile(params.headerPath, "utf8");
|
|
211
314
|
const parsed = parseCurlHeaderDump(raw);
|
|
212
315
|
if (parsed) {
|
|
316
|
+
if (parsed.status >= 300 && parsed.status < 400 && !params.isClosed()) {
|
|
317
|
+
await new Promise((resolve) => setTimeout(resolve, 25));
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
213
320
|
return parsed;
|
|
214
321
|
}
|
|
215
322
|
} catch {
|
|
216
323
|
}
|
|
217
324
|
if (params.isClosed()) {
|
|
218
|
-
|
|
325
|
+
const stderr2 = params.stderr().trim();
|
|
326
|
+
const exitCode = params.exitCode();
|
|
327
|
+
throw createHttpTransportError(
|
|
328
|
+
stderr2 || `curl stream \u8BF7\u6C42\u5728\u8FD4\u56DE\u54CD\u5E94\u5934\u524D\u7ED3\u675F\uFF08requestId=${params.requestId}${exitCode ? `, exitCode=${exitCode}` : ""}\uFF09\u3002`,
|
|
329
|
+
{
|
|
330
|
+
code: "curl_stream_closed_before_headers",
|
|
331
|
+
elapsedMs: performance.now() - startedAt,
|
|
332
|
+
method: params.method,
|
|
333
|
+
requestId: params.requestId,
|
|
334
|
+
statusCode: 502,
|
|
335
|
+
transport: "curl",
|
|
336
|
+
url: params.url
|
|
337
|
+
}
|
|
338
|
+
);
|
|
219
339
|
}
|
|
220
340
|
await new Promise((resolve) => setTimeout(resolve, 25));
|
|
221
341
|
}
|
|
222
|
-
|
|
342
|
+
const stderr = params.stderr().trim();
|
|
343
|
+
throw createHttpTransportError(
|
|
344
|
+
`\u7B49\u5F85 curl stream \u54CD\u5E94\u5934\u8D85\u65F6\uFF08${Math.round(params.timeoutMs / 1e3)} \u79D2\uFF0CrequestId=${params.requestId}\uFF09\u3002${stderr ? ` ${stderr}` : ""}`,
|
|
345
|
+
{
|
|
346
|
+
code: "curl_stream_header_timeout",
|
|
347
|
+
elapsedMs: performance.now() - startedAt,
|
|
348
|
+
method: params.method,
|
|
349
|
+
requestId: params.requestId,
|
|
350
|
+
statusCode: 504,
|
|
351
|
+
transport: "curl",
|
|
352
|
+
url: params.url
|
|
353
|
+
}
|
|
354
|
+
);
|
|
223
355
|
}
|
|
224
356
|
async function runCurlStream(init, params) {
|
|
225
357
|
if (init.signal?.aborted) {
|
|
@@ -266,6 +398,7 @@ async function runCurlStream(init, params) {
|
|
|
266
398
|
child.stdin.on("error", () => void 0);
|
|
267
399
|
child.stdin.end(hasBody ? init.body : void 0);
|
|
268
400
|
const body = new PassThrough();
|
|
401
|
+
body.on("error", () => void 0);
|
|
269
402
|
const phases = {
|
|
270
403
|
spawnCurlMs: performance.now() - startedAt
|
|
271
404
|
};
|
|
@@ -292,16 +425,31 @@ async function runCurlStream(init, params) {
|
|
|
292
425
|
init.signal?.removeEventListener("abort", abort);
|
|
293
426
|
void fs.unlink(headerPath).catch(() => void 0);
|
|
294
427
|
if (exitCode !== 0 && !init.signal?.aborted) {
|
|
295
|
-
body.destroy(
|
|
428
|
+
body.destroy(createHttpTransportError(
|
|
429
|
+
stderr.trim() || `curl stream \u8BF7\u6C42\u5931\u8D25\uFF0C\u9000\u51FA\u7801 ${exitCode}\uFF08requestId=${requestId}\uFF09\u3002`,
|
|
430
|
+
{
|
|
431
|
+
code: "curl_stream_body_failed",
|
|
432
|
+
elapsedMs: performance.now() - startedAt,
|
|
433
|
+
method: init.method,
|
|
434
|
+
requestId,
|
|
435
|
+
statusCode: 502,
|
|
436
|
+
transport: "curl",
|
|
437
|
+
url: init.url
|
|
438
|
+
}
|
|
439
|
+
));
|
|
296
440
|
}
|
|
297
441
|
});
|
|
298
442
|
let parsed;
|
|
299
443
|
try {
|
|
300
444
|
parsed = await waitForCurlHeaders({
|
|
301
445
|
headerPath,
|
|
446
|
+
method: init.method,
|
|
302
447
|
isClosed: () => closed,
|
|
448
|
+
exitCode: () => exitCode,
|
|
303
449
|
stderr: () => stderr,
|
|
304
|
-
|
|
450
|
+
requestId,
|
|
451
|
+
timeoutMs: getCurlStreamHeaderTimeoutMs(params?.timeoutMs),
|
|
452
|
+
url: init.url
|
|
305
453
|
});
|
|
306
454
|
} catch (error) {
|
|
307
455
|
child.kill("SIGTERM");
|
|
@@ -461,6 +609,7 @@ async function requestStream(init) {
|
|
|
461
609
|
});
|
|
462
610
|
}
|
|
463
611
|
export {
|
|
612
|
+
isTransientHttpError,
|
|
464
613
|
requestStream,
|
|
465
614
|
requestText
|
|
466
615
|
};
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { DEFAULT_CODEX_MODEL } from "../../models/openai-codex-models.js";
|
|
3
3
|
import { requestStream, requestText } from "../http-client.js";
|
|
4
4
|
const CODEX_RESPONSES_URL = "https://chatgpt.com/backend-api/codex/responses";
|
|
5
|
+
const CODEX_RESPONSES_COMPACT_URL = `${CODEX_RESPONSES_URL}/compact`;
|
|
5
6
|
const URL_KEY_RE = /(url|uri|href|download|preview|thumbnail|image|asset|file)/i;
|
|
6
7
|
const REFERENCE_KEY_RE = /(image|asset|file|media|blob|artifact|download|preview|thumbnail)/i;
|
|
7
8
|
const REFERENCE_VALUE_RE = /^(file|asset|image|img|media|blob)-[\w-]+$/i;
|
|
@@ -428,7 +429,7 @@ async function streamOpenAICodex(params) {
|
|
|
428
429
|
}
|
|
429
430
|
const response = await requestStream({
|
|
430
431
|
method: "POST",
|
|
431
|
-
url: CODEX_RESPONSES_URL,
|
|
432
|
+
url: params.endpoint === "responses/compact" ? CODEX_RESPONSES_COMPACT_URL : CODEX_RESPONSES_URL,
|
|
432
433
|
headers: buildCodexRequestHeaders(params.profile),
|
|
433
434
|
body: JSON.stringify(requestBody),
|
|
434
435
|
signal: params.signal
|