@jonsoc/console-app 1.1.34

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 (217) hide show
  1. package/.opencode/agent/css.md +149 -0
  2. package/README.md +32 -0
  3. package/package.json +49 -0
  4. package/public/apple-touch-icon-v3.png +1 -0
  5. package/public/apple-touch-icon.png +1 -0
  6. package/public/email +1 -0
  7. package/public/favicon-96x96-v3.png +1 -0
  8. package/public/favicon-96x96.png +1 -0
  9. package/public/favicon-v3.ico +1 -0
  10. package/public/favicon-v3.svg +1 -0
  11. package/public/favicon.ico +1 -0
  12. package/public/favicon.svg +1 -0
  13. package/public/opencode-brand-assets.zip +0 -0
  14. package/public/robots.txt +6 -0
  15. package/public/site.webmanifest +1 -0
  16. package/public/social-share-black.png +1 -0
  17. package/public/social-share-zen.png +1 -0
  18. package/public/social-share.png +1 -0
  19. package/public/theme.json +182 -0
  20. package/public/web-app-manifest-192x192.png +1 -0
  21. package/public/web-app-manifest-512x512.png +1 -0
  22. package/script/generate-sitemap.ts +103 -0
  23. package/src/app.css +1 -0
  24. package/src/app.tsx +27 -0
  25. package/src/asset/black/hero.png +0 -0
  26. package/src/asset/brand/opencode-brand-assets.zip +0 -0
  27. package/src/asset/brand/opencode-logo-dark.png +0 -0
  28. package/src/asset/brand/opencode-logo-dark.svg +16 -0
  29. package/src/asset/brand/opencode-logo-light.png +0 -0
  30. package/src/asset/brand/opencode-logo-light.svg +16 -0
  31. package/src/asset/brand/opencode-wordmark-dark.png +0 -0
  32. package/src/asset/brand/opencode-wordmark-dark.svg +30 -0
  33. package/src/asset/brand/opencode-wordmark-light.png +0 -0
  34. package/src/asset/brand/opencode-wordmark-light.svg +30 -0
  35. package/src/asset/brand/opencode-wordmark-simple-dark.png +0 -0
  36. package/src/asset/brand/opencode-wordmark-simple-dark.svg +22 -0
  37. package/src/asset/brand/opencode-wordmark-simple-light.png +0 -0
  38. package/src/asset/brand/opencode-wordmark-simple-light.svg +22 -0
  39. package/src/asset/brand/preview-opencode-dark.png +0 -0
  40. package/src/asset/brand/preview-opencode-logo-dark.png +0 -0
  41. package/src/asset/brand/preview-opencode-logo-light.png +0 -0
  42. package/src/asset/brand/preview-opencode-wordmark-dark.png +0 -0
  43. package/src/asset/brand/preview-opencode-wordmark-light.png +0 -0
  44. package/src/asset/brand/preview-opencode-wordmark-simple-dark.png +0 -0
  45. package/src/asset/brand/preview-opencode-wordmark-simple-light.png +0 -0
  46. package/src/asset/lander/avatar-adam.png +0 -0
  47. package/src/asset/lander/avatar-david.png +0 -0
  48. package/src/asset/lander/avatar-dax.png +0 -0
  49. package/src/asset/lander/avatar-frank.png +0 -0
  50. package/src/asset/lander/avatar-jay.png +0 -0
  51. package/src/asset/lander/brand-assets-dark.svg +10 -0
  52. package/src/asset/lander/brand-assets-light.svg +10 -0
  53. package/src/asset/lander/brand.png +0 -0
  54. package/src/asset/lander/check.svg +3 -0
  55. package/src/asset/lander/copy.svg +3 -0
  56. package/src/asset/lander/desktop-app-icon.png +0 -0
  57. package/src/asset/lander/dock.png +0 -0
  58. package/src/asset/lander/logo-dark.svg +11 -0
  59. package/src/asset/lander/logo-light.svg +11 -0
  60. package/src/asset/lander/opencode-comparison-min.mp4 +0 -0
  61. package/src/asset/lander/opencode-comparison-poster.png +0 -0
  62. package/src/asset/lander/opencode-desktop-icon.png +0 -0
  63. package/src/asset/lander/opencode-logo-dark.svg +11 -0
  64. package/src/asset/lander/opencode-logo-light.svg +11 -0
  65. package/src/asset/lander/opencode-min.mp4 +0 -0
  66. package/src/asset/lander/opencode-poster.png +0 -0
  67. package/src/asset/lander/opencode-wordmark-dark.svg +25 -0
  68. package/src/asset/lander/opencode-wordmark-light.svg +25 -0
  69. package/src/asset/lander/screenshot-github.png +0 -0
  70. package/src/asset/lander/screenshot-splash.png +0 -0
  71. package/src/asset/lander/screenshot-vscode.png +0 -0
  72. package/src/asset/lander/screenshot.png +0 -0
  73. package/src/asset/lander/wordmark-dark.svg +3 -0
  74. package/src/asset/lander/wordmark-light.svg +3 -0
  75. package/src/asset/logo-ornate-dark.svg +18 -0
  76. package/src/asset/logo-ornate-light.svg +18 -0
  77. package/src/asset/logo.svg +18 -0
  78. package/src/asset/zen-ornate-dark.svg +8 -0
  79. package/src/asset/zen-ornate-light.svg +8 -0
  80. package/src/component/dropdown.css +80 -0
  81. package/src/component/dropdown.tsx +79 -0
  82. package/src/component/email-signup.tsx +48 -0
  83. package/src/component/faq.tsx +33 -0
  84. package/src/component/footer.tsx +38 -0
  85. package/src/component/header-context-menu.css +63 -0
  86. package/src/component/header.tsx +279 -0
  87. package/src/component/icon.tsx +257 -0
  88. package/src/component/legal.tsx +20 -0
  89. package/src/component/modal.css +66 -0
  90. package/src/component/modal.tsx +24 -0
  91. package/src/component/spotlight.css +15 -0
  92. package/src/component/spotlight.tsx +820 -0
  93. package/src/config.ts +29 -0
  94. package/src/context/auth.session.ts +0 -0
  95. package/src/context/auth.ts +116 -0
  96. package/src/context/auth.withActor.ts +7 -0
  97. package/src/entry-client.tsx +4 -0
  98. package/src/entry-server.tsx +30 -0
  99. package/src/global.d.ts +5 -0
  100. package/src/lib/github.ts +38 -0
  101. package/src/middleware.ts +5 -0
  102. package/src/routes/[...404].css +130 -0
  103. package/src/routes/[...404].tsx +38 -0
  104. package/src/routes/api/enterprise.ts +47 -0
  105. package/src/routes/auth/[...callback].ts +41 -0
  106. package/src/routes/auth/authorize.ts +10 -0
  107. package/src/routes/auth/index.ts +12 -0
  108. package/src/routes/auth/logout.ts +17 -0
  109. package/src/routes/auth/status.ts +7 -0
  110. package/src/routes/bench/[id].tsx +365 -0
  111. package/src/routes/bench/index.tsx +86 -0
  112. package/src/routes/bench/submission.ts +29 -0
  113. package/src/routes/black/common.tsx +62 -0
  114. package/src/routes/black/index.tsx +108 -0
  115. package/src/routes/black/subscribe/[plan].tsx +449 -0
  116. package/src/routes/black/workspace.css +214 -0
  117. package/src/routes/black/workspace.tsx +229 -0
  118. package/src/routes/black.css +828 -0
  119. package/src/routes/black.tsx +285 -0
  120. package/src/routes/brand/index.css +555 -0
  121. package/src/routes/brand/index.tsx +252 -0
  122. package/src/routes/changelog/index.css +477 -0
  123. package/src/routes/changelog/index.tsx +147 -0
  124. package/src/routes/debug/index.ts +13 -0
  125. package/src/routes/desktop-feedback.ts +5 -0
  126. package/src/routes/discord.ts +5 -0
  127. package/src/routes/docs/[...path].ts +20 -0
  128. package/src/routes/docs/index.ts +20 -0
  129. package/src/routes/download/[platform].ts +38 -0
  130. package/src/routes/download/index.css +750 -0
  131. package/src/routes/download/index.tsx +482 -0
  132. package/src/routes/download/types.ts +4 -0
  133. package/src/routes/enterprise/index.css +578 -0
  134. package/src/routes/enterprise/index.tsx +251 -0
  135. package/src/routes/index.css +1251 -0
  136. package/src/routes/index.tsx +840 -0
  137. package/src/routes/legal/privacy-policy/index.css +343 -0
  138. package/src/routes/legal/privacy-policy/index.tsx +1512 -0
  139. package/src/routes/legal/terms-of-service/index.css +254 -0
  140. package/src/routes/legal/terms-of-service/index.tsx +512 -0
  141. package/src/routes/openapi.json.ts +7 -0
  142. package/src/routes/s/[id].ts +20 -0
  143. package/src/routes/stripe/webhook.ts +532 -0
  144. package/src/routes/t/[...path].tsx +20 -0
  145. package/src/routes/temp.tsx +172 -0
  146. package/src/routes/user-menu.css +18 -0
  147. package/src/routes/user-menu.tsx +32 -0
  148. package/src/routes/workspace/[id]/billing/billing-section.module.css +185 -0
  149. package/src/routes/workspace/[id]/billing/billing-section.tsx +240 -0
  150. package/src/routes/workspace/[id]/billing/black-section.module.css +142 -0
  151. package/src/routes/workspace/[id]/billing/black-section.tsx +269 -0
  152. package/src/routes/workspace/[id]/billing/black-waitlist-section.module.css +23 -0
  153. package/src/routes/workspace/[id]/billing/index.tsx +32 -0
  154. package/src/routes/workspace/[id]/billing/monthly-limit-section.module.css +96 -0
  155. package/src/routes/workspace/[id]/billing/monthly-limit-section.tsx +133 -0
  156. package/src/routes/workspace/[id]/billing/payment-section.module.css +93 -0
  157. package/src/routes/workspace/[id]/billing/payment-section.tsx +122 -0
  158. package/src/routes/workspace/[id]/billing/reload-section.module.css +261 -0
  159. package/src/routes/workspace/[id]/billing/reload-section.tsx +213 -0
  160. package/src/routes/workspace/[id]/graph-section.module.css +145 -0
  161. package/src/routes/workspace/[id]/graph-section.tsx +475 -0
  162. package/src/routes/workspace/[id]/index.tsx +81 -0
  163. package/src/routes/workspace/[id]/keys/index.tsx +11 -0
  164. package/src/routes/workspace/[id]/keys/key-section.module.css +197 -0
  165. package/src/routes/workspace/[id]/keys/key-section.tsx +176 -0
  166. package/src/routes/workspace/[id]/members/index.tsx +11 -0
  167. package/src/routes/workspace/[id]/members/member-section.module.css +249 -0
  168. package/src/routes/workspace/[id]/members/member-section.tsx +343 -0
  169. package/src/routes/workspace/[id]/members/role-dropdown.css +72 -0
  170. package/src/routes/workspace/[id]/members/role-dropdown.tsx +43 -0
  171. package/src/routes/workspace/[id]/model-section.module.css +173 -0
  172. package/src/routes/workspace/[id]/model-section.tsx +174 -0
  173. package/src/routes/workspace/[id]/new-user-section.module.css +143 -0
  174. package/src/routes/workspace/[id]/new-user-section.tsx +104 -0
  175. package/src/routes/workspace/[id]/provider-section.module.css +138 -0
  176. package/src/routes/workspace/[id]/provider-section.tsx +188 -0
  177. package/src/routes/workspace/[id]/settings/index.tsx +11 -0
  178. package/src/routes/workspace/[id]/settings/settings-section.module.css +94 -0
  179. package/src/routes/workspace/[id]/settings/settings-section.tsx +122 -0
  180. package/src/routes/workspace/[id]/usage-section.module.css +185 -0
  181. package/src/routes/workspace/[id]/usage-section.tsx +200 -0
  182. package/src/routes/workspace/[id].css +308 -0
  183. package/src/routes/workspace/[id].tsx +62 -0
  184. package/src/routes/workspace/common.tsx +120 -0
  185. package/src/routes/workspace-picker.css +74 -0
  186. package/src/routes/workspace-picker.tsx +122 -0
  187. package/src/routes/workspace.css +107 -0
  188. package/src/routes/workspace.tsx +38 -0
  189. package/src/routes/zen/index.css +866 -0
  190. package/src/routes/zen/index.tsx +343 -0
  191. package/src/routes/zen/util/dataDumper.ts +44 -0
  192. package/src/routes/zen/util/error.ts +13 -0
  193. package/src/routes/zen/util/handler.ts +784 -0
  194. package/src/routes/zen/util/logger.ts +12 -0
  195. package/src/routes/zen/util/provider/anthropic.ts +752 -0
  196. package/src/routes/zen/util/provider/google.ts +75 -0
  197. package/src/routes/zen/util/provider/openai-compatible.ts +546 -0
  198. package/src/routes/zen/util/provider/openai.ts +630 -0
  199. package/src/routes/zen/util/provider/provider.ts +210 -0
  200. package/src/routes/zen/util/rateLimiter.ts +41 -0
  201. package/src/routes/zen/util/stickyProviderTracker.ts +16 -0
  202. package/src/routes/zen/util/trialLimiter.ts +49 -0
  203. package/src/routes/zen/v1/chat/completions.ts +11 -0
  204. package/src/routes/zen/v1/messages.ts +11 -0
  205. package/src/routes/zen/v1/models/[model].ts +13 -0
  206. package/src/routes/zen/v1/models.ts +60 -0
  207. package/src/routes/zen/v1/responses.ts +11 -0
  208. package/src/style/base.css +21 -0
  209. package/src/style/component/button.css +102 -0
  210. package/src/style/index.css +8 -0
  211. package/src/style/reset.css +76 -0
  212. package/src/style/token/color.css +91 -0
  213. package/src/style/token/font.css +21 -0
  214. package/src/style/token/space.css +46 -0
  215. package/sst-env.d.ts +9 -0
  216. package/tsconfig.json +21 -0
  217. package/vite.config.ts +25 -0
@@ -0,0 +1,630 @@
1
+ import { ProviderHelper, CommonRequest, CommonResponse, CommonChunk } from "./provider"
2
+
3
+ type Usage = {
4
+ input_tokens?: number
5
+ input_tokens_details?: {
6
+ cached_tokens?: number
7
+ }
8
+ output_tokens?: number
9
+ output_tokens_details?: {
10
+ reasoning_tokens?: number
11
+ }
12
+ total_tokens?: number
13
+ }
14
+
15
+ export const openaiHelper: ProviderHelper = () => ({
16
+ format: "openai",
17
+ modifyUrl: (providerApi: string) => providerApi + "/responses",
18
+ modifyHeaders: (headers: Headers, body: Record<string, any>, apiKey: string) => {
19
+ headers.set("authorization", `Bearer ${apiKey}`)
20
+ },
21
+ modifyBody: (body: Record<string, any>) => {
22
+ return body
23
+ },
24
+ createBinaryStreamDecoder: () => undefined,
25
+ streamSeparator: "\n\n",
26
+ createUsageParser: () => {
27
+ let usage: Usage
28
+
29
+ return {
30
+ parse: (chunk: string) => {
31
+ const [event, data] = chunk.split("\n")
32
+ if (event !== "event: response.completed") return
33
+ if (!data.startsWith("data: ")) return
34
+
35
+ let json
36
+ try {
37
+ json = JSON.parse(data.slice(6)) as { response?: { usage?: Usage } }
38
+ } catch (e) {
39
+ return
40
+ }
41
+
42
+ if (!json.response?.usage) return
43
+ usage = json.response.usage
44
+ },
45
+ retrieve: () => usage,
46
+ }
47
+ },
48
+ normalizeUsage: (usage: Usage) => {
49
+ const inputTokens = usage.input_tokens ?? 0
50
+ const outputTokens = usage.output_tokens ?? 0
51
+ const reasoningTokens = usage.output_tokens_details?.reasoning_tokens ?? undefined
52
+ const cacheReadTokens = usage.input_tokens_details?.cached_tokens ?? undefined
53
+ return {
54
+ inputTokens: inputTokens - (cacheReadTokens ?? 0),
55
+ outputTokens: outputTokens - (reasoningTokens ?? 0),
56
+ reasoningTokens,
57
+ cacheReadTokens,
58
+ cacheWrite5mTokens: undefined,
59
+ cacheWrite1hTokens: undefined,
60
+ }
61
+ },
62
+ })
63
+
64
+ export function fromOpenaiRequest(body: any): CommonRequest {
65
+ if (!body || typeof body !== "object") return body
66
+
67
+ const toImg = (p: any) => {
68
+ if (!p || typeof p !== "object") return undefined
69
+ if ((p as any).type === "image_url" && (p as any).image_url)
70
+ return { type: "image_url", image_url: (p as any).image_url }
71
+ if ((p as any).type === "input_image" && (p as any).image_url)
72
+ return { type: "image_url", image_url: (p as any).image_url }
73
+ const s = (p as any).source
74
+ if (!s || typeof s !== "object") return undefined
75
+ if ((s as any).type === "url" && typeof (s as any).url === "string")
76
+ return { type: "image_url", image_url: { url: (s as any).url } }
77
+ if (
78
+ (s as any).type === "base64" &&
79
+ typeof (s as any).media_type === "string" &&
80
+ typeof (s as any).data === "string"
81
+ )
82
+ return {
83
+ type: "image_url",
84
+ image_url: { url: `data:${(s as any).media_type};base64,${(s as any).data}` },
85
+ }
86
+ return undefined
87
+ }
88
+
89
+ const msgs: any[] = []
90
+
91
+ const inMsgs = Array.isArray(body.input) ? body.input : Array.isArray(body.messages) ? body.messages : []
92
+
93
+ for (const m of inMsgs) {
94
+ if (!m) continue
95
+
96
+ // Responses API items without role:
97
+ if (!(m as any).role && (m as any).type) {
98
+ if ((m as any).type === "function_call") {
99
+ const name = (m as any).name
100
+ const a = (m as any).arguments
101
+ const args = typeof a === "string" ? a : JSON.stringify(a ?? {})
102
+ msgs.push({
103
+ role: "assistant",
104
+ tool_calls: [{ id: (m as any).id, type: "function", function: { name, arguments: args } }],
105
+ })
106
+ }
107
+ if ((m as any).type === "function_call_output") {
108
+ const id = (m as any).call_id
109
+ const out = (m as any).output
110
+ const content = typeof out === "string" ? out : JSON.stringify(out)
111
+ msgs.push({ role: "tool", tool_call_id: id, content })
112
+ }
113
+ continue
114
+ }
115
+
116
+ if ((m as any).role === "system" || (m as any).role === "developer") {
117
+ const c = (m as any).content
118
+ if (typeof c === "string" && c.length > 0) msgs.push({ role: "system", content: c })
119
+ if (Array.isArray(c)) {
120
+ const t = c.find((p: any) => p && typeof p.text === "string")
121
+ if (t && typeof t.text === "string" && t.text.length > 0) msgs.push({ role: "system", content: t.text })
122
+ }
123
+ continue
124
+ }
125
+
126
+ if ((m as any).role === "user") {
127
+ const c = (m as any).content
128
+ if (typeof c === "string") {
129
+ msgs.push({ role: "user", content: c })
130
+ } else if (Array.isArray(c)) {
131
+ const parts: any[] = []
132
+ for (const p of c) {
133
+ if (!p || !(p as any).type) continue
134
+ if (((p as any).type === "text" || (p as any).type === "input_text") && typeof (p as any).text === "string")
135
+ parts.push({ type: "text", text: (p as any).text })
136
+ const ip = toImg(p)
137
+ if (ip) parts.push(ip)
138
+ if ((p as any).type === "tool_result") {
139
+ const id = (p as any).tool_call_id
140
+ const content =
141
+ typeof (p as any).content === "string" ? (p as any).content : JSON.stringify((p as any).content)
142
+ msgs.push({ role: "tool", tool_call_id: id, content })
143
+ }
144
+ }
145
+ if (parts.length === 1 && parts[0].type === "text") msgs.push({ role: "user", content: parts[0].text })
146
+ else if (parts.length > 0) msgs.push({ role: "user", content: parts })
147
+ }
148
+ continue
149
+ }
150
+
151
+ if ((m as any).role === "assistant") {
152
+ const c = (m as any).content
153
+ const out: any = { role: "assistant" }
154
+ if (typeof c === "string" && c.length > 0) out.content = c
155
+ if (Array.isArray((m as any).tool_calls)) out.tool_calls = (m as any).tool_calls
156
+ msgs.push(out)
157
+ continue
158
+ }
159
+
160
+ if ((m as any).role === "tool") {
161
+ msgs.push({
162
+ role: "tool",
163
+ tool_call_id: (m as any).tool_call_id,
164
+ content: (m as any).content,
165
+ })
166
+ continue
167
+ }
168
+ }
169
+
170
+ const tcIn = body.tool_choice
171
+ const tc = (() => {
172
+ if (!tcIn) return undefined
173
+ if (tcIn === "auto") return "auto"
174
+ if (tcIn === "required") return "required"
175
+ if ((tcIn as any).type === "function" && (tcIn as any).function?.name)
176
+ return { type: "function" as const, function: { name: (tcIn as any).function.name } }
177
+ return undefined
178
+ })()
179
+
180
+ const stop = (() => {
181
+ const v = body.stop_sequences ?? body.stop
182
+ if (!v) return undefined
183
+ if (Array.isArray(v)) return v.length === 1 ? v[0] : v
184
+ if (typeof v === "string") return v
185
+ return undefined
186
+ })()
187
+
188
+ return {
189
+ model: body.model,
190
+ max_tokens: body.max_output_tokens ?? body.max_tokens,
191
+ temperature: body.temperature,
192
+ top_p: body.top_p,
193
+ stop,
194
+ messages: msgs,
195
+ stream: !!body.stream,
196
+ tools: Array.isArray(body.tools) ? body.tools : undefined,
197
+ tool_choice: tc,
198
+ }
199
+ }
200
+
201
+ export function toOpenaiRequest(body: CommonRequest) {
202
+ if (!body || typeof body !== "object") return body
203
+
204
+ const msgsIn = Array.isArray(body.messages) ? body.messages : []
205
+ const input: any[] = []
206
+
207
+ const toPart = (p: any) => {
208
+ if (!p || typeof p !== "object") return undefined
209
+ if ((p as any).type === "text" && typeof (p as any).text === "string")
210
+ return { type: "input_text", text: (p as any).text }
211
+ if ((p as any).type === "image_url" && (p as any).image_url)
212
+ return { type: "input_image", image_url: (p as any).image_url }
213
+ const s = (p as any).source
214
+ if (!s || typeof s !== "object") return undefined
215
+ if ((s as any).type === "url" && typeof (s as any).url === "string")
216
+ return { type: "input_image", image_url: { url: (s as any).url } }
217
+ if (
218
+ (s as any).type === "base64" &&
219
+ typeof (s as any).media_type === "string" &&
220
+ typeof (s as any).data === "string"
221
+ )
222
+ return {
223
+ type: "input_image",
224
+ image_url: { url: `data:${(s as any).media_type};base64,${(s as any).data}` },
225
+ }
226
+ return undefined
227
+ }
228
+
229
+ for (const m of msgsIn) {
230
+ if (!m || !(m as any).role) continue
231
+
232
+ if ((m as any).role === "system") {
233
+ const c = (m as any).content
234
+ if (typeof c === "string") input.push({ role: "system", content: c })
235
+ continue
236
+ }
237
+
238
+ if ((m as any).role === "user") {
239
+ const c = (m as any).content
240
+ if (typeof c === "string") {
241
+ input.push({ role: "user", content: [{ type: "input_text", text: c }] })
242
+ } else if (Array.isArray(c)) {
243
+ const parts: any[] = []
244
+ for (const p of c) {
245
+ const op = toPart(p)
246
+ if (op) parts.push(op)
247
+ }
248
+ if (parts.length > 0) input.push({ role: "user", content: parts })
249
+ }
250
+ continue
251
+ }
252
+
253
+ if ((m as any).role === "assistant") {
254
+ const c = (m as any).content
255
+ if (typeof c === "string" && c.length > 0) {
256
+ input.push({ role: "assistant", content: [{ type: "output_text", text: c }] })
257
+ }
258
+ if (Array.isArray((m as any).tool_calls)) {
259
+ for (const tc of (m as any).tool_calls) {
260
+ if ((tc as any).type === "function" && (tc as any).function) {
261
+ const name = (tc as any).function.name
262
+ const a = (tc as any).function.arguments
263
+ const args = typeof a === "string" ? a : JSON.stringify(a)
264
+ input.push({ type: "function_call", call_id: (tc as any).id, name, arguments: args })
265
+ }
266
+ }
267
+ }
268
+ continue
269
+ }
270
+
271
+ if ((m as any).role === "tool") {
272
+ const out = typeof (m as any).content === "string" ? (m as any).content : JSON.stringify((m as any).content)
273
+ input.push({ type: "function_call_output", call_id: (m as any).tool_call_id, output: out })
274
+ continue
275
+ }
276
+ }
277
+
278
+ const stop_sequences = (() => {
279
+ const v = body.stop
280
+ if (!v) return undefined
281
+ if (Array.isArray(v)) return v
282
+ if (typeof v === "string") return [v]
283
+ return undefined
284
+ })()
285
+
286
+ const tcIn = body.tool_choice
287
+ const tool_choice = (() => {
288
+ if (!tcIn) return undefined
289
+ if (tcIn === "auto") return "auto"
290
+ if (tcIn === "required") return "required"
291
+ if ((tcIn as any).type === "function" && (tcIn as any).function?.name)
292
+ return { type: "function", function: { name: (tcIn as any).function.name } }
293
+ return undefined
294
+ })()
295
+
296
+ const tools = (() => {
297
+ if (!Array.isArray(body.tools)) return undefined
298
+ return body.tools.map((tool: any) => {
299
+ if (tool.type === "function") {
300
+ return {
301
+ type: "function",
302
+ name: tool.function?.name,
303
+ description: tool.function?.description,
304
+ parameters: tool.function?.parameters,
305
+ strict: tool.function?.strict,
306
+ }
307
+ }
308
+ return tool
309
+ })
310
+ })()
311
+
312
+ return {
313
+ model: body.model,
314
+ input,
315
+ max_output_tokens: body.max_tokens,
316
+ top_p: body.top_p,
317
+ stop_sequences,
318
+ stream: !!body.stream,
319
+ tools,
320
+ tool_choice,
321
+ include: Array.isArray((body as any).include) ? (body as any).include : undefined,
322
+ truncation: (body as any).truncation,
323
+ metadata: (body as any).metadata,
324
+ store: (body as any).store,
325
+ user: (body as any).user,
326
+ text: { verbosity: body.model === "gpt-5-codex" ? "medium" : "low" },
327
+ reasoning: { effort: "medium" },
328
+ }
329
+ }
330
+
331
+ export function fromOpenaiResponse(resp: any): CommonResponse {
332
+ if (!resp || typeof resp !== "object") return resp
333
+ if (Array.isArray((resp as any).choices)) return resp
334
+
335
+ const r = (resp as any).response ?? resp
336
+ if (!r || typeof r !== "object") return resp
337
+
338
+ const idIn = (r as any).id
339
+ const id =
340
+ typeof idIn === "string" ? idIn.replace(/^resp_/, "chatcmpl_") : `chatcmpl_${Math.random().toString(36).slice(2)}`
341
+ const model = (r as any).model ?? (resp as any).model
342
+
343
+ const out = Array.isArray((r as any).output) ? (r as any).output : []
344
+ const text = out
345
+ .filter((o: any) => o && o.type === "message" && Array.isArray((o as any).content))
346
+ .flatMap((o: any) => (o as any).content)
347
+ .filter((p: any) => p && p.type === "output_text" && typeof p.text === "string")
348
+ .map((p: any) => p.text)
349
+ .join("")
350
+
351
+ const tcs = out
352
+ .filter((o: any) => o && o.type === "function_call")
353
+ .map((o: any) => {
354
+ const name = (o as any).name
355
+ const a = (o as any).arguments
356
+ const args = typeof a === "string" ? a : JSON.stringify(a ?? {})
357
+ const tid =
358
+ typeof (o as any).id === "string" && (o as any).id.length > 0
359
+ ? (o as any).id
360
+ : `toolu_${Math.random().toString(36).slice(2)}`
361
+ return { id: tid, type: "function" as const, function: { name, arguments: args } }
362
+ })
363
+
364
+ const finish = (r: string | null) => {
365
+ if (r === "stop") return "stop"
366
+ if (r === "tool_call" || r === "tool_calls") return "tool_calls"
367
+ if (r === "length" || r === "max_output_tokens") return "length"
368
+ if (r === "content_filter") return "content_filter"
369
+ return null
370
+ }
371
+
372
+ const u = (r as any).usage ?? (resp as any).usage
373
+ const usage = (() => {
374
+ if (!u) return undefined as any
375
+ const pt = typeof (u as any).input_tokens === "number" ? (u as any).input_tokens : undefined
376
+ const ct = typeof (u as any).output_tokens === "number" ? (u as any).output_tokens : undefined
377
+ const total = pt != null && ct != null ? pt + ct : undefined
378
+ const cached = (u as any).input_tokens_details?.cached_tokens
379
+ const details = typeof cached === "number" ? { cached_tokens: cached } : undefined
380
+ return {
381
+ prompt_tokens: pt,
382
+ completion_tokens: ct,
383
+ total_tokens: total,
384
+ ...(details ? { prompt_tokens_details: details } : {}),
385
+ }
386
+ })()
387
+
388
+ return {
389
+ id,
390
+ object: "chat.completion",
391
+ created: Math.floor(Date.now() / 1000),
392
+ model,
393
+ choices: [
394
+ {
395
+ index: 0,
396
+ message: {
397
+ role: "assistant",
398
+ ...(text && text.length > 0 ? { content: text } : {}),
399
+ ...(tcs.length > 0 ? { tool_calls: tcs } : {}),
400
+ },
401
+ finish_reason: finish((r as any).stop_reason ?? null),
402
+ },
403
+ ],
404
+ ...(usage ? { usage } : {}),
405
+ }
406
+ }
407
+
408
+ export function toOpenaiResponse(resp: CommonResponse) {
409
+ if (!resp || typeof resp !== "object") return resp
410
+ if (!Array.isArray((resp as any).choices)) return resp
411
+
412
+ const choice = (resp as any).choices[0]
413
+ if (!choice) return resp
414
+
415
+ const msg = choice.message
416
+ if (!msg) return resp
417
+
418
+ const outputItems: any[] = []
419
+
420
+ if (typeof msg.content === "string" && msg.content.length > 0) {
421
+ outputItems.push({
422
+ id: `msg_${Math.random().toString(36).slice(2)}`,
423
+ type: "message",
424
+ status: "completed",
425
+ role: "assistant",
426
+ content: [{ type: "output_text", text: msg.content, annotations: [], logprobs: [] }],
427
+ })
428
+ }
429
+
430
+ if (Array.isArray(msg.tool_calls)) {
431
+ for (const tc of msg.tool_calls) {
432
+ if ((tc as any).type === "function" && (tc as any).function) {
433
+ outputItems.push({
434
+ id: (tc as any).id,
435
+ type: "function_call",
436
+ name: (tc as any).function.name,
437
+ call_id: (tc as any).id,
438
+ arguments: (tc as any).function.arguments,
439
+ })
440
+ }
441
+ }
442
+ }
443
+
444
+ const stop_reason = (() => {
445
+ const r = choice.finish_reason
446
+ if (r === "stop") return "stop"
447
+ if (r === "tool_calls") return "tool_call"
448
+ if (r === "length") return "max_output_tokens"
449
+ if (r === "content_filter") return "content_filter"
450
+ return null
451
+ })()
452
+
453
+ const usage = (() => {
454
+ const u = (resp as any).usage
455
+ if (!u) return undefined
456
+ return {
457
+ input_tokens: u.prompt_tokens,
458
+ output_tokens: u.completion_tokens,
459
+ total_tokens: u.total_tokens,
460
+ ...(u.prompt_tokens_details?.cached_tokens
461
+ ? { input_tokens_details: { cached_tokens: u.prompt_tokens_details.cached_tokens } }
462
+ : {}),
463
+ }
464
+ })()
465
+
466
+ return {
467
+ id: (resp as any).id?.replace(/^chatcmpl_/, "resp_") ?? `resp_${Math.random().toString(36).slice(2)}`,
468
+ object: "response",
469
+ model: (resp as any).model,
470
+ output: outputItems,
471
+ stop_reason,
472
+ usage,
473
+ }
474
+ }
475
+
476
+ export function fromOpenaiChunk(chunk: string): CommonChunk | string {
477
+ const lines = chunk.split("\n")
478
+ const ev = lines[0]
479
+ const dl = lines[1]
480
+ if (!ev || !dl || !dl.startsWith("data: ")) return chunk
481
+
482
+ let json: any
483
+ try {
484
+ json = JSON.parse(dl.slice(6))
485
+ } catch {
486
+ return chunk
487
+ }
488
+
489
+ const respObj = json.response ?? {}
490
+
491
+ const out: CommonChunk = {
492
+ id: respObj.id ?? json.id ?? "",
493
+ object: "chat.completion.chunk",
494
+ created: Math.floor(Date.now() / 1000),
495
+ model: respObj.model ?? json.model ?? "",
496
+ choices: [],
497
+ }
498
+
499
+ const e = ev.replace("event: ", "").trim()
500
+
501
+ if (e === "response.output_text.delta") {
502
+ const d = (json as any).delta ?? (json as any).text ?? (json as any).output_text_delta
503
+ if (typeof d === "string" && d.length > 0)
504
+ out.choices.push({ index: 0, delta: { content: d }, finish_reason: null })
505
+ }
506
+
507
+ if (e === "response.output_item.added" && (json as any).item?.type === "function_call") {
508
+ const name = (json as any).item?.name
509
+ const id = (json as any).item?.id
510
+ if (typeof name === "string" && name.length > 0) {
511
+ out.choices.push({
512
+ index: 0,
513
+ delta: {
514
+ tool_calls: [{ index: 0, id, type: "function", function: { name, arguments: "" } }],
515
+ },
516
+ finish_reason: null,
517
+ })
518
+ }
519
+ }
520
+
521
+ if (e === "response.function_call_arguments.delta") {
522
+ const a = (json as any).delta ?? (json as any).arguments_delta
523
+ if (typeof a === "string" && a.length > 0) {
524
+ out.choices.push({
525
+ index: 0,
526
+ delta: { tool_calls: [{ index: 0, function: { arguments: a } }] },
527
+ finish_reason: null,
528
+ })
529
+ }
530
+ }
531
+
532
+ if (e === "response.completed") {
533
+ const fr = (() => {
534
+ const sr = (respObj as any).stop_reason ?? (json as any).stop_reason
535
+ if (sr === "stop") return "stop"
536
+ if (sr === "tool_call" || sr === "tool_calls") return "tool_calls"
537
+ if (sr === "length" || sr === "max_output_tokens") return "length"
538
+ if (sr === "content_filter") return "content_filter"
539
+ return null
540
+ })()
541
+ out.choices.push({ index: 0, delta: {}, finish_reason: fr })
542
+
543
+ const u = (respObj as any).usage ?? (json as any).response?.usage
544
+ if (u) {
545
+ out.usage = {
546
+ prompt_tokens: u.input_tokens,
547
+ completion_tokens: u.output_tokens,
548
+ total_tokens: (u.input_tokens || 0) + (u.output_tokens || 0),
549
+ ...(u.input_tokens_details?.cached_tokens
550
+ ? { prompt_tokens_details: { cached_tokens: u.input_tokens_details.cached_tokens } }
551
+ : {}),
552
+ }
553
+ }
554
+ }
555
+
556
+ return out
557
+ }
558
+
559
+ export function toOpenaiChunk(chunk: CommonChunk): string {
560
+ if (!chunk.choices || !Array.isArray(chunk.choices) || chunk.choices.length === 0) {
561
+ return ""
562
+ }
563
+
564
+ const choice = chunk.choices[0]
565
+ const d = choice.delta
566
+ if (!d) return ""
567
+
568
+ const id = chunk.id
569
+ const model = chunk.model
570
+
571
+ if (d.content) {
572
+ const data = {
573
+ id,
574
+ type: "response.output_text.delta",
575
+ delta: d.content,
576
+ response: { id, model },
577
+ }
578
+ return `event: response.output_text.delta\ndata: ${JSON.stringify(data)}`
579
+ }
580
+
581
+ if (d.tool_calls) {
582
+ for (const tc of d.tool_calls) {
583
+ if (tc.function?.name) {
584
+ const data = {
585
+ type: "response.output_item.added",
586
+ output_index: 0,
587
+ item: {
588
+ id: tc.id,
589
+ type: "function_call",
590
+ name: tc.function.name,
591
+ call_id: tc.id,
592
+ arguments: "",
593
+ },
594
+ }
595
+ return `event: response.output_item.added\ndata: ${JSON.stringify(data)}`
596
+ }
597
+ if (tc.function?.arguments) {
598
+ const data = {
599
+ type: "response.function_call_arguments.delta",
600
+ output_index: 0,
601
+ delta: tc.function.arguments,
602
+ }
603
+ return `event: response.function_call_arguments.delta\ndata: ${JSON.stringify(data)}`
604
+ }
605
+ }
606
+ }
607
+
608
+ if (choice.finish_reason) {
609
+ const u = chunk.usage
610
+ const usage = u
611
+ ? {
612
+ input_tokens: u.prompt_tokens,
613
+ output_tokens: u.completion_tokens,
614
+ total_tokens: u.total_tokens,
615
+ ...(u.prompt_tokens_details?.cached_tokens
616
+ ? { input_tokens_details: { cached_tokens: u.prompt_tokens_details.cached_tokens } }
617
+ : {}),
618
+ }
619
+ : undefined
620
+
621
+ const data: any = {
622
+ id,
623
+ type: "response.completed",
624
+ response: { id, model, ...(usage ? { usage } : {}) },
625
+ }
626
+ return `event: response.completed\ndata: ${JSON.stringify(data)}`
627
+ }
628
+
629
+ return ""
630
+ }