@mastra/code-sdk 1.0.3-alpha.0 → 1.1.0-alpha.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 (258) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/dist/acp/agent.js +155 -167
  3. package/dist/acp/agent.js.map +1 -1
  4. package/dist/acp/event-mapper.js +193 -168
  5. package/dist/acp/event-mapper.js.map +1 -1
  6. package/dist/acp/index.js +60 -49
  7. package/dist/acp/index.js.map +1 -1
  8. package/dist/acp/server.js +31 -30
  9. package/dist/acp/server.js.map +1 -1
  10. package/dist/acp.js +1 -4
  11. package/dist/agents/credential-resolver.js +40 -29
  12. package/dist/agents/credential-resolver.js.map +1 -1
  13. package/dist/agents/custom-provider-source.js +16 -9
  14. package/dist/agents/custom-provider-source.js.map +1 -1
  15. package/dist/agents/instructions.js +27 -36
  16. package/dist/agents/instructions.js.map +1 -1
  17. package/dist/agents/mastracode-gateway.js +355 -428
  18. package/dist/agents/mastracode-gateway.js.map +1 -1
  19. package/dist/agents/memory.js +87 -78
  20. package/dist/agents/memory.js.map +1 -1
  21. package/dist/agents/model.js +114 -119
  22. package/dist/agents/model.js.map +1 -1
  23. package/dist/agents/modes/build.js +12 -13
  24. package/dist/agents/modes/build.js.map +1 -1
  25. package/dist/agents/modes/explore.js +14 -13
  26. package/dist/agents/modes/explore.js.map +1 -1
  27. package/dist/agents/modes/plan.js +14 -15
  28. package/dist/agents/modes/plan.js.map +1 -1
  29. package/dist/agents/prompts/agent-instructions.js +78 -69
  30. package/dist/agents/prompts/agent-instructions.js.map +1 -1
  31. package/dist/agents/prompts/build.js +19 -16
  32. package/dist/agents/prompts/build.js.map +1 -1
  33. package/dist/agents/prompts/fast.js +8 -4
  34. package/dist/agents/prompts/fast.js.map +1 -1
  35. package/dist/agents/prompts/index.js +48 -47
  36. package/dist/agents/prompts/index.js.map +1 -1
  37. package/dist/agents/prompts/model.js +6 -5
  38. package/dist/agents/prompts/model.js.map +1 -1
  39. package/dist/agents/prompts/plan.js +20 -16
  40. package/dist/agents/prompts/plan.js.map +1 -1
  41. package/dist/agents/prompts/tool-guidance.js +77 -109
  42. package/dist/agents/prompts/tool-guidance.js.map +1 -1
  43. package/dist/agents/sandbox-filesystem.js +245 -259
  44. package/dist/agents/sandbox-filesystem.js.map +1 -1
  45. package/dist/agents/sandbox-reattach.js +9 -11
  46. package/dist/agents/sandbox-reattach.js.map +1 -1
  47. package/dist/agents/thread-caveman-state.js +92 -81
  48. package/dist/agents/thread-caveman-state.js.map +1 -1
  49. package/dist/agents/tool-availability.js +62 -80
  50. package/dist/agents/tool-availability.js.map +1 -1
  51. package/dist/agents/tools.js +99 -139
  52. package/dist/agents/tools.js.map +1 -1
  53. package/dist/agents/workspace.js +232 -225
  54. package/dist/agents/workspace.js.map +1 -1
  55. package/dist/analytics.js +139 -128
  56. package/dist/analytics.js.map +1 -1
  57. package/dist/auth/authorization-input.js +39 -25
  58. package/dist/auth/authorization-input.js.map +1 -1
  59. package/dist/auth/device-code.js +112 -80
  60. package/dist/auth/device-code.js.map +1 -1
  61. package/dist/auth/index.js +2 -9
  62. package/dist/auth/pkce.js +27 -16
  63. package/dist/auth/pkce.js.map +1 -1
  64. package/dist/auth/providers/anthropic.js +107 -93
  65. package/dist/auth/providers/anthropic.js.map +1 -1
  66. package/dist/auth/providers/github-copilot.js +304 -293
  67. package/dist/auth/providers/github-copilot.js.map +1 -1
  68. package/dist/auth/providers/openai-codex.js +442 -460
  69. package/dist/auth/providers/openai-codex.js.map +1 -1
  70. package/dist/auth/providers/xai.js +179 -145
  71. package/dist/auth/providers/xai.js.map +1 -1
  72. package/dist/auth/storage.js +182 -175
  73. package/dist/auth/storage.js.map +1 -1
  74. package/dist/auth/types.js +1 -1
  75. package/dist/clipboard/index.js +206 -172
  76. package/dist/clipboard/index.js.map +1 -1
  77. package/dist/constants.js +11 -16
  78. package/dist/constants.js.map +1 -1
  79. package/dist/error-classification.js +19 -11
  80. package/dist/error-classification.js.map +1 -1
  81. package/dist/evals/context-builder.js +145 -104
  82. package/dist/evals/context-builder.js.map +1 -1
  83. package/dist/evals/index.js +4 -7
  84. package/dist/evals/scorers/classify-command.js +62 -41
  85. package/dist/evals/scorers/classify-command.js.map +1 -1
  86. package/dist/evals/scorers/efficiency.js +239 -181
  87. package/dist/evals/scorers/efficiency.js.map +1 -1
  88. package/dist/evals/scorers/extract-tools.js +51 -47
  89. package/dist/evals/scorers/extract-tools.js.map +1 -1
  90. package/dist/evals/scorers/index.js +1 -5
  91. package/dist/evals/scorers/outcome.js +321 -212
  92. package/dist/evals/scorers/outcome.js.map +1 -1
  93. package/dist/goal-manager.js +266 -261
  94. package/dist/goal-manager.js.map +1 -1
  95. package/dist/goal-signal.js +14 -13
  96. package/dist/goal-signal.js.map +1 -1
  97. package/dist/headless/cli.js +153 -154
  98. package/dist/headless/cli.js.map +1 -1
  99. package/dist/headless/flags.js +180 -178
  100. package/dist/headless/flags.js.map +1 -1
  101. package/dist/headless/format.js +72 -71
  102. package/dist/headless/format.js.map +1 -1
  103. package/dist/headless/index.js +5 -31
  104. package/dist/headless/policy.js +35 -31
  105. package/dist/headless/policy.js.map +1 -1
  106. package/dist/headless/run-mc.js +332 -334
  107. package/dist/headless/run-mc.js.map +1 -1
  108. package/dist/headless/types.js +16 -7
  109. package/dist/headless/types.js.map +1 -1
  110. package/dist/hooks/config.js +56 -61
  111. package/dist/hooks/config.js.map +1 -1
  112. package/dist/hooks/executor.js +118 -116
  113. package/dist/hooks/executor.js.map +1 -1
  114. package/dist/hooks/index.js +4 -15
  115. package/dist/hooks/manager.js +221 -280
  116. package/dist/hooks/manager.js.map +1 -1
  117. package/dist/hooks/types.js +5 -4
  118. package/dist/hooks/types.js.map +1 -1
  119. package/dist/index.d.ts.map +1 -1
  120. package/dist/index.js +632 -712
  121. package/dist/index.js.map +1 -1
  122. package/dist/ipc/ipc-reporter.js +19 -22
  123. package/dist/ipc/ipc-reporter.js.map +1 -1
  124. package/dist/lsp/client.js +248 -306
  125. package/dist/lsp/client.js.map +1 -1
  126. package/dist/lsp/index.js +1 -6
  127. package/dist/lsp/language.js +39 -43
  128. package/dist/lsp/language.js.map +1 -1
  129. package/dist/lsp/manager.js +78 -93
  130. package/dist/lsp/manager.js.map +1 -1
  131. package/dist/lsp/server.js +151 -120
  132. package/dist/lsp/server.js.map +1 -1
  133. package/dist/lsp/workspace.js +50 -47
  134. package/dist/lsp/workspace.js.map +1 -1
  135. package/dist/mcp/config.js +210 -199
  136. package/dist/mcp/config.js.map +1 -1
  137. package/dist/mcp/index.js +2 -9
  138. package/dist/mcp/manager.js +482 -475
  139. package/dist/mcp/manager.js.map +1 -1
  140. package/dist/mcp/types.js +1 -1
  141. package/dist/onboarding/custom-packs.js +14 -17
  142. package/dist/onboarding/custom-packs.js.map +1 -1
  143. package/dist/onboarding/custom-providers.js +22 -20
  144. package/dist/onboarding/custom-providers.js.map +1 -1
  145. package/dist/onboarding/index.d.ts +1 -1
  146. package/dist/onboarding/index.d.ts.map +1 -1
  147. package/dist/onboarding/index.js +3 -21
  148. package/dist/onboarding/om-settings.d.ts +5 -0
  149. package/dist/onboarding/om-settings.d.ts.map +1 -1
  150. package/dist/onboarding/om-settings.js +41 -20
  151. package/dist/onboarding/om-settings.js.map +1 -1
  152. package/dist/onboarding/packs.d.ts +4 -6
  153. package/dist/onboarding/packs.d.ts.map +1 -1
  154. package/dist/onboarding/packs.js +154 -105
  155. package/dist/onboarding/packs.js.map +1 -1
  156. package/dist/onboarding/settings.js +575 -517
  157. package/dist/onboarding/settings.js.map +1 -1
  158. package/dist/permissions.js +112 -99
  159. package/dist/permissions.js.map +1 -1
  160. package/dist/plugin.js +21 -23
  161. package/dist/plugin.js.map +1 -1
  162. package/dist/plugins/dependencies.js +64 -87
  163. package/dist/plugins/dependencies.js.map +1 -1
  164. package/dist/plugins/install.js +159 -169
  165. package/dist/plugins/install.js.map +1 -1
  166. package/dist/plugins/loader.js +170 -185
  167. package/dist/plugins/loader.js.map +1 -1
  168. package/dist/plugins/manager.js +374 -380
  169. package/dist/plugins/manager.js.map +1 -1
  170. package/dist/plugins/manifest.js +38 -63
  171. package/dist/plugins/manifest.js.map +1 -1
  172. package/dist/plugins/package-link.js +48 -64
  173. package/dist/plugins/package-link.js.map +1 -1
  174. package/dist/plugins/paths.js +18 -19
  175. package/dist/plugins/paths.js.map +1 -1
  176. package/dist/plugins/registry.js +97 -81
  177. package/dist/plugins/registry.js.map +1 -1
  178. package/dist/plugins/scaffold.js +62 -98
  179. package/dist/plugins/scaffold.js.map +1 -1
  180. package/dist/plugins/types.js +1 -1
  181. package/dist/processors/plan-rejection-abort.js +45 -44
  182. package/dist/processors/plan-rejection-abort.js.map +1 -1
  183. package/dist/providers/amazon-bedrock-gateway.js +134 -125
  184. package/dist/providers/amazon-bedrock-gateway.js.map +1 -1
  185. package/dist/providers/amazon-bedrock.js +68 -47
  186. package/dist/providers/amazon-bedrock.js.map +1 -1
  187. package/dist/providers/claude-max.js +153 -133
  188. package/dist/providers/claude-max.js.map +1 -1
  189. package/dist/providers/github-copilot.js +232 -230
  190. package/dist/providers/github-copilot.js.map +1 -1
  191. package/dist/providers/openai-codex.js +271 -260
  192. package/dist/providers/openai-codex.js.map +1 -1
  193. package/dist/providers/xai.js +57 -50
  194. package/dist/providers/xai.js.map +1 -1
  195. package/dist/schema.js +91 -89
  196. package/dist/schema.js.map +1 -1
  197. package/dist/theme-palette.js +18 -14
  198. package/dist/theme-palette.js.map +1 -1
  199. package/dist/tool-names.js +46 -37
  200. package/dist/tool-names.js.map +1 -1
  201. package/dist/tools/index.js +2 -8
  202. package/dist/tools/request-sandbox-access.js +71 -80
  203. package/dist/tools/request-sandbox-access.js.map +1 -1
  204. package/dist/tools/utils.js +26 -16
  205. package/dist/tools/utils.js.map +1 -1
  206. package/dist/tools/web-search.js +51 -55
  207. package/dist/tools/web-search.js.map +1 -1
  208. package/dist/utils/binaries.js +65 -61
  209. package/dist/utils/binaries.js.map +1 -1
  210. package/dist/utils/debug-log.js +51 -47
  211. package/dist/utils/debug-log.js.map +1 -1
  212. package/dist/utils/errors.js +157 -192
  213. package/dist/utils/errors.js.map +1 -1
  214. package/dist/utils/gateway-sync.js +49 -37
  215. package/dist/utils/gateway-sync.js.map +1 -1
  216. package/dist/utils/path-security.js +7 -6
  217. package/dist/utils/path-security.js.map +1 -1
  218. package/dist/utils/plan-diff.js +58 -39
  219. package/dist/utils/plan-diff.js.map +1 -1
  220. package/dist/utils/plans.js +92 -74
  221. package/dist/utils/plans.js.map +1 -1
  222. package/dist/utils/project.js +318 -256
  223. package/dist/utils/project.js.map +1 -1
  224. package/dist/utils/signals-pubsub.js +117 -99
  225. package/dist/utils/signals-pubsub.js.map +1 -1
  226. package/dist/utils/slash-command-loader.js +118 -154
  227. package/dist/utils/slash-command-loader.js.map +1 -1
  228. package/dist/utils/slash-command-processor.js +92 -83
  229. package/dist/utils/slash-command-processor.js.map +1 -1
  230. package/dist/utils/stdin-pipe.js +87 -63
  231. package/dist/utils/stdin-pipe.js.map +1 -1
  232. package/dist/utils/storage-factory.js +104 -84
  233. package/dist/utils/storage-factory.js.map +1 -1
  234. package/dist/utils/storage-maintenance.js +249 -211
  235. package/dist/utils/storage-maintenance.js.map +1 -1
  236. package/dist/utils/thread-lock.js +96 -92
  237. package/dist/utils/thread-lock.js.map +1 -1
  238. package/dist/utils/token-estimator.js +12 -12
  239. package/dist/utils/token-estimator.js.map +1 -1
  240. package/dist/utils/update-check.d.ts +1 -1
  241. package/dist/utils/update-check.js +341 -259
  242. package/dist/utils/update-check.js.map +1 -1
  243. package/dist/voice/stt-registry.js +131 -113
  244. package/dist/voice/stt-registry.js.map +1 -1
  245. package/package.json +11 -11
  246. package/dist/acp.js.map +0 -1
  247. package/dist/auth/index.js.map +0 -1
  248. package/dist/auth/types.js.map +0 -1
  249. package/dist/evals/index.js.map +0 -1
  250. package/dist/evals/scorers/index.js.map +0 -1
  251. package/dist/headless/index.js.map +0 -1
  252. package/dist/hooks/index.js.map +0 -1
  253. package/dist/lsp/index.js.map +0 -1
  254. package/dist/mcp/index.js.map +0 -1
  255. package/dist/mcp/types.js.map +0 -1
  256. package/dist/onboarding/index.js.map +0 -1
  257. package/dist/plugins/types.js.map +0 -1
  258. package/dist/tools/index.js.map +0 -1
@@ -1,31 +1,38 @@
1
+ import { parseAuthorizationInput } from "../authorization-input.js";
2
+ import { generatePKCE } from "../pkce.js";
3
+ //#region src/auth/providers/openai-codex.ts
4
+ /**
5
+ * OpenAI Codex (ChatGPT OAuth) flow
6
+ *
7
+ * Inspired by pi-mono's OAuth implementation:
8
+ * https://github.com/badlogic/pi-mono/blob/main/packages/ai/src/utils/oauth/openai-codex.ts
9
+ *
10
+ * NOTE: This module uses Node.js crypto and http for the OAuth callback.
11
+ * It is only intended for CLI use, not browser environments.
12
+ */
1
13
  let _randomBytes = null;
2
14
  let _cryptoPromise = null;
3
15
  let _httpPromise = null;
4
16
  let _http = null;
5
17
  if (typeof process !== "undefined" && (process.versions?.node || process.versions?.bun)) {
6
- _cryptoPromise = import("node:crypto").then((m) => {
7
- _randomBytes = m.randomBytes;
8
- return m;
9
- });
10
- _httpPromise = import("node:http").then((m) => {
11
- _http = m;
12
- return m;
13
- });
18
+ _cryptoPromise = import("crypto").then((m) => {
19
+ _randomBytes = m.randomBytes;
20
+ return m;
21
+ });
22
+ _httpPromise = import("http").then((m) => {
23
+ _http = m;
24
+ return m;
25
+ });
14
26
  }
15
- import { parseAuthorizationInput } from "../authorization-input.js";
16
- import { generatePKCE } from "../pkce.js";
17
- const OPENAI_CODEX_AUTH_MODES = [
18
- {
19
- id: "browser",
20
- name: "Browser (local callback)",
21
- description: "Opens the browser and waits for the OAuth callback on localhost."
22
- },
23
- {
24
- id: "device",
25
- name: "Device code (headless)",
26
- description: "Shows a code to enter at openai.com \u2014 for SSH, remote, or no-browser environments."
27
- }
28
- ];
27
+ const OPENAI_CODEX_AUTH_MODES = [{
28
+ id: "browser",
29
+ name: "Browser (local callback)",
30
+ description: "Opens the browser and waits for the OAuth callback on localhost."
31
+ }, {
32
+ id: "device",
33
+ name: "Device code (headless)",
34
+ description: "Shows a code to enter at openai.com — for SSH, remote, or no-browser environments."
35
+ }];
29
36
  const CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
30
37
  const ISSUER = "https://auth.openai.com";
31
38
  const AUTHORIZE_URL = `${ISSUER}/oauth/authorize`;
@@ -37,7 +44,7 @@ const DEVICE_REDIRECT_URI = `${ISSUER}/deviceauth/callback`;
37
44
  const DEFAULT_CALLBACK_PORT = 1455;
38
45
  const FALLBACK_CALLBACK_PORT = 1457;
39
46
  const DEFAULT_TOKEN_EXPIRES_IN_SECONDS = 3600;
40
- const DEVICE_AUTH_TIMEOUT_MS = 15 * 60 * 1e3;
47
+ const DEVICE_AUTH_TIMEOUT_MS = 900 * 1e3;
41
48
  const SCOPE = "openid profile email offline_access api.connectors.read api.connectors.invoke";
42
49
  const JWT_CLAIM_PATH = "https://api.openai.com/auth";
43
50
  const SUCCESS_HTML = `<!doctype html>
@@ -52,486 +59,461 @@ const SUCCESS_HTML = `<!doctype html>
52
59
  </body>
53
60
  </html>`;
54
61
  async function createState() {
55
- const randomBytes = await getRandomBytes();
56
- return randomBytes(16).toString("hex");
62
+ return (await getRandomBytes())(16).toString("hex");
57
63
  }
58
64
  function decodeJwt(token) {
59
- try {
60
- const parts = token.split(".");
61
- if (parts.length !== 3) return null;
62
- const payload = parts[1] ?? "";
63
- const padded = payload.replace(/-/g, "+").replace(/_/g, "/").padEnd(Math.ceil(payload.length / 4) * 4, "=");
64
- const decoded = atob(padded);
65
- return JSON.parse(decoded);
66
- } catch {
67
- return null;
68
- }
65
+ try {
66
+ const parts = token.split(".");
67
+ if (parts.length !== 3) return null;
68
+ const payload = parts[1] ?? "";
69
+ const padded = payload.replace(/-/g, "+").replace(/_/g, "/").padEnd(Math.ceil(payload.length / 4) * 4, "=");
70
+ const decoded = atob(padded);
71
+ return JSON.parse(decoded);
72
+ } catch {
73
+ return null;
74
+ }
69
75
  }
70
76
  function extractAccountIdFromClaims(payload) {
71
- if (!payload) return null;
72
- const accountId = payload.chatgpt_account_id ?? payload[JWT_CLAIM_PATH]?.chatgpt_account_id;
73
- return typeof accountId === "string" && accountId.length > 0 ? accountId : null;
77
+ if (!payload) return null;
78
+ const accountId = payload.chatgpt_account_id ?? payload[JWT_CLAIM_PATH]?.chatgpt_account_id;
79
+ return typeof accountId === "string" && accountId.length > 0 ? accountId : null;
74
80
  }
75
81
  function getAccountId(tokens, fallback) {
76
- const fromIdToken = tokens.idToken ? extractAccountIdFromClaims(decodeJwt(tokens.idToken)) : null;
77
- if (fromIdToken) return fromIdToken;
78
- const fromAccessToken = extractAccountIdFromClaims(decodeJwt(tokens.access));
79
- if (fromAccessToken) return fromAccessToken;
80
- return fallback;
82
+ const fromIdToken = tokens.idToken ? extractAccountIdFromClaims(decodeJwt(tokens.idToken)) : null;
83
+ if (fromIdToken) return fromIdToken;
84
+ const fromAccessToken = extractAccountIdFromClaims(decodeJwt(tokens.access));
85
+ if (fromAccessToken) return fromAccessToken;
86
+ return fallback;
81
87
  }
82
88
  function requireAccountId(tokens, fallback) {
83
- const accountId = getAccountId(tokens, fallback);
84
- if (!accountId) {
85
- throw new Error("Failed to extract ChatGPT account id from OpenAI Codex token");
86
- }
87
- return accountId;
89
+ const accountId = getAccountId(tokens, fallback);
90
+ if (!accountId) throw new Error("Failed to extract ChatGPT account id from OpenAI Codex token");
91
+ return accountId;
88
92
  }
89
93
  function tokenResponseToResult(json, logPrefix) {
90
- if (!json.access_token || !json.refresh_token) {
91
- console.error(`[openai-codex] ${logPrefix} response missing fields:`, json);
92
- return { type: "failed" };
93
- }
94
- return {
95
- type: "success",
96
- access: json.access_token,
97
- refresh: json.refresh_token,
98
- expires: Date.now() + (json.expires_in ?? DEFAULT_TOKEN_EXPIRES_IN_SECONDS) * 1e3,
99
- idToken: json.id_token
100
- };
94
+ if (!json.access_token || !json.refresh_token) {
95
+ console.error(`[openai-codex] ${logPrefix} response missing fields:`, json);
96
+ return { type: "failed" };
97
+ }
98
+ return {
99
+ type: "success",
100
+ access: json.access_token,
101
+ refresh: json.refresh_token,
102
+ expires: Date.now() + (json.expires_in ?? DEFAULT_TOKEN_EXPIRES_IN_SECONDS) * 1e3,
103
+ idToken: json.id_token
104
+ };
101
105
  }
102
106
  async function exchangeAuthorizationCode(code, verifier, redirectUri) {
103
- const response = await fetch(TOKEN_URL, {
104
- method: "POST",
105
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
106
- body: new URLSearchParams({
107
- grant_type: "authorization_code",
108
- client_id: CLIENT_ID,
109
- code,
110
- code_verifier: verifier,
111
- redirect_uri: redirectUri
112
- })
113
- });
114
- if (!response.ok) {
115
- const text = await response.text().catch(() => "");
116
- console.error("[openai-codex] code->token failed:", response.status, text);
117
- return { type: "failed" };
118
- }
119
- return tokenResponseToResult(await response.json(), "token");
107
+ const response = await fetch(TOKEN_URL, {
108
+ method: "POST",
109
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
110
+ body: new URLSearchParams({
111
+ grant_type: "authorization_code",
112
+ client_id: CLIENT_ID,
113
+ code,
114
+ code_verifier: verifier,
115
+ redirect_uri: redirectUri
116
+ })
117
+ });
118
+ if (!response.ok) {
119
+ const text = await response.text().catch(() => "");
120
+ console.error("[openai-codex] code->token failed:", response.status, text);
121
+ return { type: "failed" };
122
+ }
123
+ return tokenResponseToResult(await response.json(), "token");
120
124
  }
121
125
  async function refreshAccessToken(refreshToken) {
122
- try {
123
- const response = await fetch(TOKEN_URL, {
124
- method: "POST",
125
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
126
- body: new URLSearchParams({
127
- grant_type: "refresh_token",
128
- refresh_token: refreshToken,
129
- client_id: CLIENT_ID
130
- })
131
- });
132
- if (!response.ok) {
133
- const text = await response.text().catch(() => "");
134
- console.error("[openai-codex] Token refresh failed:", response.status, text);
135
- return { type: "failed" };
136
- }
137
- return tokenResponseToResult(await response.json(), "Token refresh");
138
- } catch (error) {
139
- console.error("[openai-codex] Token refresh error:", error);
140
- return { type: "failed" };
141
- }
126
+ try {
127
+ const response = await fetch(TOKEN_URL, {
128
+ method: "POST",
129
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
130
+ body: new URLSearchParams({
131
+ grant_type: "refresh_token",
132
+ refresh_token: refreshToken,
133
+ client_id: CLIENT_ID
134
+ })
135
+ });
136
+ if (!response.ok) {
137
+ const text = await response.text().catch(() => "");
138
+ console.error("[openai-codex] Token refresh failed:", response.status, text);
139
+ return { type: "failed" };
140
+ }
141
+ return tokenResponseToResult(await response.json(), "Token refresh");
142
+ } catch (error) {
143
+ console.error("[openai-codex] Token refresh error:", error);
144
+ return { type: "failed" };
145
+ }
142
146
  }
143
147
  async function getRandomBytes() {
144
- if (!_randomBytes && _cryptoPromise) {
145
- _randomBytes = (await _cryptoPromise).randomBytes;
146
- }
147
- if (!_randomBytes) {
148
- throw new Error("OpenAI Codex OAuth is only available in Node.js environments");
149
- }
150
- return _randomBytes;
148
+ if (!_randomBytes && _cryptoPromise) _randomBytes = (await _cryptoPromise).randomBytes;
149
+ if (!_randomBytes) throw new Error("OpenAI Codex OAuth is only available in Node.js environments");
150
+ return _randomBytes;
151
151
  }
152
152
  async function createAuthorizationFlow(redirectUri, state, originator = "mastracode") {
153
- const { verifier, challenge } = await generatePKCE();
154
- const url = new URL(AUTHORIZE_URL);
155
- url.searchParams.set("response_type", "code");
156
- url.searchParams.set("client_id", CLIENT_ID);
157
- url.searchParams.set("redirect_uri", redirectUri);
158
- url.searchParams.set("scope", SCOPE);
159
- url.searchParams.set("code_challenge", challenge);
160
- url.searchParams.set("code_challenge_method", "S256");
161
- url.searchParams.set("state", state);
162
- url.searchParams.set("id_token_add_organizations", "true");
163
- url.searchParams.set("codex_cli_simplified_flow", "true");
164
- url.searchParams.set("originator", originator);
165
- return { verifier, url: url.toString() };
153
+ const { verifier, challenge } = await generatePKCE();
154
+ const url = new URL(AUTHORIZE_URL);
155
+ url.searchParams.set("response_type", "code");
156
+ url.searchParams.set("client_id", CLIENT_ID);
157
+ url.searchParams.set("redirect_uri", redirectUri);
158
+ url.searchParams.set("scope", SCOPE);
159
+ url.searchParams.set("code_challenge", challenge);
160
+ url.searchParams.set("code_challenge_method", "S256");
161
+ url.searchParams.set("state", state);
162
+ url.searchParams.set("id_token_add_organizations", "true");
163
+ url.searchParams.set("codex_cli_simplified_flow", "true");
164
+ url.searchParams.set("originator", originator);
165
+ return {
166
+ verifier,
167
+ url: url.toString()
168
+ };
166
169
  }
167
170
  const CODEX_CALLBACK_PORTS = {
168
- defaultPort: DEFAULT_CALLBACK_PORT,
169
- fallbackPort: FALLBACK_CALLBACK_PORT
171
+ defaultPort: DEFAULT_CALLBACK_PORT,
172
+ fallbackPort: FALLBACK_CALLBACK_PORT
170
173
  };
171
174
  async function requestCancel(port) {
172
- try {
173
- await fetch(`http://127.0.0.1:${port}/cancel`, { signal: AbortSignal.timeout(200) });
174
- } catch {
175
- }
175
+ try {
176
+ await fetch(`http://127.0.0.1:${port}/cancel`, { signal: AbortSignal.timeout(200) });
177
+ } catch {}
176
178
  }
177
179
  function listen(server, port) {
178
- return new Promise((resolve) => {
179
- const onError = () => {
180
- server.off("listening", onListening);
181
- resolve(false);
182
- };
183
- const onListening = () => {
184
- server.off("error", onError);
185
- resolve(true);
186
- };
187
- server.once("error", onError);
188
- server.once("listening", onListening);
189
- server.listen(port, "127.0.0.1");
190
- });
180
+ return new Promise((resolve) => {
181
+ const onError = () => {
182
+ server.off("listening", onListening);
183
+ resolve(false);
184
+ };
185
+ const onListening = () => {
186
+ server.off("error", onError);
187
+ resolve(true);
188
+ };
189
+ server.once("error", onError);
190
+ server.once("listening", onListening);
191
+ server.listen(port, "127.0.0.1");
192
+ });
191
193
  }
192
194
  async function bindOAuthServer(server, ports) {
193
- await requestCancel(ports.defaultPort);
194
- if (await listen(server, ports.defaultPort)) return ports.defaultPort;
195
- if (await listen(server, ports.fallbackPort)) return ports.fallbackPort;
196
- return null;
195
+ await requestCancel(ports.defaultPort);
196
+ if (await listen(server, ports.defaultPort)) return ports.defaultPort;
197
+ if (await listen(server, ports.fallbackPort)) return ports.fallbackPort;
198
+ return null;
197
199
  }
198
200
  async function getHttpModule() {
199
- if (!_http && _httpPromise) {
200
- _http = await _httpPromise;
201
- }
202
- if (!_http) {
203
- throw new Error("OpenAI Codex OAuth is only available in Node.js environments");
204
- }
205
- return _http;
201
+ if (!_http && _httpPromise) _http = await _httpPromise;
202
+ if (!_http) throw new Error("OpenAI Codex OAuth is only available in Node.js environments");
203
+ return _http;
206
204
  }
207
205
  async function startLocalOAuthServer(state, ports = CODEX_CALLBACK_PORTS) {
208
- const http = await getHttpModule();
209
- let lastCode = null;
210
- let cancelled = false;
211
- const server = http.createServer((req, res) => {
212
- try {
213
- const url = new URL(req.url || "", "http://localhost");
214
- if (url.pathname === "/cancel") {
215
- cancelled = true;
216
- res.statusCode = 200;
217
- res.end("Cancelled");
218
- return;
219
- }
220
- if (url.pathname !== "/auth/callback") {
221
- res.statusCode = 404;
222
- res.end("Not found");
223
- return;
224
- }
225
- if (url.searchParams.get("state") !== state) {
226
- res.statusCode = 400;
227
- res.end("State mismatch");
228
- return;
229
- }
230
- const code = url.searchParams.get("code");
231
- if (!code) {
232
- res.statusCode = 400;
233
- res.end("Missing authorization code");
234
- return;
235
- }
236
- res.statusCode = 200;
237
- res.setHeader("Content-Type", "text/html; charset=utf-8");
238
- res.end(SUCCESS_HTML);
239
- lastCode = code;
240
- } catch {
241
- res.statusCode = 500;
242
- res.end("Internal error");
243
- }
244
- });
245
- return new Promise((resolve) => {
246
- bindOAuthServer(server, ports).then((port) => {
247
- if (!port) {
248
- resolve({
249
- redirectUri: `http://localhost:${ports.fallbackPort}/auth/callback`,
250
- warning: `OpenAI Codex OAuth requires localhost port ${ports.defaultPort} or ${ports.fallbackPort}, but both are in use. Automatic browser callback will not work until one is freed.`,
251
- close: () => {
252
- try {
253
- server.close();
254
- } catch {
255
- }
256
- },
257
- cancelWait: () => {
258
- },
259
- waitForCode: async () => null
260
- });
261
- return;
262
- }
263
- resolve({
264
- redirectUri: `http://localhost:${port}/auth/callback`,
265
- close: () => server.close(),
266
- cancelWait: () => {
267
- cancelled = true;
268
- },
269
- waitForCode: async () => {
270
- const sleep = () => new Promise((r) => setTimeout(r, 100));
271
- for (let i = 0; i < 600; i += 1) {
272
- if (lastCode) return { code: lastCode };
273
- if (cancelled) return null;
274
- await sleep();
275
- }
276
- return null;
277
- }
278
- });
279
- });
280
- });
206
+ const http = await getHttpModule();
207
+ let lastCode = null;
208
+ let cancelled = false;
209
+ const server = http.createServer((req, res) => {
210
+ try {
211
+ const url = new URL(req.url || "", "http://localhost");
212
+ if (url.pathname === "/cancel") {
213
+ cancelled = true;
214
+ res.statusCode = 200;
215
+ res.end("Cancelled");
216
+ return;
217
+ }
218
+ if (url.pathname !== "/auth/callback") {
219
+ res.statusCode = 404;
220
+ res.end("Not found");
221
+ return;
222
+ }
223
+ if (url.searchParams.get("state") !== state) {
224
+ res.statusCode = 400;
225
+ res.end("State mismatch");
226
+ return;
227
+ }
228
+ const code = url.searchParams.get("code");
229
+ if (!code) {
230
+ res.statusCode = 400;
231
+ res.end("Missing authorization code");
232
+ return;
233
+ }
234
+ res.statusCode = 200;
235
+ res.setHeader("Content-Type", "text/html; charset=utf-8");
236
+ res.end(SUCCESS_HTML);
237
+ lastCode = code;
238
+ } catch {
239
+ res.statusCode = 500;
240
+ res.end("Internal error");
241
+ }
242
+ });
243
+ return new Promise((resolve) => {
244
+ bindOAuthServer(server, ports).then((port) => {
245
+ if (!port) {
246
+ resolve({
247
+ redirectUri: `http://localhost:${ports.fallbackPort}/auth/callback`,
248
+ warning: `OpenAI Codex OAuth requires localhost port ${ports.defaultPort} or ${ports.fallbackPort}, but both are in use. Automatic browser callback will not work until one is freed.`,
249
+ close: () => {
250
+ try {
251
+ server.close();
252
+ } catch {}
253
+ },
254
+ cancelWait: () => {},
255
+ waitForCode: async () => null
256
+ });
257
+ return;
258
+ }
259
+ resolve({
260
+ redirectUri: `http://localhost:${port}/auth/callback`,
261
+ close: () => server.close(),
262
+ cancelWait: () => {
263
+ cancelled = true;
264
+ },
265
+ waitForCode: async () => {
266
+ const sleep = () => new Promise((r) => setTimeout(r, 100));
267
+ for (let i = 0; i < 600; i += 1) {
268
+ if (lastCode) return { code: lastCode };
269
+ if (cancelled) return null;
270
+ await sleep();
271
+ }
272
+ return null;
273
+ }
274
+ });
275
+ });
276
+ });
281
277
  }
278
+ /**
279
+ * Start a Codex device-code login: request a user code and return the
280
+ * serializable pending state for subsequent polls.
281
+ */
282
282
  async function startCodexDeviceLogin(options) {
283
- const response = await fetch(DEVICE_USER_CODE_URL, {
284
- method: "POST",
285
- headers: {
286
- "Content-Type": "application/json",
287
- "User-Agent": "mastracode"
288
- },
289
- body: JSON.stringify({ client_id: CLIENT_ID, originator: "mastracode" }),
290
- signal: options?.signal
291
- });
292
- if (!response.ok) {
293
- throw new Error(`Failed to initiate OpenAI Codex device authorization: ${response.status}`);
294
- }
295
- const deviceData = await response.json();
296
- const userCode = deviceData.user_code ?? deviceData.usercode;
297
- if (!deviceData.device_auth_id || !userCode) {
298
- throw new Error("OpenAI Codex device authorization response missing required fields");
299
- }
300
- const intervalSeconds = typeof deviceData.interval === "number" ? deviceData.interval : Number.parseInt(deviceData.interval ?? "", 10) || 5;
301
- return {
302
- deviceAuthId: deviceData.device_auth_id,
303
- userCode,
304
- url: DEVICE_AUTHORIZE_URL,
305
- instructions: `Enter code: ${userCode}`,
306
- intervalMs: Math.max(intervalSeconds, 1) * 1e3,
307
- deadlineAt: Date.now() + DEVICE_AUTH_TIMEOUT_MS
308
- };
283
+ const response = await fetch(DEVICE_USER_CODE_URL, {
284
+ method: "POST",
285
+ headers: {
286
+ "Content-Type": "application/json",
287
+ "User-Agent": "mastracode"
288
+ },
289
+ body: JSON.stringify({
290
+ client_id: CLIENT_ID,
291
+ originator: "mastracode"
292
+ }),
293
+ signal: options?.signal
294
+ });
295
+ if (!response.ok) throw new Error(`Failed to initiate OpenAI Codex device authorization: ${response.status}`);
296
+ const deviceData = await response.json();
297
+ const userCode = deviceData.user_code ?? deviceData.usercode;
298
+ if (!deviceData.device_auth_id || !userCode) throw new Error("OpenAI Codex device authorization response missing required fields");
299
+ const intervalSeconds = typeof deviceData.interval === "number" ? deviceData.interval : Number.parseInt(deviceData.interval ?? "", 10) || 5;
300
+ return {
301
+ deviceAuthId: deviceData.device_auth_id,
302
+ userCode,
303
+ url: DEVICE_AUTHORIZE_URL,
304
+ instructions: `Enter code: ${userCode}`,
305
+ intervalMs: Math.max(intervalSeconds, 1) * 1e3,
306
+ deadlineAt: Date.now() + DEVICE_AUTH_TIMEOUT_MS
307
+ };
309
308
  }
309
+ /**
310
+ * Perform exactly one upstream poll for a pending Codex device login.
311
+ * The Codex device endpoint signals "still pending" via HTTP 403/404 (it is
312
+ * not an RFC 8628 error-JSON endpoint); on success it returns the
313
+ * authorization code plus server-held PKCE verifier, which we exchange
314
+ * immediately for credentials. Never throws for flow-level conditions.
315
+ */
310
316
  async function pollCodexDeviceLogin(pending, options) {
311
- if (Date.now() >= pending.deadlineAt) {
312
- return { status: "failed", error: "OpenAI Codex device authorization timed out after 15 minutes" };
313
- }
314
- const pollResponse = await fetch(DEVICE_TOKEN_URL, {
315
- method: "POST",
316
- headers: {
317
- "Content-Type": "application/json",
318
- "User-Agent": "mastracode"
319
- },
320
- body: JSON.stringify({
321
- device_auth_id: pending.deviceAuthId,
322
- user_code: pending.userCode
323
- }),
324
- signal: options?.signal
325
- });
326
- if (pollResponse.ok) {
327
- const data = await pollResponse.json();
328
- if (!data.authorization_code || !data.code_verifier) {
329
- return { status: "failed", error: "OpenAI Codex device token response missing required fields" };
330
- }
331
- const tokenResult = await exchangeAuthorizationCode(
332
- data.authorization_code,
333
- data.code_verifier,
334
- DEVICE_REDIRECT_URI
335
- );
336
- if (tokenResult.type !== "success") {
337
- return { status: "failed", error: "Token exchange failed" };
338
- }
339
- let accountId;
340
- try {
341
- accountId = requireAccountId(tokenResult);
342
- } catch (error) {
343
- return { status: "failed", error: error instanceof Error ? error.message : String(error) };
344
- }
345
- return {
346
- status: "complete",
347
- credentials: {
348
- access: tokenResult.access,
349
- refresh: tokenResult.refresh,
350
- expires: tokenResult.expires,
351
- accountId
352
- }
353
- };
354
- }
355
- if (pollResponse.status !== 403 && pollResponse.status !== 404) {
356
- const text = await pollResponse.text().catch(() => "");
357
- return {
358
- status: "failed",
359
- error: `OpenAI Codex device authorization failed: ${pollResponse.status}${text ? ` ${text}` : ""}`
360
- };
361
- }
362
- return { status: "pending", nextPollMs: pending.intervalMs };
317
+ if (Date.now() >= pending.deadlineAt) return {
318
+ status: "failed",
319
+ error: "OpenAI Codex device authorization timed out after 15 minutes"
320
+ };
321
+ const pollResponse = await fetch(DEVICE_TOKEN_URL, {
322
+ method: "POST",
323
+ headers: {
324
+ "Content-Type": "application/json",
325
+ "User-Agent": "mastracode"
326
+ },
327
+ body: JSON.stringify({
328
+ device_auth_id: pending.deviceAuthId,
329
+ user_code: pending.userCode
330
+ }),
331
+ signal: options?.signal
332
+ });
333
+ if (pollResponse.ok) {
334
+ const data = await pollResponse.json();
335
+ if (!data.authorization_code || !data.code_verifier) return {
336
+ status: "failed",
337
+ error: "OpenAI Codex device token response missing required fields"
338
+ };
339
+ const tokenResult = await exchangeAuthorizationCode(data.authorization_code, data.code_verifier, DEVICE_REDIRECT_URI);
340
+ if (tokenResult.type !== "success") return {
341
+ status: "failed",
342
+ error: "Token exchange failed"
343
+ };
344
+ let accountId;
345
+ try {
346
+ accountId = requireAccountId(tokenResult);
347
+ } catch (error) {
348
+ return {
349
+ status: "failed",
350
+ error: error instanceof Error ? error.message : String(error)
351
+ };
352
+ }
353
+ return {
354
+ status: "complete",
355
+ credentials: {
356
+ access: tokenResult.access,
357
+ refresh: tokenResult.refresh,
358
+ expires: tokenResult.expires,
359
+ accountId
360
+ }
361
+ };
362
+ }
363
+ if (pollResponse.status !== 403 && pollResponse.status !== 404) {
364
+ const text = await pollResponse.text().catch(() => "");
365
+ return {
366
+ status: "failed",
367
+ error: `OpenAI Codex device authorization failed: ${pollResponse.status}${text ? ` ${text}` : ""}`
368
+ };
369
+ }
370
+ return {
371
+ status: "pending",
372
+ nextPollMs: pending.intervalMs
373
+ };
363
374
  }
364
375
  async function loginOpenAICodexDevice(options) {
365
- const pending = await startCodexDeviceLogin({ signal: options.signal });
366
- const sleep = options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
367
- options.onAuth({
368
- url: pending.url,
369
- instructions: pending.instructions
370
- });
371
- await sleep(pending.intervalMs);
372
- while (true) {
373
- if (options.signal?.aborted) {
374
- throw new Error("Login cancelled");
375
- }
376
- const result = await pollCodexDeviceLogin(pending, { signal: options.signal });
377
- if (result.status === "complete") {
378
- return result.credentials;
379
- }
380
- if (result.status === "failed") {
381
- throw new Error(result.error);
382
- }
383
- options.onProgress?.("Waiting for OpenAI Codex device authorization...");
384
- await sleep(result.nextPollMs);
385
- }
376
+ const pending = await startCodexDeviceLogin({ signal: options.signal });
377
+ const sleep = options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
378
+ options.onAuth({
379
+ url: pending.url,
380
+ instructions: pending.instructions
381
+ });
382
+ await sleep(pending.intervalMs);
383
+ while (true) {
384
+ if (options.signal?.aborted) throw new Error("Login cancelled");
385
+ const result = await pollCodexDeviceLogin(pending, { signal: options.signal });
386
+ if (result.status === "complete") return result.credentials;
387
+ if (result.status === "failed") throw new Error(result.error);
388
+ options.onProgress?.("Waiting for OpenAI Codex device authorization...");
389
+ await sleep(result.nextPollMs);
390
+ }
386
391
  }
392
+ /**
393
+ * Login with OpenAI Codex OAuth
394
+ *
395
+ * @param options.onAuth - Called with URL and instructions when auth starts
396
+ * @param options.onPrompt - Called to prompt user for manual code paste (fallback if no onManualCodeInput)
397
+ * @param options.onProgress - Optional progress messages
398
+ * @param options.onManualCodeInput - Optional promise that resolves with user-pasted code.
399
+ * Races with browser callback - whichever completes first wins.
400
+ * Useful for showing paste input immediately alongside browser flow.
401
+ * @param options.originator - OAuth originator parameter (defaults to "mastracode")
402
+ */
387
403
  async function loginOpenAICodex(options) {
388
- const envMode = typeof process !== "undefined" && process.env?.MASTRACODE_OPENAI_CODEX_AUTH_MODE === "device" ? "device" : void 0;
389
- const mode = options.mode ?? envMode ?? "browser";
390
- if (mode === "device") {
391
- return loginOpenAICodexDevice({
392
- onAuth: options.onAuth,
393
- onProgress: options.onProgress,
394
- signal: options.signal
395
- });
396
- }
397
- const state = await createState();
398
- const server = await startLocalOAuthServer(state);
399
- if (server.warning) {
400
- options.onProgress?.(server.warning);
401
- }
402
- const { verifier, url } = await createAuthorizationFlow(
403
- server.redirectUri,
404
- state,
405
- options.originator ?? "mastracode"
406
- );
407
- options.onAuth({
408
- url,
409
- instructions: server.warning ? `${server.warning} You can still paste the authorization code or full redirect URL manually.` : "A browser window should open. Complete login to finish."
410
- });
411
- let code;
412
- try {
413
- if (options.onManualCodeInput) {
414
- let manualCode;
415
- let manualError;
416
- const manualPromise = options.onManualCodeInput().then((input) => {
417
- manualCode = input;
418
- server.cancelWait();
419
- }).catch((err) => {
420
- manualError = err instanceof Error ? err : new Error(String(err));
421
- server.cancelWait();
422
- });
423
- const result = await server.waitForCode();
424
- if (manualError) {
425
- throw manualError;
426
- }
427
- if (result?.code) {
428
- code = result.code;
429
- } else if (manualCode) {
430
- const parsed = parseAuthorizationInput(manualCode);
431
- if (parsed.state && parsed.state !== state) {
432
- throw new Error("State mismatch");
433
- }
434
- code = parsed.code;
435
- }
436
- if (!code) {
437
- await manualPromise;
438
- if (manualError) {
439
- throw manualError;
440
- }
441
- if (manualCode) {
442
- const parsed = parseAuthorizationInput(manualCode);
443
- if (parsed.state && parsed.state !== state) {
444
- throw new Error("State mismatch");
445
- }
446
- code = parsed.code;
447
- }
448
- }
449
- } else {
450
- const result = await server.waitForCode();
451
- if (result?.code) {
452
- code = result.code;
453
- }
454
- }
455
- if (!code) {
456
- const input = await options.onPrompt({
457
- message: "Paste the authorization code (or full redirect URL):"
458
- });
459
- const parsed = parseAuthorizationInput(input);
460
- if (parsed.state && parsed.state !== state) {
461
- throw new Error("State mismatch");
462
- }
463
- code = parsed.code;
464
- }
465
- if (!code) {
466
- throw new Error("Missing authorization code");
467
- }
468
- const tokenResult = await exchangeAuthorizationCode(code, verifier, server.redirectUri);
469
- if (tokenResult.type !== "success") {
470
- throw new Error("Token exchange failed");
471
- }
472
- const accountId = requireAccountId(tokenResult);
473
- return {
474
- access: tokenResult.access,
475
- refresh: tokenResult.refresh,
476
- expires: tokenResult.expires,
477
- accountId
478
- };
479
- } finally {
480
- server.close();
481
- }
404
+ const envMode = typeof process !== "undefined" && process.env?.MASTRACODE_OPENAI_CODEX_AUTH_MODE === "device" ? "device" : void 0;
405
+ if ((options.mode ?? envMode ?? "browser") === "device") return loginOpenAICodexDevice({
406
+ onAuth: options.onAuth,
407
+ onProgress: options.onProgress,
408
+ signal: options.signal
409
+ });
410
+ const state = await createState();
411
+ const server = await startLocalOAuthServer(state);
412
+ if (server.warning) options.onProgress?.(server.warning);
413
+ const { verifier, url } = await createAuthorizationFlow(server.redirectUri, state, options.originator ?? "mastracode");
414
+ options.onAuth({
415
+ url,
416
+ instructions: server.warning ? `${server.warning} You can still paste the authorization code or full redirect URL manually.` : "A browser window should open. Complete login to finish."
417
+ });
418
+ let code;
419
+ try {
420
+ if (options.onManualCodeInput) {
421
+ let manualCode;
422
+ let manualError;
423
+ const manualPromise = options.onManualCodeInput().then((input) => {
424
+ manualCode = input;
425
+ server.cancelWait();
426
+ }).catch((err) => {
427
+ manualError = err instanceof Error ? err : new Error(String(err));
428
+ server.cancelWait();
429
+ });
430
+ const result = await server.waitForCode();
431
+ if (manualError) throw manualError;
432
+ if (result?.code) code = result.code;
433
+ else if (manualCode) {
434
+ const parsed = parseAuthorizationInput(manualCode);
435
+ if (parsed.state && parsed.state !== state) throw new Error("State mismatch");
436
+ code = parsed.code;
437
+ }
438
+ if (!code) {
439
+ await manualPromise;
440
+ if (manualError) throw manualError;
441
+ if (manualCode) {
442
+ const parsed = parseAuthorizationInput(manualCode);
443
+ if (parsed.state && parsed.state !== state) throw new Error("State mismatch");
444
+ code = parsed.code;
445
+ }
446
+ }
447
+ } else {
448
+ const result = await server.waitForCode();
449
+ if (result?.code) code = result.code;
450
+ }
451
+ if (!code) {
452
+ const parsed = parseAuthorizationInput(await options.onPrompt({ message: "Paste the authorization code (or full redirect URL):" }));
453
+ if (parsed.state && parsed.state !== state) throw new Error("State mismatch");
454
+ code = parsed.code;
455
+ }
456
+ if (!code) throw new Error("Missing authorization code");
457
+ const tokenResult = await exchangeAuthorizationCode(code, verifier, server.redirectUri);
458
+ if (tokenResult.type !== "success") throw new Error("Token exchange failed");
459
+ const accountId = requireAccountId(tokenResult);
460
+ return {
461
+ access: tokenResult.access,
462
+ refresh: tokenResult.refresh,
463
+ expires: tokenResult.expires,
464
+ accountId
465
+ };
466
+ } finally {
467
+ server.close();
468
+ }
482
469
  }
483
470
  const __testing = {
484
- createAuthorizationFlow,
485
- decodeJwt,
486
- extractAccountIdFromClaims,
487
- getAccountId,
488
- loginOpenAICodexDevice,
489
- requireAccountId,
490
- startLocalOAuthServer
471
+ createAuthorizationFlow,
472
+ decodeJwt,
473
+ extractAccountIdFromClaims,
474
+ getAccountId,
475
+ loginOpenAICodexDevice,
476
+ requireAccountId,
477
+ startLocalOAuthServer
491
478
  };
479
+ /**
480
+ * Refresh OpenAI Codex OAuth token
481
+ */
492
482
  async function refreshOpenAICodexToken(refreshToken, previousAccountId) {
493
- const result = await refreshAccessToken(refreshToken);
494
- if (result.type !== "success") {
495
- throw new Error("Failed to refresh OpenAI Codex token");
496
- }
497
- const accountId = requireAccountId(result, previousAccountId);
498
- return {
499
- access: result.access,
500
- refresh: result.refresh,
501
- expires: result.expires,
502
- accountId
503
- };
483
+ const result = await refreshAccessToken(refreshToken);
484
+ if (result.type !== "success") throw new Error("Failed to refresh OpenAI Codex token");
485
+ const accountId = requireAccountId(result, previousAccountId);
486
+ return {
487
+ access: result.access,
488
+ refresh: result.refresh,
489
+ expires: result.expires,
490
+ accountId
491
+ };
504
492
  }
505
493
  const openaiCodexOAuthProvider = {
506
- id: "openai-codex",
507
- name: "ChatGPT Plus/Pro (Codex Subscription)",
508
- usesCallbackServer: true,
509
- authModes: OPENAI_CODEX_AUTH_MODES,
510
- async login(callbacks) {
511
- const mode = callbacks.authMode === "device" || callbacks.authMode === "browser" ? callbacks.authMode : void 0;
512
- return loginOpenAICodex({
513
- onAuth: callbacks.onAuth,
514
- onPrompt: callbacks.onPrompt,
515
- onProgress: callbacks.onProgress,
516
- onManualCodeInput: callbacks.onManualCodeInput,
517
- signal: callbacks.signal,
518
- mode
519
- });
520
- },
521
- async refreshToken(credentials) {
522
- return refreshOpenAICodexToken(credentials.refresh, credentials.accountId);
523
- },
524
- getApiKey(credentials) {
525
- return credentials.access;
526
- }
527
- };
528
- export {
529
- OPENAI_CODEX_AUTH_MODES,
530
- __testing,
531
- loginOpenAICodex,
532
- openaiCodexOAuthProvider,
533
- pollCodexDeviceLogin,
534
- refreshOpenAICodexToken,
535
- startCodexDeviceLogin
494
+ id: "openai-codex",
495
+ name: "ChatGPT Plus/Pro (Codex Subscription)",
496
+ usesCallbackServer: true,
497
+ authModes: OPENAI_CODEX_AUTH_MODES,
498
+ async login(callbacks) {
499
+ const mode = callbacks.authMode === "device" || callbacks.authMode === "browser" ? callbacks.authMode : void 0;
500
+ return loginOpenAICodex({
501
+ onAuth: callbacks.onAuth,
502
+ onPrompt: callbacks.onPrompt,
503
+ onProgress: callbacks.onProgress,
504
+ onManualCodeInput: callbacks.onManualCodeInput,
505
+ signal: callbacks.signal,
506
+ mode
507
+ });
508
+ },
509
+ async refreshToken(credentials) {
510
+ return refreshOpenAICodexToken(credentials.refresh, credentials.accountId);
511
+ },
512
+ getApiKey(credentials) {
513
+ return credentials.access;
514
+ }
536
515
  };
516
+ //#endregion
517
+ export { OPENAI_CODEX_AUTH_MODES, __testing, loginOpenAICodex, openaiCodexOAuthProvider, pollCodexDeviceLogin, refreshOpenAICodexToken, startCodexDeviceLogin };
518
+
537
519
  //# sourceMappingURL=openai-codex.js.map