ai-zero-token 1.0.0 → 1.0.2

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 (67) hide show
  1. package/README.md +235 -58
  2. package/dist/api.js +0 -1
  3. package/dist/cli/commands/ask.js +131 -5
  4. package/dist/cli/commands/clear.js +0 -1
  5. package/dist/cli/commands/help.js +15 -10
  6. package/dist/cli/commands/login.js +0 -1
  7. package/dist/cli/commands/models.js +0 -1
  8. package/dist/cli/commands/serve.js +42 -4
  9. package/dist/cli/commands/start.js +10 -0
  10. package/dist/cli/commands/status.js +1 -1
  11. package/dist/cli/index.js +4 -1
  12. package/dist/cli/shared.js +57 -6
  13. package/dist/cli.js +0 -1
  14. package/dist/core/context.js +7 -2
  15. package/dist/core/models/openai-codex-models.js +0 -1
  16. package/dist/core/providers/http-client.js +97 -9
  17. package/dist/core/providers/openai-codex/chat.js +217 -24
  18. package/dist/core/providers/openai-codex/oauth.js +15 -4
  19. package/dist/core/providers/openai-codex/pkce.js +0 -1
  20. package/dist/core/services/auth-service.js +89 -16
  21. package/dist/core/services/chat-service.js +24 -14
  22. package/dist/core/services/config-service.js +0 -1
  23. package/dist/core/services/image-service.js +360 -0
  24. package/dist/core/services/model-service.js +4 -2
  25. package/dist/core/store/profile-store.js +79 -6
  26. package/dist/core/store/settings-store.js +1 -2
  27. package/dist/core/types.js +0 -1
  28. package/dist/http.js +0 -1
  29. package/dist/models.js +0 -1
  30. package/dist/oauth.js +0 -1
  31. package/dist/pkce.js +0 -1
  32. package/dist/server/admin-page.js +2615 -0
  33. package/dist/server/app.js +566 -39
  34. package/dist/server/index.js +13 -3
  35. package/dist/store.js +0 -1
  36. package/package.json +14 -6
  37. package/dist/api.js.map +0 -1
  38. package/dist/cli/commands/ask.js.map +0 -1
  39. package/dist/cli/commands/clear.js.map +0 -1
  40. package/dist/cli/commands/help.js.map +0 -1
  41. package/dist/cli/commands/login.js.map +0 -1
  42. package/dist/cli/commands/models.js.map +0 -1
  43. package/dist/cli/commands/serve.js.map +0 -1
  44. package/dist/cli/commands/status.js.map +0 -1
  45. package/dist/cli/index.js.map +0 -1
  46. package/dist/cli/shared.js.map +0 -1
  47. package/dist/cli.js.map +0 -1
  48. package/dist/core/context.js.map +0 -1
  49. package/dist/core/models/openai-codex-models.js.map +0 -1
  50. package/dist/core/providers/http-client.js.map +0 -1
  51. package/dist/core/providers/openai-codex/chat.js.map +0 -1
  52. package/dist/core/providers/openai-codex/oauth.js.map +0 -1
  53. package/dist/core/providers/openai-codex/pkce.js.map +0 -1
  54. package/dist/core/services/auth-service.js.map +0 -1
  55. package/dist/core/services/chat-service.js.map +0 -1
  56. package/dist/core/services/config-service.js.map +0 -1
  57. package/dist/core/services/model-service.js.map +0 -1
  58. package/dist/core/store/profile-store.js.map +0 -1
  59. package/dist/core/store/settings-store.js.map +0 -1
  60. package/dist/core/types.js.map +0 -1
  61. package/dist/http.js.map +0 -1
  62. package/dist/models.js.map +0 -1
  63. package/dist/oauth.js.map +0 -1
  64. package/dist/pkce.js.map +0 -1
  65. package/dist/server/app.js.map +0 -1
  66. package/dist/server/index.js.map +0 -1
  67. package/dist/store.js.map +0 -1
@@ -2,17 +2,55 @@
2
2
  import { createGatewayContext } from "../../core/context.js";
3
3
  import { startServer } from "../../server/index.js";
4
4
  import { parseServeArgs } from "../shared.js";
5
- async function runServeCommand(args) {
6
- const { host, port } = parseServeArgs(args);
5
+ import { spawn } from "node:child_process";
6
+ function createBrowserUrl(host, port) {
7
+ if (host === "0.0.0.0" || host === "::") {
8
+ return `http://127.0.0.1:${port}`;
9
+ }
10
+ return `http://${host}:${port}`;
11
+ }
12
+ function createListenUrl(host, port) {
13
+ return `http://${host}:${port}`;
14
+ }
15
+ function tryOpenBrowser(url) {
16
+ try {
17
+ if (process.platform === "darwin") {
18
+ const child2 = spawn("open", [url], { stdio: "ignore", detached: true });
19
+ child2.unref();
20
+ return true;
21
+ }
22
+ if (process.platform === "win32") {
23
+ const child2 = spawn("cmd", ["/c", "start", "", url], { stdio: "ignore", detached: true });
24
+ child2.unref();
25
+ return true;
26
+ }
27
+ const child = spawn("xdg-open", [url], { stdio: "ignore", detached: true });
28
+ child.unref();
29
+ return true;
30
+ } catch {
31
+ return false;
32
+ }
33
+ }
34
+ async function runServeCommand(args, options) {
35
+ const { host, port, openBrowser } = parseServeArgs(args);
7
36
  const ctx = createGatewayContext();
8
37
  const status = await ctx.authService.getStatus();
9
38
  const server = await startServer({ host, port });
39
+ const adminUrl = createBrowserUrl(server.host, server.port);
40
+ const listenUrl = createListenUrl(server.host, server.port);
41
+ const shouldOpenBrowser = openBrowser ?? options?.openBrowserByDefault ?? false;
10
42
  console.log("\u672C\u5730\u7F51\u5173\u5DF2\u542F\u52A8\u3002");
11
- console.log(`url: http://${server.host}:${server.port}`);
43
+ console.log(`url: ${listenUrl}`);
44
+ console.log(`admin: ${adminUrl}`);
45
+ console.log(`apiBase: ${adminUrl}/v1`);
46
+ console.log(`corsOrigin: ${server.corsOrigin}`);
12
47
  console.log(`activeProvider: ${status.activeProvider ?? "none"}`);
13
48
  console.log(`defaultModel: ${status.defaultModel}`);
49
+ if (shouldOpenBrowser) {
50
+ const opened = tryOpenBrowser(adminUrl);
51
+ console.log(opened ? "\u5DF2\u5C1D\u8BD5\u6253\u5F00\u7BA1\u7406\u9875\u9762\u3002" : "\u672A\u80FD\u81EA\u52A8\u6253\u5F00\u7BA1\u7406\u9875\u9762\uFF0C\u8BF7\u624B\u52A8\u8BBF\u95EE\u4E0A\u9762\u7684 admin \u5730\u5740\u3002");
52
+ }
14
53
  }
15
54
  export {
16
55
  runServeCommand
17
56
  };
18
- //# sourceMappingURL=serve.js.map
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ import { runServeCommand } from "./serve.js";
3
+ async function runStartCommand(args) {
4
+ await runServeCommand(args, {
5
+ openBrowserByDefault: true
6
+ });
7
+ }
8
+ export {
9
+ runStartCommand
10
+ };
@@ -15,6 +15,7 @@ async function runStatusCommand() {
15
15
  console.log("\u5F53\u524D\u767B\u5F55\u72B6\u6001:");
16
16
  console.log(`provider: ${status.activeProvider}`);
17
17
  console.log(`profileId: ${status.activeProfileId}`);
18
+ console.log(`profileCount: ${status.profileCount}`);
18
19
  console.log(`defaultModel: ${status.defaultModel}`);
19
20
  console.log(`serverHost: ${status.serverHost}`);
20
21
  console.log(`serverPort: ${status.serverPort}`);
@@ -29,4 +30,3 @@ async function runStatusCommand() {
29
30
  export {
30
31
  runStatusCommand
31
32
  };
32
- //# sourceMappingURL=status.js.map
package/dist/cli/index.js CHANGED
@@ -5,6 +5,7 @@ import { printHelp } from "./commands/help.js";
5
5
  import { runLoginCommand } from "./commands/login.js";
6
6
  import { runModelsCommand } from "./commands/models.js";
7
7
  import { runServeCommand } from "./commands/serve.js";
8
+ import { runStartCommand } from "./commands/start.js";
8
9
  import { runStatusCommand } from "./commands/status.js";
9
10
  async function runCli(argv = process.argv.slice(2)) {
10
11
  const [command, ...rest] = argv;
@@ -24,6 +25,9 @@ async function runCli(argv = process.argv.slice(2)) {
24
25
  case "serve":
25
26
  await runServeCommand(rest);
26
27
  return;
28
+ case "start":
29
+ await runStartCommand(rest);
30
+ return;
27
31
  case "clear":
28
32
  await runClearCommand();
29
33
  return;
@@ -40,4 +44,3 @@ async function runCli(argv = process.argv.slice(2)) {
40
44
  export {
41
45
  runCli
42
46
  };
43
- //# sourceMappingURL=index.js.map
@@ -7,16 +7,55 @@ function formatExpiry(expires) {
7
7
  function parseAskArgs(args) {
8
8
  const rest = [...args];
9
9
  let model;
10
+ let payloadFile;
11
+ let dumpRawFile;
12
+ let writeArtifactsDir;
13
+ let printRaw = false;
14
+ let allowUnknownModel = false;
10
15
  for (let index = 0; index < rest.length; index += 1) {
11
- if (rest[index] !== "--model") {
16
+ if (rest[index] === "--model") {
17
+ model = rest[index + 1];
18
+ rest.splice(index, 2);
19
+ index -= 1;
12
20
  continue;
13
21
  }
14
- model = rest[index + 1];
15
- rest.splice(index, 2);
16
- break;
22
+ if (rest[index] === "--payload-file") {
23
+ payloadFile = rest[index + 1];
24
+ rest.splice(index, 2);
25
+ index -= 1;
26
+ continue;
27
+ }
28
+ if (rest[index] === "--dump-raw") {
29
+ dumpRawFile = rest[index + 1];
30
+ rest.splice(index, 2);
31
+ index -= 1;
32
+ continue;
33
+ }
34
+ if (rest[index] === "--write-artifacts-dir") {
35
+ writeArtifactsDir = rest[index + 1];
36
+ rest.splice(index, 2);
37
+ index -= 1;
38
+ continue;
39
+ }
40
+ if (rest[index] === "--print-raw") {
41
+ printRaw = true;
42
+ rest.splice(index, 1);
43
+ index -= 1;
44
+ continue;
45
+ }
46
+ if (rest[index] === "--allow-unknown-model") {
47
+ allowUnknownModel = true;
48
+ rest.splice(index, 1);
49
+ index -= 1;
50
+ }
17
51
  }
18
52
  return {
19
53
  model,
54
+ payloadFile,
55
+ dumpRawFile,
56
+ writeArtifactsDir,
57
+ printRaw,
58
+ allowUnknownModel,
20
59
  prompt: rest.join(" ").trim()
21
60
  };
22
61
  }
@@ -24,6 +63,7 @@ function parseServeArgs(args) {
24
63
  const rest = [...args];
25
64
  let host;
26
65
  let port;
66
+ let openBrowser;
27
67
  for (let index = 0; index < rest.length; index += 1) {
28
68
  if (rest[index] === "--host") {
29
69
  host = rest[index + 1];
@@ -38,13 +78,24 @@ function parseServeArgs(args) {
38
78
  }
39
79
  rest.splice(index, 2);
40
80
  index -= 1;
81
+ continue;
82
+ }
83
+ if (rest[index] === "--open") {
84
+ openBrowser = true;
85
+ rest.splice(index, 1);
86
+ index -= 1;
87
+ continue;
88
+ }
89
+ if (rest[index] === "--no-open") {
90
+ openBrowser = false;
91
+ rest.splice(index, 1);
92
+ index -= 1;
41
93
  }
42
94
  }
43
- return { host, port };
95
+ return { host, port, openBrowser };
44
96
  }
45
97
  export {
46
98
  formatExpiry,
47
99
  parseAskArgs,
48
100
  parseServeArgs
49
101
  };
50
- //# sourceMappingURL=shared.js.map
package/dist/cli.js CHANGED
@@ -5,4 +5,3 @@ runCli().catch((error) => {
5
5
  console.error(`\u9519\u8BEF: ${message}`);
6
6
  process.exitCode = 1;
7
7
  });
8
- //# sourceMappingURL=cli.js.map
@@ -2,6 +2,7 @@
2
2
  import { ConfigService } from "./services/config-service.js";
3
3
  import { AuthService } from "./services/auth-service.js";
4
4
  import { ChatService } from "./services/chat-service.js";
5
+ import { ImageService } from "./services/image-service.js";
5
6
  import { ModelService } from "./services/model-service.js";
6
7
  function createGatewayContext() {
7
8
  const configService = new ConfigService();
@@ -11,14 +12,18 @@ function createGatewayContext() {
11
12
  authService,
12
13
  modelService
13
14
  });
15
+ const imageService = new ImageService({
16
+ authService,
17
+ configService
18
+ });
14
19
  return {
15
20
  configService,
16
21
  authService,
17
22
  modelService,
18
- chatService
23
+ chatService,
24
+ imageService
19
25
  };
20
26
  }
21
27
  export {
22
28
  createGatewayContext
23
29
  };
24
- //# sourceMappingURL=context.js.map
@@ -31,4 +31,3 @@ export {
31
31
  SUPPORTED_CODEX_MODELS,
32
32
  isSupportedCodexModel
33
33
  };
34
- //# sourceMappingURL=openai-codex-models.js.map
@@ -1,7 +1,45 @@
1
1
  #!/usr/bin/env node
2
2
  import { spawn } from "node:child_process";
3
3
  const CURL_STATUS_MARKER = "\n__CURL_STATUS__:";
4
- async function runCurlRequest(init) {
4
+ let requestSequence = 0;
5
+ function nextRequestId() {
6
+ requestSequence += 1;
7
+ return `http-${String(requestSequence).padStart(4, "0")}`;
8
+ }
9
+ function roundMs(value) {
10
+ return Math.round(value * 100) / 100;
11
+ }
12
+ function finalizeTiming(startedAt, phases) {
13
+ return {
14
+ phasesMs: Object.fromEntries(
15
+ Object.entries(phases).map(([key, value]) => [key, roundMs(value)])
16
+ ),
17
+ totalMs: roundMs(performance.now() - startedAt)
18
+ };
19
+ }
20
+ function logHttpTiming(params) {
21
+ console.info("[http] request timing", {
22
+ requestId: params.requestId,
23
+ method: params.method,
24
+ url: params.url,
25
+ transport: params.transport,
26
+ status: params.status,
27
+ bodyLength: params.bodyLength,
28
+ fallbackFrom: params.fallbackFrom,
29
+ phasesMs: params.timing.phasesMs,
30
+ totalMs: params.timing.totalMs
31
+ });
32
+ }
33
+ function normalizeHeaders(headers) {
34
+ const normalized = {};
35
+ headers.forEach((value, key) => {
36
+ normalized[key.toLowerCase()] = value;
37
+ });
38
+ return normalized;
39
+ }
40
+ async function runCurlRequest(init, params) {
41
+ const requestId = params?.requestId ?? nextRequestId();
42
+ const startedAt = performance.now();
5
43
  const args = [
6
44
  "--silent",
7
45
  "--show-error",
@@ -22,6 +60,9 @@ async function runCurlRequest(init) {
22
60
  env: process.env,
23
61
  stdio: ["ignore", "pipe", "pipe"]
24
62
  });
63
+ const phases = {
64
+ spawnCurlMs: performance.now() - startedAt
65
+ };
25
66
  let stdout = "";
26
67
  let stderr = "";
27
68
  child.stdout.setEncoding("utf8");
@@ -36,9 +77,11 @@ async function runCurlRequest(init) {
36
77
  child.on("error", reject);
37
78
  child.on("close", (code) => resolve(code ?? 1));
38
79
  });
80
+ phases.waitForCurlMs = performance.now() - startedAt - phases.spawnCurlMs;
39
81
  if (exitCode !== 0) {
40
82
  throw new Error(stderr.trim() || `curl \u8BF7\u6C42\u5931\u8D25\uFF0C\u9000\u51FA\u7801 ${exitCode}`);
41
83
  }
84
+ const parseStartedAt = performance.now();
42
85
  const markerIndex = stdout.lastIndexOf(CURL_STATUS_MARKER);
43
86
  if (markerIndex === -1) {
44
87
  throw new Error("curl \u54CD\u5E94\u7F3A\u5C11\u72B6\u6001\u7801\u6807\u8BB0\u3002");
@@ -49,36 +92,81 @@ async function runCurlRequest(init) {
49
92
  if (!Number.isFinite(status)) {
50
93
  throw new Error(`\u65E0\u6CD5\u89E3\u6790 curl \u72B6\u6001\u7801: ${statusText}`);
51
94
  }
95
+ phases.parseResponseMs = performance.now() - parseStartedAt;
96
+ const timing = finalizeTiming(startedAt, phases);
97
+ logHttpTiming({
98
+ requestId,
99
+ method: init.method,
100
+ url: init.url,
101
+ status,
102
+ transport: "curl",
103
+ timing,
104
+ bodyLength: body.length,
105
+ fallbackFrom: params?.fallbackFrom
106
+ });
52
107
  return {
53
108
  body,
54
109
  status,
55
- transport: "curl"
110
+ transport: "curl",
111
+ timing,
112
+ requestId,
113
+ headers: {}
56
114
  };
57
115
  }
58
116
  async function requestText(init) {
117
+ const requestId = nextRequestId();
59
118
  const useCurlOnly = process.env.OAUTH_DEMO_USE_CURL === "1";
60
- const timeoutMs = init.timeoutMs ?? 2e4;
119
+ const timeoutMs = init.timeoutMs;
120
+ const signal = typeof timeoutMs === "number" && Number.isFinite(timeoutMs) && timeoutMs > 0 ? AbortSignal.timeout(timeoutMs) : void 0;
61
121
  if (!useCurlOnly) {
122
+ const startedAt = performance.now();
123
+ const phases = {};
62
124
  try {
125
+ const fetchStartedAt = performance.now();
63
126
  const response = await fetch(init.url, {
64
127
  method: init.method,
65
128
  headers: init.headers,
66
129
  body: init.body,
67
- signal: AbortSignal.timeout(timeoutMs)
130
+ signal
131
+ });
132
+ phases.waitForHeadersMs = performance.now() - fetchStartedAt;
133
+ const readBodyStartedAt = performance.now();
134
+ const body = await response.text();
135
+ phases.readBodyMs = performance.now() - readBodyStartedAt;
136
+ const timing = finalizeTiming(startedAt, phases);
137
+ logHttpTiming({
138
+ requestId,
139
+ method: init.method,
140
+ url: init.url,
141
+ status: response.status,
142
+ transport: "fetch",
143
+ timing,
144
+ bodyLength: body.length
68
145
  });
69
146
  return {
70
- body: await response.text(),
147
+ body,
71
148
  status: response.status,
72
- transport: "fetch"
149
+ transport: "fetch",
150
+ timing,
151
+ requestId,
152
+ headers: normalizeHeaders(response.headers)
73
153
  };
74
154
  } catch (error) {
75
155
  const message = error instanceof Error ? error.message : String(error);
76
- console.log(`fetch \u8BF7\u6C42\u5931\u8D25\uFF0C\u51C6\u5907\u56DE\u9000\u5230 curl: ${message}`);
156
+ console.warn("[http] fetch attempt failed", {
157
+ requestId,
158
+ method: init.method,
159
+ url: init.url,
160
+ elapsedMs: roundMs(performance.now() - startedAt),
161
+ error: message
162
+ });
77
163
  }
78
164
  }
79
- return runCurlRequest(init);
165
+ return runCurlRequest(init, {
166
+ requestId,
167
+ fallbackFrom: useCurlOnly ? void 0 : "fetch"
168
+ });
80
169
  }
81
170
  export {
82
171
  requestText
83
172
  };
84
- //# sourceMappingURL=http-client.js.map
@@ -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 extractCodexText(body) {
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 (event.type === "response.completed" || event.type === "response.done" || event.type === "response.incomplete") {
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 { text: completedText, raw: responsePayload ?? events };
239
+ return {
240
+ text: completedText,
241
+ raw: {
242
+ request: requestBody,
243
+ response: responsePayload ?? null,
244
+ events
245
+ },
246
+ artifacts
247
+ };
61
248
  }
62
- return { text: accumulated.trim(), raw: responsePayload ?? events };
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
- throw new Error(`\u8C03\u7528 Responses API \u5931\u8D25: HTTP ${response.status} via ${response.transport} ${response.body}`);
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 extractCodexText(response.body);
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