@terminai/core 0.21.0 → 0.25.0

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 (303) hide show
  1. package/dist/docs/assets/image.png +0 -0
  2. package/dist/src/audit/redaction.js +24 -0
  3. package/dist/src/audit/redaction.js.map +1 -1
  4. package/dist/src/audit/redaction.test.d.ts +7 -0
  5. package/dist/src/audit/redaction.test.js +45 -0
  6. package/dist/src/audit/redaction.test.js.map +1 -0
  7. package/dist/src/auth/geminiAuthStatus.js +1 -1
  8. package/dist/src/auth/geminiAuthStatus.js.map +1 -1
  9. package/dist/src/auth/geminiAuthStatus.test.js +1 -1
  10. package/dist/src/auth/geminiAuthStatus.test.js.map +1 -1
  11. package/dist/src/auth/providerRegistry.js +20 -0
  12. package/dist/src/auth/providerRegistry.js.map +1 -1
  13. package/dist/src/auth/wizardSettings.d.ts +5 -0
  14. package/dist/src/auth/wizardSettings.js +48 -1
  15. package/dist/src/auth/wizardSettings.js.map +1 -1
  16. package/dist/src/auth/wizardSettings.test.js +30 -2
  17. package/dist/src/auth/wizardSettings.test.js.map +1 -1
  18. package/dist/src/auth/wizardState.d.ts +1 -1
  19. package/dist/src/auth/wizardState.js +6 -0
  20. package/dist/src/auth/wizardState.js.map +1 -1
  21. package/dist/src/auth/wizardState.test.js +23 -0
  22. package/dist/src/auth/wizardState.test.js.map +1 -1
  23. package/dist/src/availability/policyHelpers.test.js +4 -4
  24. package/dist/src/availability/policyHelpers.test.js.map +1 -1
  25. package/dist/src/brain/__tests__/environmentDetector.test.js +2 -1
  26. package/dist/src/brain/__tests__/environmentDetector.test.js.map +1 -1
  27. package/dist/src/brain/__tests__/orchestratorFlag.test.d.ts +7 -0
  28. package/dist/src/brain/__tests__/orchestratorFlag.test.js +47 -0
  29. package/dist/src/brain/__tests__/orchestratorFlag.test.js.map +1 -0
  30. package/dist/src/brain/__tests__/orchestratorRecovery.test.d.ts +7 -0
  31. package/dist/src/brain/__tests__/orchestratorRecovery.test.js +85 -0
  32. package/dist/src/brain/__tests__/orchestratorRecovery.test.js.map +1 -0
  33. package/dist/src/brain/__tests__/thinkingOrchestrator.test.js +1 -0
  34. package/dist/src/brain/__tests__/thinkingOrchestrator.test.js.map +1 -1
  35. package/dist/src/brain/__tests__/toolIntegration.test.d.ts +7 -0
  36. package/dist/src/brain/__tests__/toolIntegration.test.js +154 -0
  37. package/dist/src/brain/__tests__/toolIntegration.test.js.map +1 -0
  38. package/dist/src/brain/advisors/codeGenerator.js +3 -2
  39. package/dist/src/brain/advisors/codeGenerator.js.map +1 -1
  40. package/dist/src/brain/advisors/depScanner.js +3 -2
  41. package/dist/src/brain/advisors/depScanner.js.map +1 -1
  42. package/dist/src/brain/advisors/enumerator.js +3 -2
  43. package/dist/src/brain/advisors/enumerator.js.map +1 -1
  44. package/dist/src/brain/advisors/fallbackChain.js +3 -2
  45. package/dist/src/brain/advisors/fallbackChain.js.map +1 -1
  46. package/dist/src/brain/advisors/patternMatcher.js +3 -2
  47. package/dist/src/brain/advisors/patternMatcher.js.map +1 -1
  48. package/dist/src/brain/consensus.js +2 -1
  49. package/dist/src/brain/consensus.js.map +1 -1
  50. package/dist/src/brain/environmentDetector.d.ts +1 -0
  51. package/dist/src/brain/environmentDetector.js +35 -20
  52. package/dist/src/brain/environmentDetector.js.map +1 -1
  53. package/dist/src/brain/pacLoop.js +2 -1
  54. package/dist/src/brain/pacLoop.js.map +1 -1
  55. package/dist/src/brain/riskAssessor.d.ts +6 -2
  56. package/dist/src/brain/riskAssessor.js +7 -4
  57. package/dist/src/brain/riskAssessor.js.map +1 -1
  58. package/dist/src/brain/stepBackEvaluator.js +2 -1
  59. package/dist/src/brain/stepBackEvaluator.js.map +1 -1
  60. package/dist/src/brain/thinkingOrchestrator.d.ts +9 -1
  61. package/dist/src/brain/thinkingOrchestrator.js +47 -1
  62. package/dist/src/brain/thinkingOrchestrator.js.map +1 -1
  63. package/dist/src/brain/toolIntegration.d.ts +51 -0
  64. package/dist/src/brain/toolIntegration.js +202 -0
  65. package/dist/src/brain/toolIntegration.js.map +1 -0
  66. package/dist/src/code_assist/oauth2.begin.test.js +4 -1
  67. package/dist/src/code_assist/oauth2.begin.test.js.map +1 -1
  68. package/dist/src/code_assist/oauth2.d.ts +1 -1
  69. package/dist/src/code_assist/oauth2.js +9 -13
  70. package/dist/src/code_assist/oauth2.js.map +1 -1
  71. package/dist/src/code_assist/oauth2.test.js +69 -41
  72. package/dist/src/code_assist/oauth2.test.js.map +1 -1
  73. package/dist/src/commands/init.js +5 -0
  74. package/dist/src/commands/init.js.map +1 -1
  75. package/dist/src/computer/PersistentShell.d.ts +5 -0
  76. package/dist/src/computer/PersistentShell.js +21 -2
  77. package/dist/src/computer/PersistentShell.js.map +1 -1
  78. package/dist/src/computer/PersistentShell.test.js +23 -11
  79. package/dist/src/computer/PersistentShell.test.js.map +1 -1
  80. package/dist/src/config/builder.js +78 -5
  81. package/dist/src/config/builder.js.map +1 -1
  82. package/dist/src/config/config.d.ts +12 -1
  83. package/dist/src/config/config.js +64 -0
  84. package/dist/src/config/config.js.map +1 -1
  85. package/dist/src/config/config.test.js +39 -0
  86. package/dist/src/config/config.test.js.map +1 -1
  87. package/dist/src/config/models.d.ts +2 -2
  88. package/dist/src/config/models.js +2 -2
  89. package/dist/src/config/models.js.map +1 -1
  90. package/dist/src/config/models.test.js +1 -1
  91. package/dist/src/config/settings/schema.d.ts +78 -4
  92. package/dist/src/config/settings/schema.js +76 -4
  93. package/dist/src/config/settings/schema.js.map +1 -1
  94. package/dist/src/core/chatGptCodexContentGenerator.d.ts +35 -0
  95. package/dist/src/core/chatGptCodexContentGenerator.js +605 -0
  96. package/dist/src/core/chatGptCodexContentGenerator.js.map +1 -0
  97. package/dist/src/core/chatGptCodexContentGenerator.test.d.ts +7 -0
  98. package/dist/src/core/chatGptCodexContentGenerator.test.js +250 -0
  99. package/dist/src/core/chatGptCodexContentGenerator.test.js.map +1 -0
  100. package/dist/src/core/contentGenerator.d.ts +2 -1
  101. package/dist/src/core/contentGenerator.js +10 -0
  102. package/dist/src/core/contentGenerator.js.map +1 -1
  103. package/dist/src/core/contentGenerator.test.js +36 -0
  104. package/dist/src/core/contentGenerator.test.js.map +1 -1
  105. package/dist/src/core/loggingContentGenerator.d.ts +1 -0
  106. package/dist/src/core/loggingContentGenerator.js +30 -0
  107. package/dist/src/core/loggingContentGenerator.js.map +1 -1
  108. package/dist/src/core/loggingContentGenerator.test.js +62 -0
  109. package/dist/src/core/loggingContentGenerator.test.js.map +1 -1
  110. package/dist/src/core/openaiContentGenerator.d.ts +16 -0
  111. package/dist/src/core/openaiContentGenerator.js +252 -53
  112. package/dist/src/core/openaiContentGenerator.js.map +1 -1
  113. package/dist/src/core/openaiContentGenerator.test.js +176 -7
  114. package/dist/src/core/openaiContentGenerator.test.js.map +1 -1
  115. package/dist/src/core/providerTypes.d.ts +17 -1
  116. package/dist/src/core/providerTypes.js +10 -0
  117. package/dist/src/core/providerTypes.js.map +1 -1
  118. package/dist/src/core/providerTypes.test.js +11 -0
  119. package/dist/src/core/providerTypes.test.js.map +1 -1
  120. package/dist/src/core/turn.js +1 -1
  121. package/dist/src/core/turn.js.map +1 -1
  122. package/dist/src/core/turn.test.js +1 -1
  123. package/dist/src/core/turn.test.js.map +1 -1
  124. package/dist/src/fallback/handler.test.js +14 -14
  125. package/dist/src/fallback/handler.test.js.map +1 -1
  126. package/dist/src/generated/git-commit.d.ts +2 -2
  127. package/dist/src/generated/git-commit.js +2 -2
  128. package/dist/src/generated/git-commit.js.map +1 -1
  129. package/dist/src/gui/drivers/__tests__/sidecarContract.test.d.ts +2 -2
  130. package/dist/src/gui/drivers/__tests__/sidecarContract.test.js +3 -4
  131. package/dist/src/gui/drivers/__tests__/sidecarContract.test.js.map +1 -1
  132. package/dist/src/gui/drivers/linuxAtspiDriver.js +11 -4
  133. package/dist/src/gui/drivers/linuxAtspiDriver.js.map +1 -1
  134. package/dist/src/gui/drivers/windowsUiaDriver.js +2 -2
  135. package/dist/src/gui/drivers/windowsUiaDriver.js.map +1 -1
  136. package/dist/src/gui/selectors/__tests__/selectors.test.js +5 -2
  137. package/dist/src/gui/selectors/__tests__/selectors.test.js.map +1 -1
  138. package/dist/src/gui/selectors/parser.d.ts +1 -1
  139. package/dist/src/gui/selectors/parser.js +1 -1
  140. package/dist/src/gui/selectors/parser.js.map +1 -1
  141. package/dist/src/gui/service/DesktopAutomationService.d.ts +1 -1
  142. package/dist/src/gui/service/DesktopAutomationService.js +8 -9
  143. package/dist/src/gui/service/DesktopAutomationService.js.map +1 -1
  144. package/dist/src/gui/service/__tests__/capabilities.test.d.ts +6 -0
  145. package/dist/src/gui/service/__tests__/capabilities.test.js +32 -4
  146. package/dist/src/gui/service/__tests__/capabilities.test.js.map +1 -1
  147. package/dist/src/gui/service/__tests__/diagnose.test.d.ts +6 -0
  148. package/dist/src/gui/service/__tests__/diagnose.test.js +6 -0
  149. package/dist/src/gui/service/__tests__/diagnose.test.js.map +1 -1
  150. package/dist/src/gui/service/__tests__/progressive.test.d.ts +6 -0
  151. package/dist/src/gui/service/__tests__/progressive.test.js +30 -3
  152. package/dist/src/gui/service/__tests__/progressive.test.js.map +1 -1
  153. package/dist/src/gui/service/__tests__/redaction.test.d.ts +6 -0
  154. package/dist/src/gui/service/__tests__/redaction.test.js +27 -1
  155. package/dist/src/gui/service/__tests__/redaction.test.js.map +1 -1
  156. package/dist/src/gui/service/__tests__/selectorError.test.d.ts +6 -0
  157. package/dist/src/gui/service/__tests__/selectorError.test.js +6 -0
  158. package/dist/src/gui/service/__tests__/selectorError.test.js.map +1 -1
  159. package/dist/src/gui/service/__tests__/wait.test.d.ts +6 -0
  160. package/dist/src/gui/service/__tests__/wait.test.js +29 -2
  161. package/dist/src/gui/service/__tests__/wait.test.js.map +1 -1
  162. package/dist/src/index.d.ts +6 -0
  163. package/dist/src/index.js +7 -0
  164. package/dist/src/index.js.map +1 -1
  165. package/dist/src/openai_chatgpt/constants.d.ts +23 -0
  166. package/dist/src/openai_chatgpt/constants.js +35 -0
  167. package/dist/src/openai_chatgpt/constants.js.map +1 -0
  168. package/dist/src/openai_chatgpt/credentialStorage.d.ts +18 -0
  169. package/dist/src/openai_chatgpt/credentialStorage.js +116 -0
  170. package/dist/src/openai_chatgpt/credentialStorage.js.map +1 -0
  171. package/dist/src/openai_chatgpt/credentialStorage.test.d.ts +7 -0
  172. package/dist/src/openai_chatgpt/credentialStorage.test.js +73 -0
  173. package/dist/src/openai_chatgpt/credentialStorage.test.js.map +1 -0
  174. package/dist/src/openai_chatgpt/imports.d.ts +10 -0
  175. package/dist/src/openai_chatgpt/imports.js +164 -0
  176. package/dist/src/openai_chatgpt/imports.js.map +1 -0
  177. package/dist/src/openai_chatgpt/imports.test.d.ts +7 -0
  178. package/dist/src/openai_chatgpt/imports.test.js +82 -0
  179. package/dist/src/openai_chatgpt/imports.test.js.map +1 -0
  180. package/dist/src/openai_chatgpt/jwt.d.ts +7 -0
  181. package/dist/src/openai_chatgpt/jwt.js +31 -0
  182. package/dist/src/openai_chatgpt/jwt.js.map +1 -0
  183. package/dist/src/openai_chatgpt/jwt.test.d.ts +7 -0
  184. package/dist/src/openai_chatgpt/jwt.test.js +23 -0
  185. package/dist/src/openai_chatgpt/jwt.test.js.map +1 -0
  186. package/dist/src/openai_chatgpt/oauthClient.d.ts +52 -0
  187. package/dist/src/openai_chatgpt/oauthClient.js +196 -0
  188. package/dist/src/openai_chatgpt/oauthClient.js.map +1 -0
  189. package/dist/src/openai_chatgpt/oauthClient.test.d.ts +7 -0
  190. package/dist/src/openai_chatgpt/oauthClient.test.js +138 -0
  191. package/dist/src/openai_chatgpt/oauthClient.test.js.map +1 -0
  192. package/dist/src/openai_chatgpt/types.d.ts +34 -0
  193. package/dist/src/openai_chatgpt/types.js +8 -0
  194. package/dist/src/openai_chatgpt/types.js.map +1 -0
  195. package/dist/src/policy/shell-safety.test.js +2 -1
  196. package/dist/src/policy/shell-safety.test.js.map +1 -1
  197. package/dist/src/safety/approval-ladder/__tests__/buildShellActionProfile.test.js +2 -1
  198. package/dist/src/safety/approval-ladder/__tests__/buildShellActionProfile.test.js.map +1 -1
  199. package/dist/src/safety/built-in.test.js +4 -2
  200. package/dist/src/safety/built-in.test.js.map +1 -1
  201. package/dist/src/telemetry/metrics.test.js +95 -75
  202. package/dist/src/telemetry/metrics.test.js.map +1 -1
  203. package/dist/src/tools/__tests__/edit.risk.test.d.ts +7 -0
  204. package/dist/src/tools/__tests__/edit.risk.test.js +138 -0
  205. package/dist/src/tools/__tests__/edit.risk.test.js.map +1 -0
  206. package/dist/src/tools/__tests__/memoryToolDedupe.test.d.ts +7 -0
  207. package/dist/src/tools/__tests__/memoryToolDedupe.test.js +53 -0
  208. package/dist/src/tools/__tests__/memoryToolDedupe.test.js.map +1 -0
  209. package/dist/src/tools/__tests__/shell.pin.test.js +2 -1
  210. package/dist/src/tools/__tests__/shell.pin.test.js.map +1 -1
  211. package/dist/src/tools/__tests__/shell.provenance.test.js +14 -4
  212. package/dist/src/tools/__tests__/shell.provenance.test.js.map +1 -1
  213. package/dist/src/tools/__tests__/ui-diagnose.test.d.ts +6 -0
  214. package/dist/src/tools/__tests__/ui-diagnose.test.js +11 -8
  215. package/dist/src/tools/__tests__/ui-diagnose.test.js.map +1 -1
  216. package/dist/src/tools/__tests__/ui-health.test.d.ts +6 -0
  217. package/dist/src/tools/__tests__/ui-health.test.js +37 -37
  218. package/dist/src/tools/__tests__/ui-health.test.js.map +1 -1
  219. package/dist/src/tools/agent-control.js +6 -18
  220. package/dist/src/tools/agent-control.js.map +1 -1
  221. package/dist/src/tools/agent-control.test.js +2 -28
  222. package/dist/src/tools/agent-control.test.js.map +1 -1
  223. package/dist/src/tools/edit.js +49 -9
  224. package/dist/src/tools/edit.js.map +1 -1
  225. package/dist/src/tools/edit.test.js +1 -19
  226. package/dist/src/tools/edit.test.js.map +1 -1
  227. package/dist/src/tools/file-ops.js +9 -10
  228. package/dist/src/tools/file-ops.js.map +1 -1
  229. package/dist/src/tools/file-ops.test.js +0 -7
  230. package/dist/src/tools/file-ops.test.js.map +1 -1
  231. package/dist/src/tools/glob.js +4 -16
  232. package/dist/src/tools/glob.js.map +1 -1
  233. package/dist/src/tools/glob.test.js +0 -29
  234. package/dist/src/tools/glob.test.js.map +1 -1
  235. package/dist/src/tools/grep.js +4 -10
  236. package/dist/src/tools/grep.js.map +1 -1
  237. package/dist/src/tools/ls.d.ts +1 -1
  238. package/dist/src/tools/ls.js +8 -7
  239. package/dist/src/tools/ls.js.map +1 -1
  240. package/dist/src/tools/ls.test.js +0 -7
  241. package/dist/src/tools/ls.test.js.map +1 -1
  242. package/dist/src/tools/mcp-client-manager.js +8 -6
  243. package/dist/src/tools/mcp-client-manager.js.map +1 -1
  244. package/dist/src/tools/mcp-client-manager.test.js +0 -10
  245. package/dist/src/tools/mcp-client-manager.test.js.map +1 -1
  246. package/dist/src/tools/memoryTool.js +10 -0
  247. package/dist/src/tools/memoryTool.js.map +1 -1
  248. package/dist/src/tools/process-manager.js +6 -5
  249. package/dist/src/tools/process-manager.js.map +1 -1
  250. package/dist/src/tools/process-manager.test.js +2 -1
  251. package/dist/src/tools/process-manager.test.js.map +1 -1
  252. package/dist/src/tools/read-file.js +8 -10
  253. package/dist/src/tools/read-file.js.map +1 -1
  254. package/dist/src/tools/read-file.test.js +0 -12
  255. package/dist/src/tools/read-file.test.js.map +1 -1
  256. package/dist/src/tools/read-many-files.js +2 -7
  257. package/dist/src/tools/read-many-files.js.map +1 -1
  258. package/dist/src/tools/repl.js +10 -45
  259. package/dist/src/tools/repl.js.map +1 -1
  260. package/dist/src/tools/ripGrep.js +2 -5
  261. package/dist/src/tools/ripGrep.js.map +1 -1
  262. package/dist/src/tools/ripGrep.test.js +1 -1
  263. package/dist/src/tools/ripGrep.test.js.map +1 -1
  264. package/dist/src/tools/shell.d.ts +2 -11
  265. package/dist/src/tools/shell.js +31 -191
  266. package/dist/src/tools/shell.js.map +1 -1
  267. package/dist/src/tools/shell.test.js +2 -1
  268. package/dist/src/tools/shell.test.js.map +1 -1
  269. package/dist/src/tools/smart-edit.js +2 -5
  270. package/dist/src/tools/smart-edit.js.map +1 -1
  271. package/dist/src/tools/smart-edit.test.js +0 -9
  272. package/dist/src/tools/smart-edit.test.js.map +1 -1
  273. package/dist/src/tools/tools.d.ts +5 -0
  274. package/dist/src/tools/tools.js.map +1 -1
  275. package/dist/src/tools/ui-click.js +69 -3
  276. package/dist/src/tools/ui-click.js.map +1 -1
  277. package/dist/src/tools/write-file.js +34 -8
  278. package/dist/src/tools/write-file.js.map +1 -1
  279. package/dist/src/tools/write-file.test.js +1 -15
  280. package/dist/src/tools/write-file.test.js.map +1 -1
  281. package/dist/src/utils/errorReporting.js +1 -1
  282. package/dist/src/utils/errorReporting.js.map +1 -1
  283. package/dist/src/utils/errorReporting.test.js +1 -1
  284. package/dist/src/utils/errorReporting.test.js.map +1 -1
  285. package/dist/src/utils/events.d.ts +11 -0
  286. package/dist/src/utils/events.js +1 -0
  287. package/dist/src/utils/events.js.map +1 -1
  288. package/dist/src/utils/fileUtils.js +2 -7
  289. package/dist/src/utils/fileUtils.js.map +1 -1
  290. package/dist/src/utils/processUtils.d.ts +43 -0
  291. package/dist/src/utils/processUtils.js +134 -0
  292. package/dist/src/utils/processUtils.js.map +1 -0
  293. package/dist/src/utils/shell-permissions.d.ts +1 -0
  294. package/dist/src/utils/shell-permissions.js +3 -3
  295. package/dist/src/utils/shell-permissions.js.map +1 -1
  296. package/dist/src/utils/shell-permissions.test.js +11 -5
  297. package/dist/src/utils/shell-permissions.test.js.map +1 -1
  298. package/dist/src/utils/shell-utils.js +6 -3
  299. package/dist/src/utils/shell-utils.js.map +1 -1
  300. package/dist/src/utils/shell-utils.test.js +6 -2
  301. package/dist/src/utils/shell-utils.test.js.map +1 -1
  302. package/dist/tsconfig.tsbuildinfo +1 -1
  303. package/package.json +5 -3
@@ -0,0 +1,35 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * Portions Copyright 2025 TerminaI Authors
5
+ * SPDX-License-Identifier: Apache-2.0
6
+ */
7
+ export const OPENAI_CHATGPT_OAUTH_PROVIDER_ID = 'openai_chatgpt_oauth';
8
+ export const OPENAI_CHATGPT_TOKEN_STORAGE_SERVICE = 'terminai-openai-chatgpt';
9
+ export const OPENAI_CHATGPT_TOKEN_STORAGE_SERVER_NAME = 'openai-chatgpt';
10
+ export const OPENAI_CHATGPT_CREDENTIAL_TYPE = 'openai-chatgpt';
11
+ export const DEFAULT_CHATGPT_CODEX_BASE_URL = 'https://chatgpt.com/backend-api/codex';
12
+ export const CHATGPT_CODEX_RESPONSES_PATH = 'responses';
13
+ export const DEFAULT_OPENAI_OAUTH_CLIENT_ID = 'app_EMoamEEZ73f0CkXaXp7hrann';
14
+ export const DEFAULT_OPENAI_OAUTH_AUTHORIZE_URL = 'https://auth.openai.com/oauth/authorize';
15
+ export const DEFAULT_OPENAI_OAUTH_TOKEN_URL = 'https://auth.openai.com/oauth/token';
16
+ export const DEFAULT_OPENAI_OAUTH_REDIRECT_PORT = 1455;
17
+ export const DEFAULT_REFRESH_STALE_MS = 8 * 60 * 1000;
18
+ // Legacy (older plugins/docs) — some JWTs used to expose this as a flat claim.
19
+ export const CHATGPT_ACCOUNT_ID_CLAIM = 'https://api.openai.com/auth.chatgpt_account_id';
20
+ // Codex CLI / OpenAI tokens expose auth details as a nested object claim.
21
+ export const OPENAI_AUTH_CLAIM = 'https://api.openai.com/auth';
22
+ export const OPENAI_AUTH_CHATGPT_ACCOUNT_ID_FIELD = 'chatgpt_account_id';
23
+ export const CODEX_ORIGINATOR = 'codex_cli_rs';
24
+ export const OPENAI_CHATGPT_OAUTH_DISABLE_ENV = 'TERMINAI_DISABLE_OPENAI_CHATGPT_OAUTH';
25
+ export function isOpenAIChatGptOauthProviderDisabled(env = process.env) {
26
+ const raw = env[OPENAI_CHATGPT_OAUTH_DISABLE_ENV];
27
+ if (raw === undefined)
28
+ return false;
29
+ const normalized = raw.trim().toLowerCase();
30
+ return (normalized === '1' ||
31
+ normalized === 'true' ||
32
+ normalized === 'yes' ||
33
+ normalized === 'on');
34
+ }
35
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../src/openai_chatgpt/constants.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,gCAAgC,GAAG,sBAA+B,CAAC;AAEhF,MAAM,CAAC,MAAM,oCAAoC,GAC/C,yBAAkC,CAAC;AACrC,MAAM,CAAC,MAAM,wCAAwC,GACnD,gBAAyB,CAAC;AAC5B,MAAM,CAAC,MAAM,8BAA8B,GAAG,gBAAyB,CAAC;AAExE,MAAM,CAAC,MAAM,8BAA8B,GACzC,uCAAgD,CAAC;AACnD,MAAM,CAAC,MAAM,4BAA4B,GAAG,WAAoB,CAAC;AAEjE,MAAM,CAAC,MAAM,8BAA8B,GACzC,8BAAuC,CAAC;AAC1C,MAAM,CAAC,MAAM,kCAAkC,GAC7C,yCAAkD,CAAC;AACrD,MAAM,CAAC,MAAM,8BAA8B,GACzC,qCAA8C,CAAC;AAEjD,MAAM,CAAC,MAAM,kCAAkC,GAAG,IAAa,CAAC;AAChE,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;AAEtD,+EAA+E;AAC/E,MAAM,CAAC,MAAM,wBAAwB,GACnC,gDAAyD,CAAC;AAE5D,0EAA0E;AAC1E,MAAM,CAAC,MAAM,iBAAiB,GAAG,6BAAsC,CAAC;AACxE,MAAM,CAAC,MAAM,oCAAoC,GAC/C,oBAA6B,CAAC;AAChC,MAAM,CAAC,MAAM,gBAAgB,GAAG,cAAuB,CAAC;AAExD,MAAM,CAAC,MAAM,gCAAgC,GAC3C,uCAAgD,CAAC;AAEnD,MAAM,UAAU,oCAAoC,CAClD,MAAyB,OAAO,CAAC,GAAG;IAEpC,MAAM,GAAG,GAAG,GAAG,CAAC,gCAAgC,CAAC,CAAC;IAClD,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IAEpC,MAAM,UAAU,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC5C,OAAO,CACL,UAAU,KAAK,GAAG;QAClB,UAAU,KAAK,MAAM;QACrB,UAAU,KAAK,KAAK;QACpB,UAAU,KAAK,IAAI,CACpB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * Portions Copyright 2025 TerminaI Authors
5
+ * SPDX-License-Identifier: Apache-2.0
6
+ */
7
+ import type { ChatGptOAuthCredentialPayload, ChatGptOAuthStoredCredentials } from './types.js';
8
+ export declare class ChatGptOAuthCredentialStorage {
9
+ private static storage;
10
+ static load(): Promise<ChatGptOAuthStoredCredentials | null>;
11
+ static save(payload: ChatGptOAuthCredentialPayload): Promise<void>;
12
+ /**
13
+ * Clear stored ChatGPT OAuth credentials.
14
+ * This method is intentionally infallible - it will never throw.
15
+ * This ensures /auth logout always succeeds, even if storage is corrupted.
16
+ */
17
+ static clear(): Promise<void>;
18
+ }
@@ -0,0 +1,116 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * Portions Copyright 2025 TerminaI Authors
5
+ * SPDX-License-Identifier: Apache-2.0
6
+ */
7
+ import { HybridTokenStorage } from '../mcp/token-storage/hybrid-token-storage.js';
8
+ import { coreEvents } from '../utils/events.js';
9
+ import { OPENAI_CHATGPT_CREDENTIAL_TYPE, OPENAI_CHATGPT_TOKEN_STORAGE_SERVER_NAME, OPENAI_CHATGPT_TOKEN_STORAGE_SERVICE, } from './constants.js';
10
+ const MAX_TOKEN_LENGTH = 10_000;
11
+ const MAX_ACCOUNT_ID_LENGTH = 200;
12
+ export class ChatGptOAuthCredentialStorage {
13
+ static storage = new HybridTokenStorage(OPENAI_CHATGPT_TOKEN_STORAGE_SERVICE);
14
+ static async load() {
15
+ try {
16
+ const credentials = await this.storage.getCredentials(OPENAI_CHATGPT_TOKEN_STORAGE_SERVER_NAME);
17
+ if (!credentials)
18
+ return null;
19
+ const parsed = parseStored(credentials);
20
+ return parsed;
21
+ }
22
+ catch (error) {
23
+ coreEvents.emitFeedback('error', 'Failed to load ChatGPT OAuth credentials', error);
24
+ throw new Error('Failed to load ChatGPT OAuth credentials', {
25
+ cause: error,
26
+ });
27
+ }
28
+ }
29
+ static async save(payload) {
30
+ validateToken(payload.token.accessToken, 'accessToken');
31
+ validateToken(payload.token.refreshToken, 'refreshToken');
32
+ if (payload.token.idToken)
33
+ validateToken(payload.token.idToken, 'idToken');
34
+ if (payload.accountId)
35
+ validateAccountId(payload.accountId);
36
+ const creds = {
37
+ serverName: OPENAI_CHATGPT_TOKEN_STORAGE_SERVER_NAME,
38
+ token: {
39
+ accessToken: payload.token.accessToken,
40
+ refreshToken: payload.token.refreshToken,
41
+ tokenType: payload.token.tokenType,
42
+ expiresAt: payload.token.expiresAt,
43
+ scope: payload.token.scope,
44
+ },
45
+ updatedAt: Date.now(),
46
+ credentialType: OPENAI_CHATGPT_CREDENTIAL_TYPE,
47
+ idToken: payload.token.idToken,
48
+ accountId: payload.accountId,
49
+ lastRefresh: payload.lastRefresh,
50
+ };
51
+ await this.storage.setCredentials(creds);
52
+ }
53
+ /**
54
+ * Clear stored ChatGPT OAuth credentials.
55
+ * This method is intentionally infallible - it will never throw.
56
+ * This ensures /auth logout always succeeds, even if storage is corrupted.
57
+ */
58
+ static async clear() {
59
+ // Try primary storage first
60
+ try {
61
+ await this.storage.deleteCredentials(OPENAI_CHATGPT_TOKEN_STORAGE_SERVER_NAME);
62
+ }
63
+ catch (error) {
64
+ // Log but don't throw - user recovery path must not be blocked
65
+ coreEvents.emitFeedback('warning', 'Could not clear ChatGPT OAuth credentials from primary storage', error);
66
+ }
67
+ // Also try clearing via clearAll on the storage as a fallback
68
+ try {
69
+ await this.storage.clearAll();
70
+ }
71
+ catch {
72
+ // Ignore - best effort
73
+ }
74
+ }
75
+ }
76
+ function parseStored(value) {
77
+ const raw = value;
78
+ if (raw.credentialType !== OPENAI_CHATGPT_CREDENTIAL_TYPE) {
79
+ throw new Error('Stored ChatGPT OAuth credentials have wrong credentialType');
80
+ }
81
+ if (!raw.token?.accessToken || typeof raw.token.accessToken !== 'string') {
82
+ throw new Error('Stored ChatGPT OAuth credentials missing accessToken');
83
+ }
84
+ if (!raw.token.refreshToken || typeof raw.token.refreshToken !== 'string') {
85
+ throw new Error('Stored ChatGPT OAuth credentials missing refreshToken');
86
+ }
87
+ if (raw.idToken !== undefined && typeof raw.idToken !== 'string') {
88
+ throw new Error('Stored ChatGPT OAuth credentials have invalid idToken');
89
+ }
90
+ if (raw.accountId !== undefined && typeof raw.accountId !== 'string') {
91
+ throw new Error('Stored ChatGPT OAuth credentials have invalid accountId');
92
+ }
93
+ if (raw.lastRefresh !== undefined && typeof raw.lastRefresh !== 'number') {
94
+ throw new Error('Stored ChatGPT OAuth credentials have invalid lastRefresh');
95
+ }
96
+ return raw;
97
+ }
98
+ function validateToken(value, field) {
99
+ const trimmed = value.trim();
100
+ if (trimmed.length === 0) {
101
+ throw new Error(`ChatGPT OAuth ${field} is missing/empty`);
102
+ }
103
+ if (trimmed.length > MAX_TOKEN_LENGTH) {
104
+ throw new Error(`ChatGPT OAuth ${field} is too long`);
105
+ }
106
+ }
107
+ function validateAccountId(value) {
108
+ const trimmed = value.trim();
109
+ if (trimmed.length === 0) {
110
+ throw new Error('ChatGPT OAuth accountId is missing/empty');
111
+ }
112
+ if (trimmed.length > MAX_ACCOUNT_ID_LENGTH) {
113
+ throw new Error('ChatGPT OAuth accountId is too long');
114
+ }
115
+ }
116
+ //# sourceMappingURL=credentialStorage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentialStorage.js","sourceRoot":"","sources":["../../../src/openai_chatgpt/credentialStorage.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * Portions Copyright 2025 TerminaI Authors
5
+ * SPDX-License-Identifier: Apache-2.0
6
+ */
7
+ export {};
@@ -0,0 +1,73 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * Portions Copyright 2025 TerminaI Authors
5
+ * SPDX-License-Identifier: Apache-2.0
6
+ */
7
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
8
+ import { OPENAI_CHATGPT_CREDENTIAL_TYPE, OPENAI_CHATGPT_TOKEN_STORAGE_SERVER_NAME, } from './constants.js';
9
+ const mockHybridTokenStorage = vi.hoisted(() => ({
10
+ getCredentials: vi.fn(),
11
+ setCredentials: vi.fn(),
12
+ deleteCredentials: vi.fn(),
13
+ }));
14
+ vi.mock('../mcp/token-storage/hybrid-token-storage.js', () => ({
15
+ HybridTokenStorage: vi.fn(() => mockHybridTokenStorage),
16
+ }));
17
+ import { ChatGptOAuthCredentialStorage } from './credentialStorage.js';
18
+ describe('ChatGptOAuthCredentialStorage', () => {
19
+ beforeEach(() => {
20
+ vi.clearAllMocks();
21
+ });
22
+ it('saves credentials with discriminator', async () => {
23
+ await ChatGptOAuthCredentialStorage.save({
24
+ token: {
25
+ accessToken: 'access',
26
+ refreshToken: 'refresh',
27
+ tokenType: 'Bearer',
28
+ idToken: 'id',
29
+ },
30
+ accountId: 'acct',
31
+ lastRefresh: 123,
32
+ });
33
+ expect(mockHybridTokenStorage.setCredentials).toHaveBeenCalledWith(expect.objectContaining({
34
+ serverName: OPENAI_CHATGPT_TOKEN_STORAGE_SERVER_NAME,
35
+ credentialType: OPENAI_CHATGPT_CREDENTIAL_TYPE,
36
+ accountId: 'acct',
37
+ lastRefresh: 123,
38
+ token: expect.objectContaining({
39
+ accessToken: 'access',
40
+ refreshToken: 'refresh',
41
+ tokenType: 'Bearer',
42
+ }),
43
+ }));
44
+ });
45
+ it('loads and validates discriminator', async () => {
46
+ mockHybridTokenStorage.getCredentials.mockResolvedValue({
47
+ serverName: OPENAI_CHATGPT_TOKEN_STORAGE_SERVER_NAME,
48
+ updatedAt: 1,
49
+ credentialType: OPENAI_CHATGPT_CREDENTIAL_TYPE,
50
+ token: {
51
+ accessToken: 'access',
52
+ refreshToken: 'refresh',
53
+ tokenType: 'Bearer',
54
+ },
55
+ });
56
+ const creds = await ChatGptOAuthCredentialStorage.load();
57
+ expect(creds?.token.accessToken).toBe('access');
58
+ });
59
+ it('throws if discriminator mismatches', async () => {
60
+ mockHybridTokenStorage.getCredentials.mockResolvedValue({
61
+ serverName: OPENAI_CHATGPT_TOKEN_STORAGE_SERVER_NAME,
62
+ updatedAt: 1,
63
+ credentialType: 'other',
64
+ token: {
65
+ accessToken: 'access',
66
+ refreshToken: 'refresh',
67
+ tokenType: 'Bearer',
68
+ },
69
+ });
70
+ await expect(ChatGptOAuthCredentialStorage.load()).rejects.toThrow('Failed to load ChatGPT OAuth credentials');
71
+ });
72
+ });
73
+ //# sourceMappingURL=credentialStorage.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentialStorage.test.js","sourceRoot":"","sources":["../../../src/openai_chatgpt/credentialStorage.test.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAC9D,OAAO,EACL,8BAA8B,EAC9B,wCAAwC,GACzC,MAAM,gBAAgB,CAAC;AAExB,MAAM,sBAAsB,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IAC/C,cAAc,EAAE,EAAE,CAAC,EAAE,EAAE;IACvB,cAAc,EAAE,EAAE,CAAC,EAAE,EAAE;IACvB,iBAAiB,EAAE,EAAE,CAAC,EAAE,EAAE;CAC3B,CAAC,CAAC,CAAC;AAEJ,EAAE,CAAC,IAAI,CAAC,8CAA8C,EAAE,GAAG,EAAE,CAAC,CAAC;IAC7D,kBAAkB,EAAE,EAAE,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,sBAAsB,CAAC;CACxD,CAAC,CAAC,CAAC;AAEJ,OAAO,EAAE,6BAA6B,EAAE,MAAM,wBAAwB,CAAC;AAEvE,QAAQ,CAAC,+BAA+B,EAAE,GAAG,EAAE;IAC7C,UAAU,CAAC,GAAG,EAAE;QACd,EAAE,CAAC,aAAa,EAAE,CAAC;IACrB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sCAAsC,EAAE,KAAK,IAAI,EAAE;QACpD,MAAM,6BAA6B,CAAC,IAAI,CAAC;YACvC,KAAK,EAAE;gBACL,WAAW,EAAE,QAAQ;gBACrB,YAAY,EAAE,SAAS;gBACvB,SAAS,EAAE,QAAQ;gBACnB,OAAO,EAAE,IAAI;aACd;YACD,SAAS,EAAE,MAAM;YACjB,WAAW,EAAE,GAAG;SACjB,CAAC,CAAC;QAEH,MAAM,CAAC,sBAAsB,CAAC,cAAc,CAAC,CAAC,oBAAoB,CAChE,MAAM,CAAC,gBAAgB,CAAC;YACtB,UAAU,EAAE,wCAAwC;YACpD,cAAc,EAAE,8BAA8B;YAC9C,SAAS,EAAE,MAAM;YACjB,WAAW,EAAE,GAAG;YAChB,KAAK,EAAE,MAAM,CAAC,gBAAgB,CAAC;gBAC7B,WAAW,EAAE,QAAQ;gBACrB,YAAY,EAAE,SAAS;gBACvB,SAAS,EAAE,QAAQ;aACpB,CAAC;SACH,CAAC,CACH,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mCAAmC,EAAE,KAAK,IAAI,EAAE;QACjD,sBAAsB,CAAC,cAAc,CAAC,iBAAiB,CAAC;YACtD,UAAU,EAAE,wCAAwC;YACpD,SAAS,EAAE,CAAC;YACZ,cAAc,EAAE,8BAA8B;YAC9C,KAAK,EAAE;gBACL,WAAW,EAAE,QAAQ;gBACrB,YAAY,EAAE,SAAS;gBACvB,SAAS,EAAE,QAAQ;aACpB;SACF,CAAC,CAAC;QAEH,MAAM,KAAK,GAAG,MAAM,6BAA6B,CAAC,IAAI,EAAE,CAAC;QACzD,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oCAAoC,EAAE,KAAK,IAAI,EAAE;QAClD,sBAAsB,CAAC,cAAc,CAAC,iBAAiB,CAAC;YACtD,UAAU,EAAE,wCAAwC;YACpD,SAAS,EAAE,CAAC;YACZ,cAAc,EAAE,OAAO;YACvB,KAAK,EAAE;gBACL,WAAW,EAAE,QAAQ;gBACrB,YAAY,EAAE,SAAS;gBACvB,SAAS,EAAE,QAAQ;aACpB;SACF,CAAC,CAAC;QAEH,MAAM,MAAM,CAAC,6BAA6B,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAChE,0CAA0C,CAC3C,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * Portions Copyright 2025 TerminaI Authors
5
+ * SPDX-License-Identifier: Apache-2.0
6
+ */
7
+ import type { ChatGptOAuthCredentialPayload } from './types.js';
8
+ import { ChatGptOAuthClient } from './oauthClient.js';
9
+ export declare function tryImportFromCodexCli(client?: ChatGptOAuthClient): Promise<ChatGptOAuthCredentialPayload | null>;
10
+ export declare function tryImportFromOpenCode(client?: ChatGptOAuthClient): Promise<ChatGptOAuthCredentialPayload | null>;
@@ -0,0 +1,164 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * Portions Copyright 2025 TerminaI Authors
5
+ * SPDX-License-Identifier: Apache-2.0
6
+ */
7
+ import * as os from 'node:os';
8
+ import * as path from 'node:path';
9
+ import { promises as fs } from 'node:fs';
10
+ import { ChatGptOAuthClient } from './oauthClient.js';
11
+ const MAX_FILE_BYTES = 1024 * 1024;
12
+ const MAX_TOKEN_LENGTH = 10_000;
13
+ const MAX_ACCOUNT_ID_LENGTH = 200;
14
+ export async function tryImportFromCodexCli(client = new ChatGptOAuthClient()) {
15
+ const codexHome = resolveCodexHome();
16
+ const authPath = path.join(codexHome, 'auth.json');
17
+ const raw = await readJsonFile(authPath).catch((e) => {
18
+ if (isEnoent(e))
19
+ return null;
20
+ throw e;
21
+ });
22
+ if (!raw)
23
+ return null;
24
+ const parsed = parseCodexAuthJson(raw);
25
+ if (!parsed)
26
+ return null;
27
+ const accountId = client.deriveAccountId({
28
+ accountId: parsed.accountId,
29
+ idToken: parsed.idToken,
30
+ accessToken: parsed.accessToken,
31
+ });
32
+ return {
33
+ token: {
34
+ accessToken: parsed.accessToken,
35
+ refreshToken: parsed.refreshToken,
36
+ tokenType: 'Bearer',
37
+ idToken: parsed.idToken,
38
+ expiresAt: undefined,
39
+ scope: undefined,
40
+ },
41
+ accountId,
42
+ lastRefresh: parsed.lastRefresh,
43
+ };
44
+ }
45
+ export async function tryImportFromOpenCode(client = new ChatGptOAuthClient()) {
46
+ const authPath = path.join(os.homedir(), '.opencode', 'auth', 'openai.json');
47
+ const raw = await readJsonFile(authPath).catch((e) => {
48
+ if (isEnoent(e))
49
+ return null;
50
+ throw e;
51
+ });
52
+ if (!raw)
53
+ return null;
54
+ const parsed = parseOpenCodeAuthJson(raw);
55
+ if (!parsed)
56
+ return null;
57
+ const accountId = client.deriveAccountId({
58
+ idToken: parsed.idToken,
59
+ accessToken: parsed.accessToken,
60
+ });
61
+ return {
62
+ token: {
63
+ accessToken: parsed.accessToken,
64
+ refreshToken: parsed.refreshToken,
65
+ tokenType: 'Bearer',
66
+ idToken: parsed.idToken,
67
+ expiresAt: parsed.expiresAt,
68
+ scope: undefined,
69
+ },
70
+ accountId,
71
+ lastRefresh: Date.now(),
72
+ };
73
+ }
74
+ function resolveCodexHome() {
75
+ const fromEnv = process.env['CODEX_HOME'];
76
+ if (typeof fromEnv === 'string' && fromEnv.trim().length > 0) {
77
+ return fromEnv.trim();
78
+ }
79
+ return path.join(os.homedir(), '.codex');
80
+ }
81
+ async function readJsonFile(filePath) {
82
+ const stat = await fs.stat(filePath);
83
+ if (stat.size > MAX_FILE_BYTES) {
84
+ throw new Error(`Refusing to read oversized auth file: ${filePath}`);
85
+ }
86
+ const text = await fs.readFile(filePath, 'utf8');
87
+ return JSON.parse(text);
88
+ }
89
+ function parseCodexAuthJson(value) {
90
+ if (!isPlainObject(value))
91
+ return null;
92
+ const tokens = value['tokens'];
93
+ if (!isPlainObject(tokens))
94
+ return null;
95
+ const access_token = tokens['access_token'];
96
+ const refresh_token = tokens['refresh_token'];
97
+ const id_token = tokens['id_token'];
98
+ const account_id = tokens['account_id'];
99
+ const last_refresh = value['last_refresh'];
100
+ if (!isStringWithin(access_token, MAX_TOKEN_LENGTH))
101
+ return null;
102
+ if (!isStringWithin(refresh_token, MAX_TOKEN_LENGTH))
103
+ return null;
104
+ const result = {
105
+ accessToken: access_token.trim(),
106
+ refreshToken: refresh_token.trim(),
107
+ };
108
+ if (isStringWithin(id_token, MAX_TOKEN_LENGTH)) {
109
+ result.idToken = id_token.trim();
110
+ }
111
+ if (isStringWithin(account_id, MAX_ACCOUNT_ID_LENGTH)) {
112
+ result.accountId = account_id.trim();
113
+ }
114
+ if (typeof last_refresh === 'number' && Number.isFinite(last_refresh)) {
115
+ result.lastRefresh = last_refresh;
116
+ }
117
+ return result;
118
+ }
119
+ function parseOpenCodeAuthJson(value) {
120
+ if (!isPlainObject(value))
121
+ return null;
122
+ // Supported shapes:
123
+ // 1) { type: "oauth", access: "...", refresh: "...", expires?: number }
124
+ // 2) { access_token: "...", refresh_token: "...", id_token?: "...", expires_at?: number }
125
+ const type = value['type'];
126
+ if (type !== undefined && type !== 'oauth') {
127
+ return null;
128
+ }
129
+ const access = value['access'] ?? value['access_token'];
130
+ const refresh = value['refresh'] ?? value['refresh_token'];
131
+ const idToken = value['id_token'];
132
+ const expires = value['expires'] ?? value['expires_at'] ?? value['expiresAt'];
133
+ if (!isStringWithin(access, MAX_TOKEN_LENGTH))
134
+ return null;
135
+ if (!isStringWithin(refresh, MAX_TOKEN_LENGTH))
136
+ return null;
137
+ const parsed = {
138
+ accessToken: access.trim(),
139
+ refreshToken: refresh.trim(),
140
+ };
141
+ if (isStringWithin(idToken, MAX_TOKEN_LENGTH)) {
142
+ parsed.idToken = idToken.trim();
143
+ }
144
+ if (typeof expires === 'number' && Number.isFinite(expires)) {
145
+ // OpenCode may store seconds, ms, or epoch; we accept epoch ms only if it looks plausible.
146
+ parsed.expiresAt = expires > 10_000_000_000 ? expires : expires * 1000;
147
+ }
148
+ return parsed;
149
+ }
150
+ function isPlainObject(value) {
151
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
152
+ }
153
+ function isStringWithin(value, maxLen) {
154
+ return (typeof value === 'string' &&
155
+ value.trim().length > 0 &&
156
+ value.length <= maxLen);
157
+ }
158
+ function isEnoent(error) {
159
+ return (typeof error === 'object' &&
160
+ error !== null &&
161
+ 'code' in error &&
162
+ error.code === 'ENOENT');
163
+ }
164
+ //# sourceMappingURL=imports.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"imports.js","sourceRoot":"","sources":["../../../src/openai_chatgpt/imports.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AAEzC,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAEtD,MAAM,cAAc,GAAG,IAAI,GAAG,IAAI,CAAC;AACnC,MAAM,gBAAgB,GAAG,MAAM,CAAC;AAChC,MAAM,qBAAqB,GAAG,GAAG,CAAC;AAElC,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,SAA6B,IAAI,kBAAkB,EAAE;IAErD,MAAM,SAAS,GAAG,gBAAgB,EAAE,CAAC;IACrC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;IAEnD,MAAM,GAAG,GAAG,MAAM,YAAY,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,CAAC,CAAU,EAAE,EAAE;QAC5D,IAAI,QAAQ,CAAC,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC;QAC7B,MAAM,CAAC,CAAC;IACV,CAAC,CAAC,CAAC;IACH,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IAEtB,MAAM,MAAM,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAC;IACvC,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IAEzB,MAAM,SAAS,GAAG,MAAM,CAAC,eAAe,CAAC;QACvC,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,WAAW,EAAE,MAAM,CAAC,WAAW;KAChC,CAAC,CAAC;IAEH,OAAO;QACL,KAAK,EAAE;YACL,WAAW,EAAE,MAAM,CAAC,WAAW;YAC/B,YAAY,EAAE,MAAM,CAAC,YAAY;YACjC,SAAS,EAAE,QAAQ;YACnB,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,SAAS,EAAE,SAAS;YACpB,KAAK,EAAE,SAAS;SACjB;QACD,SAAS;QACT,WAAW,EAAE,MAAM,CAAC,WAAW;KAChC,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,SAA6B,IAAI,kBAAkB,EAAE;IAErD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC;IAC7E,MAAM,GAAG,GAAG,MAAM,YAAY,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,CAAC,CAAU,EAAE,EAAE;QAC5D,IAAI,QAAQ,CAAC,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC;QAC7B,MAAM,CAAC,CAAC;IACV,CAAC,CAAC,CAAC;IACH,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IAEtB,MAAM,MAAM,GAAG,qBAAqB,CAAC,GAAG,CAAC,CAAC;IAC1C,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IAEzB,MAAM,SAAS,GAAG,MAAM,CAAC,eAAe,CAAC;QACvC,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,WAAW,EAAE,MAAM,CAAC,WAAW;KAChC,CAAC,CAAC;IAEH,OAAO;QACL,KAAK,EAAE;YACL,WAAW,EAAE,MAAM,CAAC,WAAW;YAC/B,YAAY,EAAE,MAAM,CAAC,YAAY;YACjC,SAAS,EAAE,QAAQ;YACnB,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,SAAS,EAAE,MAAM,CAAC,SAAS;YAC3B,KAAK,EAAE,SAAS;SACjB;QACD,SAAS;QACT,WAAW,EAAE,IAAI,CAAC,GAAG,EAAE;KACxB,CAAC;AACJ,CAAC;AAED,SAAS,gBAAgB;IACvB,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IAC1C,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC7D,OAAO,OAAO,CAAC,IAAI,EAAE,CAAC;IACxB,CAAC;IACD,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC,CAAC;AAC3C,CAAC;AAED,KAAK,UAAU,YAAY,CAAC,QAAgB;IAC1C,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACrC,IAAI,IAAI,CAAC,IAAI,GAAG,cAAc,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CAAC,yCAAyC,QAAQ,EAAE,CAAC,CAAC;IACvE,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACjD,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAY,CAAC;AACrC,CAAC;AAED,SAAS,kBAAkB,CAAC,KAAc;IAOxC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACvC,MAAM,MAAM,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC/B,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC;QAAE,OAAO,IAAI,CAAC;IAExC,MAAM,YAAY,GAAG,MAAM,CAAC,cAAc,CAAC,CAAC;IAC5C,MAAM,aAAa,GAAG,MAAM,CAAC,eAAe,CAAC,CAAC;IAC9C,MAAM,QAAQ,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;IACpC,MAAM,UAAU,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC;IACxC,MAAM,YAAY,GAAG,KAAK,CAAC,cAAc,CAAC,CAAC;IAE3C,IAAI,CAAC,cAAc,CAAC,YAAY,EAAE,gBAAgB,CAAC;QAAE,OAAO,IAAI,CAAC;IACjE,IAAI,CAAC,cAAc,CAAC,aAAa,EAAE,gBAAgB,CAAC;QAAE,OAAO,IAAI,CAAC;IAElE,MAAM,MAAM,GAMR;QACF,WAAW,EAAE,YAAY,CAAC,IAAI,EAAE;QAChC,YAAY,EAAE,aAAa,CAAC,IAAI,EAAE;KACnC,CAAC;IAEF,IAAI,cAAc,CAAC,QAAQ,EAAE,gBAAgB,CAAC,EAAE,CAAC;QAC/C,MAAM,CAAC,OAAO,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC;IACnC,CAAC;IACD,IAAI,cAAc,CAAC,UAAU,EAAE,qBAAqB,CAAC,EAAE,CAAC;QACtD,MAAM,CAAC,SAAS,GAAG,UAAU,CAAC,IAAI,EAAE,CAAC;IACvC,CAAC;IACD,IAAI,OAAO,YAAY,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;QACtE,MAAM,CAAC,WAAW,GAAG,YAAY,CAAC;IACpC,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,qBAAqB,CAAC,KAAc;IAM3C,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAEvC,oBAAoB;IACpB,wEAAwE;IACxE,0FAA0F;IAE1F,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC;IAC3B,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;QAC3C,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,MAAM,GAAG,KAAK,CAAC,QAAQ,CAAC,IAAI,KAAK,CAAC,cAAc,CAAC,CAAC;IACxD,MAAM,OAAO,GAAG,KAAK,CAAC,SAAS,CAAC,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;IAC3D,MAAM,OAAO,GAAG,KAAK,CAAC,UAAU,CAAC,CAAC;IAClC,MAAM,OAAO,GAAG,KAAK,CAAC,SAAS,CAAC,IAAI,KAAK,CAAC,YAAY,CAAC,IAAI,KAAK,CAAC,WAAW,CAAC,CAAC;IAE9E,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,gBAAgB,CAAC;QAAE,OAAO,IAAI,CAAC;IAC3D,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,gBAAgB,CAAC;QAAE,OAAO,IAAI,CAAC;IAE5D,MAAM,MAAM,GAKR;QACF,WAAW,EAAE,MAAM,CAAC,IAAI,EAAE;QAC1B,YAAY,EAAE,OAAO,CAAC,IAAI,EAAE;KAC7B,CAAC;IAEF,IAAI,cAAc,CAAC,OAAO,EAAE,gBAAgB,CAAC,EAAE,CAAC;QAC9C,MAAM,CAAC,OAAO,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IAClC,CAAC;IACD,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QAC5D,2FAA2F;QAC3F,MAAM,CAAC,SAAS,GAAG,OAAO,GAAG,cAAc,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,GAAG,IAAI,CAAC;IACzE,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,aAAa,CAAC,KAAc;IACnC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC;AAED,SAAS,cAAc,CAAC,KAAc,EAAE,MAAc;IACpD,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC;QACvB,KAAK,CAAC,MAAM,IAAI,MAAM,CACvB,CAAC;AACJ,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACd,MAAM,IAAI,KAAK;QACf,KAAK,CAAC,IAAI,KAAK,QAAQ,CACxB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * Portions Copyright 2025 TerminaI Authors
5
+ * SPDX-License-Identifier: Apache-2.0
6
+ */
7
+ export {};
@@ -0,0 +1,82 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * Portions Copyright 2025 TerminaI Authors
5
+ * SPDX-License-Identifier: Apache-2.0
6
+ */
7
+ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
8
+ import * as os from 'node:os';
9
+ import * as path from 'node:path';
10
+ import { promises as fs } from 'node:fs';
11
+ import { tryImportFromCodexCli, tryImportFromOpenCode } from './imports.js';
12
+ import { ChatGptOAuthClient } from './oauthClient.js';
13
+ import { CHATGPT_ACCOUNT_ID_CLAIM, OPENAI_AUTH_CLAIM } from './constants.js';
14
+ function jwt(payload) {
15
+ const header = Buffer.from(JSON.stringify({ alg: 'none' })).toString('base64url');
16
+ const body = Buffer.from(JSON.stringify(payload)).toString('base64url');
17
+ return `${header}.${body}.`;
18
+ }
19
+ describe('imports', () => {
20
+ const tmpRoot = path.join(os.tmpdir(), `terminai-chatgpt-oauth-${Date.now()}`);
21
+ beforeEach(async () => {
22
+ await fs.mkdir(tmpRoot, { recursive: true });
23
+ vi.unstubAllEnvs();
24
+ });
25
+ afterEach(async () => {
26
+ await fs.rm(tmpRoot, { recursive: true, force: true });
27
+ });
28
+ it('imports from CODEX_HOME/auth.json', async () => {
29
+ const codexHome = path.join(tmpRoot, 'codex');
30
+ await fs.mkdir(codexHome, { recursive: true });
31
+ await fs.writeFile(path.join(codexHome, 'auth.json'), JSON.stringify({
32
+ tokens: {
33
+ access_token: jwt({
34
+ [OPENAI_AUTH_CLAIM]: { chatgpt_account_id: 'acct_from_access' },
35
+ }),
36
+ refresh_token: 'refresh123',
37
+ id_token: jwt({
38
+ [OPENAI_AUTH_CLAIM]: { chatgpt_account_id: 'acct_from_id' },
39
+ }),
40
+ account_id: 'acct_stored',
41
+ },
42
+ last_refresh: 123456,
43
+ }), 'utf8');
44
+ vi.stubEnv('CODEX_HOME', codexHome);
45
+ const payload = await tryImportFromCodexCli(new ChatGptOAuthClient());
46
+ expect(payload?.token.accessToken).toBeTruthy();
47
+ expect(payload?.token.refreshToken).toBe('refresh123');
48
+ // Stored account_id wins
49
+ expect(payload?.accountId).toBe('acct_stored');
50
+ expect(payload?.lastRefresh).toBe(123456);
51
+ });
52
+ it('imports from ~/.opencode/auth/openai.json under HOME', async () => {
53
+ const home = path.join(tmpRoot, 'home');
54
+ await fs.mkdir(path.join(home, '.opencode', 'auth'), { recursive: true });
55
+ await fs.writeFile(path.join(home, '.opencode', 'auth', 'openai.json'), JSON.stringify({
56
+ type: 'oauth',
57
+ access: jwt({
58
+ [OPENAI_AUTH_CLAIM]: { chatgpt_account_id: 'acct_1' },
59
+ }),
60
+ refresh: 'refresh_1',
61
+ expires: 123,
62
+ }), 'utf8');
63
+ vi.stubEnv('HOME', home);
64
+ const payload = await tryImportFromOpenCode(new ChatGptOAuthClient());
65
+ expect(payload?.token.refreshToken).toBe('refresh_1');
66
+ expect(payload?.accountId).toBe('acct_1');
67
+ });
68
+ it('imports legacy flat claim for backwards compatibility', async () => {
69
+ const home = path.join(tmpRoot, 'home-legacy');
70
+ await fs.mkdir(path.join(home, '.opencode', 'auth'), { recursive: true });
71
+ await fs.writeFile(path.join(home, '.opencode', 'auth', 'openai.json'), JSON.stringify({
72
+ type: 'oauth',
73
+ access: jwt({ [CHATGPT_ACCOUNT_ID_CLAIM]: 'acct_legacy' }),
74
+ refresh: 'refresh_legacy',
75
+ }), 'utf8');
76
+ vi.stubEnv('HOME', home);
77
+ const payload = await tryImportFromOpenCode(new ChatGptOAuthClient());
78
+ expect(payload?.token.refreshToken).toBe('refresh_legacy');
79
+ expect(payload?.accountId).toBe('acct_legacy');
80
+ });
81
+ });
82
+ //# sourceMappingURL=imports.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"imports.test.js","sourceRoot":"","sources":["../../../src/openai_chatgpt/imports.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * Portions Copyright 2025 TerminaI Authors
5
+ * SPDX-License-Identifier: Apache-2.0
6
+ */
7
+ export declare function decodeJwtPayload(token: string): Record<string, unknown>;
@@ -0,0 +1,31 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * Portions Copyright 2025 TerminaI Authors
5
+ * SPDX-License-Identifier: Apache-2.0
6
+ */
7
+ export function decodeJwtPayload(token) {
8
+ const parts = token.split('.');
9
+ if (parts.length < 2) {
10
+ throw new Error('Invalid JWT: missing payload');
11
+ }
12
+ const payloadB64Url = parts[1];
13
+ const json = Buffer.from(base64UrlToBase64(payloadB64Url), 'base64').toString('utf8');
14
+ const parsed = JSON.parse(json);
15
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
16
+ throw new Error('Invalid JWT: payload is not an object');
17
+ }
18
+ return parsed;
19
+ }
20
+ function base64UrlToBase64(value) {
21
+ const normalized = value.replace(/-/g, '+').replace(/_/g, '/');
22
+ const padding = normalized.length % 4;
23
+ if (padding === 0)
24
+ return normalized;
25
+ if (padding === 2)
26
+ return normalized + '==';
27
+ if (padding === 3)
28
+ return normalized + '=';
29
+ throw new Error('Invalid base64url string');
30
+ }
31
+ //# sourceMappingURL=jwt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jwt.js","sourceRoot":"","sources":["../../../src/openai_chatgpt/jwt.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC/B,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;IAClD,CAAC;IAED,MAAM,aAAa,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAC/B,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,aAAa,CAAC,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAC3E,MAAM,CACP,CAAC;IACF,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAY,CAAC;IAC3C,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACnE,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;IAC3D,CAAC;IACD,OAAO,MAAiC,CAAC;AAC3C,CAAC;AAED,SAAS,iBAAiB,CAAC,KAAa;IACtC,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IAC/D,MAAM,OAAO,GAAG,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC;IACtC,IAAI,OAAO,KAAK,CAAC;QAAE,OAAO,UAAU,CAAC;IACrC,IAAI,OAAO,KAAK,CAAC;QAAE,OAAO,UAAU,GAAG,IAAI,CAAC;IAC5C,IAAI,OAAO,KAAK,CAAC;QAAE,OAAO,UAAU,GAAG,GAAG,CAAC;IAC3C,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;AAC9C,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * Portions Copyright 2025 TerminaI Authors
5
+ * SPDX-License-Identifier: Apache-2.0
6
+ */
7
+ export {};
@@ -0,0 +1,23 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * Portions Copyright 2025 TerminaI Authors
5
+ * SPDX-License-Identifier: Apache-2.0
6
+ */
7
+ import { describe, it, expect } from 'vitest';
8
+ import { decodeJwtPayload } from './jwt.js';
9
+ function jwt(payload) {
10
+ const header = Buffer.from(JSON.stringify({ alg: 'none' })).toString('base64url');
11
+ const body = Buffer.from(JSON.stringify(payload)).toString('base64url');
12
+ return `${header}.${body}.`;
13
+ }
14
+ describe('decodeJwtPayload', () => {
15
+ it('decodes base64url JSON payload', () => {
16
+ const token = jwt({ sub: 'user', foo: 'bar' });
17
+ expect(decodeJwtPayload(token)).toEqual({ sub: 'user', foo: 'bar' });
18
+ });
19
+ it('throws for invalid token', () => {
20
+ expect(() => decodeJwtPayload('not-a-jwt')).toThrow('Invalid JWT');
21
+ });
22
+ });
23
+ //# sourceMappingURL=jwt.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jwt.test.js","sourceRoot":"","sources":["../../../src/openai_chatgpt/jwt.test.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAE5C,SAAS,GAAG,CAAC,OAAgC;IAC3C,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,QAAQ,CAClE,WAAW,CACZ,CAAC;IACF,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IACxE,OAAO,GAAG,MAAM,IAAI,IAAI,GAAG,CAAC;AAC9B,CAAC;AAED,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;IAChC,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;QACxC,MAAM,KAAK,GAAG,GAAG,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC;QAC/C,MAAM,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC;IACvE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0BAA0B,EAAE,GAAG,EAAE;QAClC,MAAM,CAAC,GAAG,EAAE,CAAC,gBAAgB,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}