ai-zero-token 2.0.4 → 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.
Files changed (37) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +23 -5
  3. package/README.zh-CN.md +24 -6
  4. package/admin-ui/dist/assets/StatCard-7TEzqn2i.js +1 -0
  5. package/admin-ui/dist/assets/accounts-bCDKXGg9.js +4 -0
  6. package/admin-ui/dist/assets/{docs-oNIugCIL.js → docs--eK_2fzC.js} +1 -1
  7. package/admin-ui/dist/assets/{image-bed-CQtIhjg_.js → image-bed-7wBZ1GhS.js} +1 -1
  8. package/admin-ui/dist/assets/index-C22_3Mxq.css +1 -0
  9. package/admin-ui/dist/assets/index-CdFYy5j6.js +10 -0
  10. package/admin-ui/dist/assets/{launch-B-2Zdz9m.js → launch-BiD1Khtg.js} +1 -1
  11. package/admin-ui/dist/assets/{logs-JFuSf56b.js → logs-BdoKDqh2.js} +1 -1
  12. package/admin-ui/dist/assets/{network-detect-SfvK6uhx.js → network-detect-BvKns5nQ.js} +1 -1
  13. package/admin-ui/dist/assets/overview-wm6M45fu.js +1 -0
  14. package/admin-ui/dist/assets/settings-DOOu7Kd8.js +5 -0
  15. package/admin-ui/dist/assets/{tester-ocpF053C.js → tester-NrARmlis.js} +1 -1
  16. package/admin-ui/dist/assets/usage-CdWRVMDV.js +1 -0
  17. package/admin-ui/dist/index.html +2 -2
  18. package/dist/core/context.js +3 -0
  19. package/dist/core/providers/http-client.js +247 -3
  20. package/dist/core/providers/openai-codex/chat.js +84 -23
  21. package/dist/core/providers/openai-codex/chatgpt-web-image.js +1404 -0
  22. package/dist/core/services/auth-service.js +64 -8
  23. package/dist/core/services/config-service.js +24 -5
  24. package/dist/core/services/image-service.js +31 -1
  25. package/dist/core/services/usage-service.js +349 -0
  26. package/dist/core/store/codex-auth-store.js +429 -4
  27. package/dist/core/store/settings-store.js +62 -26
  28. package/dist/core/store/state-paths.js +17 -1
  29. package/dist/server/app.js +1278 -119
  30. package/docs/API_USAGE.md +48 -1
  31. package/docs/DESKTOP_RELEASE.md +12 -1
  32. package/package.json +1 -1
  33. package/admin-ui/dist/assets/accounts-CTjk9c4F.js +0 -4
  34. package/admin-ui/dist/assets/index-By4r-wy3.css +0 -1
  35. package/admin-ui/dist/assets/index-rgcJgVAu.js +0 -10
  36. package/admin-ui/dist/assets/overview-X_WodIqE.js +0 -1
  37. package/admin-ui/dist/assets/settings-0eXUAvcm.js +0 -1
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import { DEFAULT_CODEX_MODEL } from "../../models/openai-codex-models.js";
3
- import { requestText } from "../http-client.js";
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;
@@ -39,21 +40,65 @@ function parseOptionalText(value) {
39
40
  function parseUpstreamErrorBody(body) {
40
41
  try {
41
42
  const parsed = JSON.parse(body);
42
- const error = parsed.error;
43
- if (!error || typeof error !== "object") {
43
+ const record = asRecord(parsed.error);
44
+ if (!record) {
44
45
  return void 0;
45
46
  }
46
- const record = error;
47
+ const eligiblePromo = asRecord(record.eligible_promo);
47
48
  return {
48
49
  message: typeof record.message === "string" ? record.message : void 0,
49
50
  type: typeof record.type === "string" ? record.type : void 0,
50
51
  code: typeof record.code === "string" ? record.code : void 0,
51
- param: typeof record.param === "string" || record.param === null ? record.param : void 0
52
+ param: typeof record.param === "string" || record.param === null ? record.param : void 0,
53
+ planType: typeof record.plan_type === "string" ? record.plan_type : void 0,
54
+ resetsAt: typeof record.resets_at === "number" && Number.isFinite(record.resets_at) ? record.resets_at : void 0,
55
+ resetsInSeconds: typeof record.resets_in_seconds === "number" && Number.isFinite(record.resets_in_seconds) ? record.resets_in_seconds : void 0,
56
+ promoCampaignId: typeof eligiblePromo?.campaign_id === "string" ? eligiblePromo.campaign_id : void 0,
57
+ promoMessage: typeof eligiblePromo?.message === "string" ? eligiblePromo.message : void 0
52
58
  };
53
59
  } catch {
54
60
  return void 0;
55
61
  }
56
62
  }
63
+ function buildCodexRequestHeaders(profile) {
64
+ return {
65
+ Accept: "text/event-stream",
66
+ "Content-Type": "application/json",
67
+ Authorization: `Bearer ${profile.access}`,
68
+ "ChatGPT-Account-Id": profile.accountId,
69
+ "OpenAI-Beta": "responses=experimental",
70
+ Originator: "pi",
71
+ "User-Agent": "pi (bun demo)"
72
+ };
73
+ }
74
+ function createCodexUpstreamError(status, body, transport, quota, requestId) {
75
+ const upstreamError = parseUpstreamErrorBody(body);
76
+ const error = new Error(`\u8C03\u7528 Responses API \u5931\u8D25: HTTP ${status} via ${transport} ${body}`);
77
+ error.quota = quota ?? createUsageLimitQuotaSnapshot(status, upstreamError, requestId);
78
+ error.upstreamStatus = status;
79
+ error.upstreamErrorCode = upstreamError?.code;
80
+ error.upstreamErrorType = upstreamError?.type;
81
+ error.upstreamErrorMessage = upstreamError?.message;
82
+ return error;
83
+ }
84
+ function createUsageLimitQuotaSnapshot(status, upstreamError, requestId) {
85
+ const errorKind = `${upstreamError?.type ?? ""} ${upstreamError?.code ?? ""}`.toLowerCase();
86
+ if (status !== 429 && !errorKind.includes("usage_limit_reached")) {
87
+ return void 0;
88
+ }
89
+ return {
90
+ capturedAt: Date.now(),
91
+ sourceRequestId: requestId,
92
+ planType: upstreamError?.planType,
93
+ primaryUsedPercent: 100,
94
+ primaryResetAt: upstreamError?.resetsAt,
95
+ primaryResetAfterSeconds: upstreamError?.resetsInSeconds,
96
+ creditsHasCredits: false,
97
+ creditsBalance: "0",
98
+ promoCampaignId: upstreamError?.promoCampaignId,
99
+ promoMessage: upstreamError?.promoMessage
100
+ };
101
+ }
57
102
  function extractCodexQuotaSnapshot(headers, requestId) {
58
103
  const activeLimit = parseOptionalText(headers["x-codex-active-limit"]);
59
104
  const planType = parseOptionalText(headers["x-codex-plan-type"]);
@@ -362,34 +407,50 @@ async function askOpenAICodex(params) {
362
407
  const response = await requestText({
363
408
  method: "POST",
364
409
  url: CODEX_RESPONSES_URL,
365
- headers: {
366
- Accept: "text/event-stream",
367
- "Content-Type": "application/json",
368
- Authorization: `Bearer ${params.profile.access}`,
369
- "ChatGPT-Account-Id": params.profile.accountId,
370
- "OpenAI-Beta": "responses=experimental",
371
- Originator: "pi",
372
- "User-Agent": "pi (bun demo)"
373
- },
410
+ headers: buildCodexRequestHeaders(params.profile),
374
411
  body: JSON.stringify(requestBody)
375
412
  });
376
413
  const quota = extractCodexQuotaSnapshot(response.headers, response.requestId);
377
414
  if (response.status < 200 || response.status >= 300) {
378
- const upstreamError = parseUpstreamErrorBody(response.body);
379
- const error = new Error(`\u8C03\u7528 Responses API \u5931\u8D25: HTTP ${response.status} via ${response.transport} ${response.body}`);
380
- error.quota = quota;
381
- error.upstreamStatus = response.status;
382
- error.upstreamErrorCode = upstreamError?.code;
383
- error.upstreamErrorType = upstreamError?.type;
384
- error.upstreamErrorMessage = upstreamError?.message;
385
- throw error;
415
+ throw createCodexUpstreamError(response.status, response.body, response.transport, quota, response.requestId);
386
416
  }
387
417
  return {
388
418
  ...extractCodexText(response.body, requestBody),
389
419
  quota
390
420
  };
391
421
  }
422
+ async function streamOpenAICodex(params) {
423
+ const requestBody = params.passthroughBody ? { ...params.bodyOverride ?? {} } : {
424
+ ...buildDefaultRequestBody(params),
425
+ ...params.bodyOverride ?? {}
426
+ };
427
+ if (!params.passthroughBody && typeof requestBody.input === "undefined") {
428
+ 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");
429
+ }
430
+ const response = await requestStream({
431
+ method: "POST",
432
+ url: params.endpoint === "responses/compact" ? CODEX_RESPONSES_COMPACT_URL : CODEX_RESPONSES_URL,
433
+ headers: buildCodexRequestHeaders(params.profile),
434
+ body: JSON.stringify(requestBody),
435
+ signal: params.signal
436
+ });
437
+ const headers = response.headers;
438
+ const requestId = headers["x-request-id"] ?? response.requestId;
439
+ const quota = extractCodexQuotaSnapshot(headers, requestId);
440
+ if (response.status < 200 || response.status >= 300) {
441
+ const body = await new Response(response.body).text();
442
+ throw createCodexUpstreamError(response.status, body, response.transport, quota, requestId);
443
+ }
444
+ return {
445
+ body: response.body,
446
+ headers,
447
+ quota,
448
+ requestId,
449
+ status: response.status
450
+ };
451
+ }
392
452
  export {
393
453
  askOpenAICodex,
394
- extractCodexQuotaSnapshot
454
+ extractCodexQuotaSnapshot,
455
+ streamOpenAICodex
395
456
  };