@cartanova/qgrid-cli 2.10.10 → 2.11.1

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 (302) hide show
  1. package/bundle/dist/_virtual/_rolldown/runtime.js +1 -10
  2. package/bundle/dist/application/monit/log-buffer.js +12 -6
  3. package/bundle/dist/application/monit/monit.frame.js +11 -3
  4. package/bundle/dist/application/monit/monit.types.js +1 -1
  5. package/bundle/dist/application/qgrid/conv-routing.js +9 -6
  6. package/bundle/dist/application/qgrid/expired-token-reminder.js +2 -2
  7. package/bundle/dist/application/qgrid/oauth.js +31 -4
  8. package/bundle/dist/application/qgrid/qgrid-image-generation.js +1 -1
  9. package/bundle/dist/application/qgrid/qgrid-response-format.js +21 -19
  10. package/bundle/dist/application/qgrid/qgrid-run-lifecycle.js +40 -33
  11. package/bundle/dist/application/qgrid/qgrid.dispatcher.js +128 -93
  12. package/bundle/dist/application/qgrid/qgrid.frame.js +930 -49
  13. package/bundle/dist/application/qgrid/qgrid.types.js +212 -5
  14. package/bundle/dist/application/qgrid/schema-prompt.js +75 -48
  15. package/bundle/dist/application/qgrid/slack-user-map.js +1 -1
  16. package/bundle/dist/application/qgrid/token-death.js +1 -1
  17. package/bundle/dist/application/qgrid/token-subscriber.js +83 -30
  18. package/bundle/dist/application/qgrid/token-trigger-setup.js +1 -1
  19. package/bundle/dist/application/qgrid/token-window-keepalive.js +8 -6
  20. package/bundle/dist/application/qgrid/tool-emulation-schema.js +13 -13
  21. package/bundle/dist/application/qgrid/tool-emulation.js +19 -15
  22. package/bundle/dist/application/request-log/json-schema-type-text.js +29 -12
  23. package/bundle/dist/application/request-log/request-log.model.js +83 -70
  24. package/bundle/dist/application/request-log/request-log.types.js +1 -1
  25. package/bundle/dist/application/request-log/zod-code-format.js +1 -1
  26. package/bundle/dist/application/request-log-step/request-log-step.model.js +1 -1
  27. package/bundle/dist/application/request-log-step/request-log-step.types.js +1 -1
  28. package/bundle/dist/application/setting/setting.constant.js +1 -1
  29. package/bundle/dist/application/setting/setting.model.js +2 -2
  30. package/bundle/dist/application/setting/setting.store.js +2 -2
  31. package/bundle/dist/application/setting/setting.types.js +1 -1
  32. package/bundle/dist/application/sonamu.generated.js +32 -16
  33. package/bundle/dist/application/sonamu.generated.sso.js +1 -1
  34. package/bundle/dist/application/token/token.model.js +26 -9
  35. package/bundle/dist/application/token/token.types.js +20 -3
  36. package/bundle/dist/i18n/en.js +15 -1
  37. package/bundle/dist/i18n/ko.js +15 -1
  38. package/bundle/dist/i18n/sd.generated.js +1 -1
  39. package/bundle/dist/migrations/20260807133553_manual__backfill_request_log_cost.js +8 -6
  40. package/bundle/dist/server-bootstrap.js +4 -2
  41. package/bundle/dist/server-error-handler.js +1 -1
  42. package/bundle/dist/sonamu.config.js +37 -8
  43. package/bundle/dist/startup-migrations.js +17 -7
  44. package/bundle/dist/testing/global.js +1 -1
  45. package/bundle/dist/testing/schema-bench.js +0 -0
  46. package/bundle/dist/testing/test-database-name.js +1 -1
  47. package/bundle/dist/utils/providers/anthropic/anthropic-constants.js +1 -1
  48. package/bundle/dist/utils/providers/anthropic/anthropic-dispatcher.js +11 -5
  49. package/bundle/dist/utils/providers/anthropic/anthropic-quota.js +4 -4
  50. package/bundle/dist/utils/providers/anthropic/anthropic-thinking.js +1 -1
  51. package/bundle/dist/utils/providers/anthropic/claude-session.js +20 -19
  52. package/bundle/dist/utils/providers/anthropic/fence-strip.js +1 -1
  53. package/bundle/dist/utils/providers/anthropic/stream-json-adapter.js +49 -23
  54. package/bundle/dist/utils/providers/common/credentials.js +5 -1
  55. package/bundle/dist/utils/providers/common/effort.js +3 -3
  56. package/bundle/dist/utils/providers/common/model-cost.js +69 -14
  57. package/bundle/dist/utils/providers/common/provider-types.js +29 -1
  58. package/bundle/dist/utils/providers/common/schema-validation.js +78 -40
  59. package/bundle/dist/utils/providers/common/smooth-weighted-round-robin.js +1 -1
  60. package/bundle/dist/utils/providers/common/strictifier.js +62 -43
  61. package/bundle/dist/utils/providers/common/thinking.js +3 -2
  62. package/bundle/dist/utils/providers/common/ttft.js +1 -1
  63. package/bundle/dist/utils/providers/grok/grok-backend-protocol.js +427 -0
  64. package/bundle/dist/utils/providers/grok/grok-constants.js +55 -0
  65. package/bundle/dist/utils/providers/grok/grok-direct-client.js +196 -0
  66. package/bundle/dist/utils/providers/grok/grok-dispatcher.js +1313 -0
  67. package/bundle/dist/utils/providers/grok/grok-headers.js +118 -0
  68. package/bundle/dist/utils/providers/grok/grok-media-client.js +420 -0
  69. package/bundle/dist/utils/providers/grok/grok-models.js +114 -0
  70. package/bundle/dist/utils/providers/grok/grok-oauth.js +455 -0
  71. package/bundle/dist/utils/providers/grok/grok-quota.js +119 -0
  72. package/bundle/dist/utils/providers/grok/grok-refresh.js +192 -0
  73. package/bundle/dist/utils/providers/grok/grok-types.js +186 -0
  74. package/bundle/dist/utils/providers/grok/grok-voice-client.js +199 -0
  75. package/bundle/dist/utils/providers/grok/index.js +11 -0
  76. package/bundle/dist/utils/providers/openai/openai-backend-protocol.js +111 -83
  77. package/bundle/dist/utils/providers/openai/openai-callback-relay.js +3 -2
  78. package/bundle/dist/utils/providers/openai/openai-constants.js +1 -1
  79. package/bundle/dist/utils/providers/openai/openai-direct-client.js +23 -7
  80. package/bundle/dist/utils/providers/openai/openai-dispatcher.js +143 -106
  81. package/bundle/dist/utils/providers/openai/openai-image-generation.js +48 -33
  82. package/bundle/dist/utils/providers/openai/openai-oauth.js +28 -13
  83. package/bundle/dist/utils/providers/openai/openai-quota.js +38 -27
  84. package/bundle/dist/utils/providers/openai/openai-refresh.js +17 -9
  85. package/bundle/dist/utils/providers/openai/openai-sse.js +1 -1
  86. package/bundle/dist/utils/providers/openai/openai-transport-config.js +1 -1
  87. package/bundle/dist/utils/quiet-hours.js +1 -1
  88. package/bundle/dist/utils/slack-notify.js +14 -8
  89. package/bundle/dist/utils/subset-loaders.js +4 -5
  90. package/bundle/src/application/monit/log-buffer.ts +23 -9
  91. package/bundle/src/application/monit/monit.frame.ts +22 -5
  92. package/bundle/src/application/monit/monit.types.ts +6 -0
  93. package/bundle/src/application/qgrid/conv-routing.test.ts +76 -4
  94. package/bundle/src/application/qgrid/conv-routing.ts +35 -9
  95. package/bundle/src/application/qgrid/expired-token-reminder.ts +9 -1
  96. package/bundle/src/application/qgrid/oauth.ts +51 -15
  97. package/bundle/src/application/qgrid/qgrid-image-generation.ts +9 -0
  98. package/bundle/src/application/qgrid/qgrid-response-format.ts +50 -25
  99. package/bundle/src/application/qgrid/qgrid-run-lifecycle.test.ts +19 -0
  100. package/bundle/src/application/qgrid/qgrid-run-lifecycle.ts +84 -40
  101. package/bundle/src/application/qgrid/qgrid.dispatcher.test.ts +97 -0
  102. package/bundle/src/application/qgrid/qgrid.dispatcher.ts +274 -126
  103. package/bundle/src/application/qgrid/qgrid.frame.test.ts +1013 -3
  104. package/bundle/src/application/qgrid/qgrid.frame.ts +1415 -60
  105. package/bundle/src/application/qgrid/qgrid.types.ts +335 -0
  106. package/bundle/src/application/qgrid/schema-prompt.ts +105 -41
  107. package/bundle/src/application/qgrid/slack-user-map.ts +5 -0
  108. package/bundle/src/application/qgrid/token-death.ts +4 -0
  109. package/bundle/src/application/qgrid/token-subscriber.test.ts +21 -19
  110. package/bundle/src/application/qgrid/token-subscriber.ts +186 -53
  111. package/bundle/src/application/qgrid/token-trigger-setup.ts +2 -0
  112. package/bundle/src/application/qgrid/token-window-keepalive.ts +49 -6
  113. package/bundle/src/application/qgrid/tool-emulation-schema.ts +33 -29
  114. package/bundle/src/application/qgrid/tool-emulation.ts +52 -27
  115. package/bundle/src/application/request-log/json-schema-type-text.test.ts +5 -5
  116. package/bundle/src/application/request-log/json-schema-type-text.ts +81 -26
  117. package/bundle/src/application/request-log/request-log.model.test.ts +111 -4
  118. package/bundle/src/application/request-log/request-log.model.ts +277 -146
  119. package/bundle/src/application/request-log/request-log.types.ts +6 -0
  120. package/bundle/src/application/request-log/zod-code-format.ts +11 -0
  121. package/bundle/src/application/request-log-step/request-log-step.model.ts +1 -0
  122. package/bundle/src/application/request-log-step/request-log-step.types.ts +2 -0
  123. package/bundle/src/application/setting/setting.constant.ts +9 -0
  124. package/bundle/src/application/setting/setting.model.ts +12 -2
  125. package/bundle/src/application/setting/setting.store.ts +5 -1
  126. package/bundle/src/application/setting/setting.types.ts +8 -0
  127. package/bundle/src/application/sonamu.generated.http +133 -0
  128. package/bundle/src/application/sonamu.generated.sso.ts +2 -0
  129. package/bundle/src/application/sonamu.generated.ts +15 -0
  130. package/bundle/src/application/token/token.identity.test.ts +20 -0
  131. package/bundle/src/application/token/token.model.ts +66 -7
  132. package/bundle/src/application/token/token.types.ts +24 -1
  133. package/bundle/src/i18n/en.ts +19 -0
  134. package/bundle/src/i18n/ko.ts +21 -0
  135. package/bundle/src/i18n/sd.generated.ts +23 -2
  136. package/bundle/src/migrations/20260807133553_manual__backfill_request_log_cost.ts +16 -8
  137. package/bundle/src/server-bootstrap.ts +4 -2
  138. package/bundle/src/server-error-handler.ts +1 -0
  139. package/bundle/src/sonamu.config.ts +69 -7
  140. package/bundle/src/startup-migrations.ts +27 -9
  141. package/bundle/src/testing/global.ts +20 -0
  142. package/bundle/src/testing/schema-bench.ts +105 -30
  143. package/bundle/src/testing/test-database-name.ts +2 -0
  144. package/bundle/src/utils/providers/anthropic/anthropic-constants.ts +8 -0
  145. package/bundle/src/utils/providers/anthropic/anthropic-dispatcher.ts +41 -6
  146. package/bundle/src/utils/providers/anthropic/anthropic-quota.ts +7 -3
  147. package/bundle/src/utils/providers/anthropic/anthropic-thinking.ts +6 -0
  148. package/bundle/src/utils/providers/anthropic/claude-session.ts +58 -24
  149. package/bundle/src/utils/providers/anthropic/fence-strip.ts +11 -0
  150. package/bundle/src/utils/providers/anthropic/stream-json-adapter.ts +110 -45
  151. package/bundle/src/utils/providers/common/credentials.ts +8 -0
  152. package/bundle/src/utils/providers/common/effort.ts +9 -2
  153. package/bundle/src/utils/providers/common/model-cost.test.ts +48 -1
  154. package/bundle/src/utils/providers/common/model-cost.ts +259 -158
  155. package/bundle/src/utils/providers/common/provider-dispatcher.ts +98 -2
  156. package/bundle/src/utils/providers/common/provider-types.ts +46 -3
  157. package/bundle/src/utils/providers/common/schema-validation.test.ts +5 -1
  158. package/bundle/src/utils/providers/common/schema-validation.ts +124 -56
  159. package/bundle/src/utils/providers/common/smooth-weighted-round-robin.ts +4 -0
  160. package/bundle/src/utils/providers/common/strictifier.ts +185 -59
  161. package/bundle/src/utils/providers/common/thinking.ts +2 -2
  162. package/bundle/src/utils/providers/common/ttft.ts +4 -2
  163. package/bundle/src/utils/providers/grok/__fixtures__/golden-responses-1.0.60.json +164 -0
  164. package/bundle/src/utils/providers/grok/grok-backend-protocol.test.ts +325 -0
  165. package/bundle/src/utils/providers/grok/grok-backend-protocol.ts +721 -0
  166. package/bundle/src/utils/providers/grok/grok-constants.ts +105 -0
  167. package/bundle/src/utils/providers/grok/grok-direct-client.test.ts +179 -0
  168. package/bundle/src/utils/providers/grok/grok-direct-client.ts +313 -0
  169. package/bundle/src/utils/providers/grok/grok-dispatcher.test.ts +1395 -0
  170. package/bundle/src/utils/providers/grok/grok-dispatcher.ts +2105 -0
  171. package/bundle/src/utils/providers/grok/grok-headers.test.ts +149 -0
  172. package/bundle/src/utils/providers/grok/grok-headers.ts +199 -0
  173. package/bundle/src/utils/providers/grok/grok-media-client.test.ts +446 -0
  174. package/bundle/src/utils/providers/grok/grok-media-client.ts +703 -0
  175. package/bundle/src/utils/providers/grok/grok-models.test.ts +96 -0
  176. package/bundle/src/utils/providers/grok/grok-models.ts +161 -0
  177. package/bundle/src/utils/providers/grok/grok-oauth.test.ts +406 -0
  178. package/bundle/src/utils/providers/grok/grok-oauth.ts +771 -0
  179. package/bundle/src/utils/providers/grok/grok-quota.test.ts +142 -0
  180. package/bundle/src/utils/providers/grok/grok-quota.ts +198 -0
  181. package/bundle/src/utils/providers/grok/grok-refresh.test.ts +239 -0
  182. package/bundle/src/utils/providers/grok/grok-refresh.ts +325 -0
  183. package/bundle/src/utils/providers/grok/grok-types.test.ts +252 -0
  184. package/bundle/src/utils/providers/grok/grok-types.ts +374 -0
  185. package/bundle/src/utils/providers/grok/grok-voice-client.test.ts +321 -0
  186. package/bundle/src/utils/providers/grok/grok-voice-client.ts +284 -0
  187. package/bundle/src/utils/providers/grok/index.ts +20 -0
  188. package/bundle/src/utils/providers/openai/openai-backend-protocol.ts +221 -137
  189. package/bundle/src/utils/providers/openai/openai-callback-relay.ts +12 -1
  190. package/bundle/src/utils/providers/openai/openai-constants.ts +2 -0
  191. package/bundle/src/utils/providers/openai/openai-direct-client.ts +111 -21
  192. package/bundle/src/utils/providers/openai/openai-dispatcher.ts +322 -169
  193. package/bundle/src/utils/providers/openai/openai-image-generation.ts +116 -36
  194. package/bundle/src/utils/providers/openai/openai-oauth.ts +48 -20
  195. package/bundle/src/utils/providers/openai/openai-quota.ts +77 -46
  196. package/bundle/src/utils/providers/openai/openai-refresh.ts +42 -18
  197. package/bundle/src/utils/providers/openai/openai-sse.ts +27 -3
  198. package/bundle/src/utils/providers/openai/openai-transport-config.ts +2 -0
  199. package/bundle/src/utils/quiet-hours.ts +12 -1
  200. package/bundle/src/utils/slack-notify.ts +44 -13
  201. package/bundle/src/utils/subset-loaders.ts +7 -7
  202. package/bundle/web-dist/client/assets/clsx-5XM_pHvN.js +1 -0
  203. package/bundle/web-dist/client/assets/index-Be2qCZmI.css +1 -0
  204. package/bundle/web-dist/client/assets/index-C96h3feE.js +32 -0
  205. package/bundle/web-dist/client/assets/logs-DBM6U0QD.js +1 -0
  206. package/bundle/web-dist/client/assets/monit-BBjXa7vB.js +1 -0
  207. package/bundle/web-dist/client/assets/routes-Crz_PQzi.js +5 -0
  208. package/bundle/web-dist/client/assets/{services.generated-NP-ValHd.js → services.generated-B5lc3aIC.js} +5 -5
  209. package/bundle/web-dist/client/assets/settings-Cdwm3OBB.js +1 -0
  210. package/bundle/web-dist/client/assets/show-DBUhALT1.js +2 -0
  211. package/bundle/web-dist/client/assets/tokens-CVQAVJTo.js +1 -0
  212. package/bundle/web-dist/client/assets/total-cost-MnfKU8IN.js +1 -0
  213. package/bundle/web-dist/client/assets/use-oauth-login-flow-BfxYI4Oh.js +1 -0
  214. package/bundle/web-dist/client/assets/use-update-token-mutation-qKBC2sn7.js +1 -0
  215. package/bundle/web-dist/client/assets/{vendor-tanstack-DJRQAbrY.js → vendor-tanstack-DRPs09ai.js} +1 -1
  216. package/bundle/web-dist/client/index.html +5 -5
  217. package/bundle/web-dist/server/assets/clsx-D_N5PN_2.js +16 -0
  218. package/bundle/web-dist/server/assets/{lazyRouteComponent-CgBdabgd.js → lazyRouteComponent-AKiHfZWp.js} +129 -2
  219. package/bundle/web-dist/server/assets/{logs-BMyR98rU.js → logs-B7QmGJq3.js} +18 -14
  220. package/bundle/web-dist/server/assets/{monit-ZPt66W5M.js → monit-DFt7x3bh.js} +13 -12
  221. package/bundle/web-dist/server/assets/{plus-BZ9I0VgC.js → plus-DaYQRpBt.js} +1 -1
  222. package/bundle/web-dist/server/assets/{react-dom-Ds75e4Jl.js → react-dom-Cv23mm1K.js} +2 -129
  223. package/bundle/web-dist/server/assets/{refresh-cw-CztCFis-.js → refresh-cw-BpXNsAy-.js} +1 -1
  224. package/bundle/web-dist/server/assets/{request-model-DjGldb-h.js → request-model-eHE6YbaF.js} +1 -1
  225. package/bundle/web-dist/server/assets/{routes-Ct-k7nBA.js → routes-_Y-PJfgO.js} +110 -39
  226. package/bundle/web-dist/server/assets/{services.generated-BA4AcAf5.js → services.generated-CMJ8AHbO.js} +582 -172
  227. package/bundle/web-dist/server/assets/{settings-CBUZ47Xx.js → settings-JwJKxn2e.js} +14 -14
  228. package/bundle/web-dist/server/assets/{show-LSFIzvO2.js → show-BEv6z1v_.js} +90 -60
  229. package/bundle/web-dist/server/assets/{tokens-DxrBW512.js → tokens-CNT4mI-f.js} +162 -50
  230. package/bundle/web-dist/server/assets/total-cost-fTE9Xy82.js +18 -0
  231. package/bundle/web-dist/server/assets/use-oauth-login-flow-jkAkT3X6.js +2345 -0
  232. package/bundle/web-dist/server/assets/{use-update-token-mutation-pkArIzCP.js → use-update-token-mutation-Sm6kh02m.js} +1 -1
  233. package/bundle/web-dist/server/entry-server.generated.js +1644 -21655
  234. package/dist/cli.js +9 -9
  235. package/package.json +4 -11
  236. package/postinstall.mjs +12 -0
  237. package/skills/qgrid/SKILL.md +1 -0
  238. package/skills/qgrid/references/cli-env-and-server-boot.md +4 -1
  239. package/skills/qgrid/references/decision-rationale.md +23 -0
  240. package/skills/qgrid/references/grok-runtime.md +89 -0
  241. package/skills/qgrid/references/provider-runtime-differences.md +30 -29
  242. package/skills/qgrid/references/repo-map.md +10 -0
  243. package/skills/qgrid/references/token-auth-quota-lifecycle.md +56 -3
  244. package/bundle/dist/node_modules/.pnpm/@jridgewell_sourcemap-codec@1.5.5/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.js +0 -406
  245. package/bundle/dist/node_modules/.pnpm/@vitest_expect@4.1.10/node_modules/@vitest/expect/dist/index.js +0 -1507
  246. package/bundle/dist/node_modules/.pnpm/@vitest_pretty-format@4.1.10/node_modules/@vitest/pretty-format/dist/index.js +0 -887
  247. package/bundle/dist/node_modules/.pnpm/@vitest_runner@4.1.10/node_modules/@vitest/runner/dist/chunk-artifact.js +0 -2756
  248. package/bundle/dist/node_modules/.pnpm/@vitest_runner@4.1.10/node_modules/@vitest/runner/dist/index.js +0 -8
  249. package/bundle/dist/node_modules/.pnpm/@vitest_runner@4.1.10/node_modules/@vitest/runner/dist/utils.js +0 -8
  250. package/bundle/dist/node_modules/.pnpm/@vitest_snapshot@4.1.10/node_modules/@vitest/snapshot/dist/index.js +0 -922
  251. package/bundle/dist/node_modules/.pnpm/@vitest_spy@4.1.10/node_modules/@vitest/spy/dist/index.js +0 -386
  252. package/bundle/dist/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/chunk-pathe.M-eThtNZ.js +0 -121
  253. package/bundle/dist/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/constants.js +0 -51
  254. package/bundle/dist/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/diff.js +0 -1317
  255. package/bundle/dist/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/display.js +0 -561
  256. package/bundle/dist/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/error.js +0 -40
  257. package/bundle/dist/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/helpers.js +0 -272
  258. package/bundle/dist/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/offset.js +0 -27
  259. package/bundle/dist/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/serialize.js +0 -77
  260. package/bundle/dist/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/source-map.js +0 -381
  261. package/bundle/dist/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/timers.js +0 -51
  262. package/bundle/dist/node_modules/.pnpm/chai@6.2.2/node_modules/chai/index.js +0 -2986
  263. package/bundle/dist/node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/branding.js +0 -10
  264. package/bundle/dist/node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/index.js +0 -109
  265. package/bundle/dist/node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/messages.js +0 -10
  266. package/bundle/dist/node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/overloads.js +0 -10
  267. package/bundle/dist/node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/utils.js +0 -10
  268. package/bundle/dist/node_modules/.pnpm/magic-string@0.30.21/node_modules/magic-string/dist/magic-string.es.js +0 -1126
  269. package/bundle/dist/node_modules/.pnpm/pathe@2.0.3/node_modules/pathe/dist/index.js +0 -21
  270. package/bundle/dist/node_modules/.pnpm/pathe@2.0.3/node_modules/pathe/dist/shared/pathe.M-eThtNZ.js +0 -432
  271. package/bundle/dist/node_modules/.pnpm/tinybench@2.9.0/node_modules/tinybench/dist/index.js +0 -517
  272. package/bundle/dist/node_modules/.pnpm/tinyrainbow@3.1.0/node_modules/tinyrainbow/dist/index.js +0 -101
  273. package/bundle/dist/node_modules/.pnpm/vite@8.0.3_@emnapi_core@1.11.2_@emnapi_runtime@1.11.2_@types_node@25.0.7_esbuild@0.27.4_5b35fc1fb85d2af4d105e0d74f940f50/node_modules/vite/dist/node/module-runner.js +0 -1265
  274. package/bundle/dist/node_modules/.pnpm/vitest@4.1.10_@opentelemetry_api@1.9.0_@types_node@25.0.7_@vitest_coverage-v8@4.1.10_vi_a1977b93af7bb69308bcb1dbab39b618/node_modules/vitest/dist/chunks/_commonjsHelpers.D26ty3Ew.js +0 -9
  275. package/bundle/dist/node_modules/.pnpm/vitest@4.1.10_@opentelemetry_api@1.9.0_@types_node@25.0.7_@vitest_coverage-v8@4.1.10_vi_a1977b93af7bb69308bcb1dbab39b618/node_modules/vitest/dist/chunks/benchmark.CX_oY03V.js +0 -41
  276. package/bundle/dist/node_modules/.pnpm/vitest@4.1.10_@opentelemetry_api@1.9.0_@types_node@25.0.7_@vitest_coverage-v8@4.1.10_vi_a1977b93af7bb69308bcb1dbab39b618/node_modules/vitest/dist/chunks/evaluatedModules.Dg1zASAC.js +0 -19
  277. package/bundle/dist/node_modules/.pnpm/vitest@4.1.10_@opentelemetry_api@1.9.0_@types_node@25.0.7_@vitest_coverage-v8@4.1.10_vi_a1977b93af7bb69308bcb1dbab39b618/node_modules/vitest/dist/chunks/index.Chj8NDwU.js +0 -202
  278. package/bundle/dist/node_modules/.pnpm/vitest@4.1.10_@opentelemetry_api@1.9.0_@types_node@25.0.7_@vitest_coverage-v8@4.1.10_vi_a1977b93af7bb69308bcb1dbab39b618/node_modules/vitest/dist/chunks/index.DdgEv5B1.js +0 -46
  279. package/bundle/dist/node_modules/.pnpm/vitest@4.1.10_@opentelemetry_api@1.9.0_@types_node@25.0.7_@vitest_coverage-v8@4.1.10_vi_a1977b93af7bb69308bcb1dbab39b618/node_modules/vitest/dist/chunks/rpc.MzXet3jl.js +0 -119
  280. package/bundle/dist/node_modules/.pnpm/vitest@4.1.10_@opentelemetry_api@1.9.0_@types_node@25.0.7_@vitest_coverage-v8@4.1.10_vi_a1977b93af7bb69308bcb1dbab39b618/node_modules/vitest/dist/chunks/test.DNmyFkvJ.js +0 -3044
  281. package/bundle/dist/node_modules/.pnpm/vitest@4.1.10_@opentelemetry_api@1.9.0_@types_node@25.0.7_@vitest_coverage-v8@4.1.10_vi_a1977b93af7bb69308bcb1dbab39b618/node_modules/vitest/dist/chunks/utils.BX5Fg8C4.js +0 -59
  282. package/bundle/dist/node_modules/.pnpm/vitest@4.1.10_@opentelemetry_api@1.9.0_@types_node@25.0.7_@vitest_coverage-v8@4.1.10_vi_a1977b93af7bb69308bcb1dbab39b618/node_modules/vitest/dist/index.js +0 -28
  283. package/bundle/dist/testing/setup-mocks.js +0 -16
  284. package/bundle/src/testing/setup-mocks.ts +0 -44
  285. package/bundle/web-dist/client/assets/index-Bw2s88Ot.css +0 -1
  286. package/bundle/web-dist/client/assets/index-DZwzUfI-.js +0 -106
  287. package/bundle/web-dist/client/assets/logs-Cog79DU5.js +0 -1
  288. package/bundle/web-dist/client/assets/monit-2auznBys.js +0 -1
  289. package/bundle/web-dist/client/assets/routes-B0lTeLK-.js +0 -5
  290. package/bundle/web-dist/client/assets/settings-D8ZxZGSJ.js +0 -1
  291. package/bundle/web-dist/client/assets/show-CFSERlnM.js +0 -2
  292. package/bundle/web-dist/client/assets/tokens-CZXUyI7O.js +0 -1
  293. package/bundle/web-dist/client/assets/use-oauth-login-flow-hboOs3wG.js +0 -1
  294. package/bundle/web-dist/client/assets/use-update-token-mutation-BQHS0byG.js +0 -1
  295. package/bundle/web-dist/server/assets/use-oauth-login-flow-Gn8flMK5.js +0 -178
  296. package/dist/cli.d.ts +0 -10
  297. /package/bundle/web-dist/client/assets/{cost-DH2jZbrw.js → cost-DB3HbU7T.js} +0 -0
  298. /package/bundle/web-dist/client/assets/{plus-B62CuqT8.js → plus-BsQdY7PS.js} +0 -0
  299. /package/bundle/web-dist/client/assets/{preload-helper-ca-nBW7U.js → preload-helper-DWTEM3RW.js} +0 -0
  300. /package/bundle/web-dist/client/assets/{refresh-cw-NNcU0Cmq.js → refresh-cw-D9IUmT23.js} +0 -0
  301. /package/bundle/web-dist/client/assets/{request-model-CojtinqK.js → request-model-Bpp_rkK8.js} +0 -0
  302. /package/bundle/web-dist/server/assets/{cost-eEHeTRT5.js → cost-BC9yzyA9.js} +0 -0
@@ -0,0 +1,325 @@
1
+ // Grok (xAI) token refresh: single-flight per token, early refresh with a stable per-account
2
+ // jitter, refresh-token retention when the IdP does not rotate, persist-before-resolve.
3
+ // Mechanism follows grok-build 1.0.45 xai-grok-login/src (oidc/protocol.rs:413-520,
4
+ // oidc/refresh.rs:21-27,260-264, model.rs:9). grok-build retries transient failures in place
5
+ // (3 attempts); qgrid does not, because the dispatcher owns the per-request attempt budget.
6
+ import { createHash } from "node:crypto";
7
+
8
+ import { getLogger } from "@logtape/logtape";
9
+
10
+ import { grokShellUserAgent } from "./grok-headers";
11
+ import {
12
+ type GrokFetch,
13
+ GrokTokenResponse,
14
+ getGrokOAuthEndpoints,
15
+ readGrokOAuthErrorCode,
16
+ } from "./grok-oauth";
17
+ import {
18
+ GrokCredentials,
19
+ type GrokRefreshResult,
20
+ type GrokRefresher,
21
+ type GrokTokenRef,
22
+ decodeGrokJwt,
23
+ } from "./grok-types";
24
+
25
+ const logger = getLogger(["qgrid", "grok-refresh"]);
26
+
27
+ /** model.rs:9 DEFAULT_EARLY_INVALIDATION_SECS. */
28
+ export const GROK_REFRESH_MARGIN_MS = 300_000;
29
+
30
+ /** Upper bound of the per-account jitter added to the margin (inclusive). */
31
+ export const GROK_REFRESH_JITTER_MAX_MS = 60_000;
32
+ const REFRESH_TIMEOUT_MS = 15_000;
33
+
34
+ /** oidc/refresh.rs:21-27: the only codes that end a refresh token's life. */
35
+ const TERMINAL_ERROR_CODES = new Set(["invalid_grant", "invalid_client"]);
36
+
37
+ /** The refresh token is dead; only a new login recovers. Never retry. */
38
+ export class GrokReauthRequiredError extends Error {
39
+ override readonly name = "GrokReauthRequiredError";
40
+ constructor(
41
+ readonly oauthError: string,
42
+ readonly status: number,
43
+ ) {
44
+ super(`Grok refresh rejected: HTTP ${status} ${oauthError}`);
45
+ }
46
+ }
47
+
48
+ /** Network failure, timeout, 5xx, 429, or an unrecognized error; the refresh token still lives. */
49
+ export class GrokRefreshTransientError extends Error {
50
+ override readonly name = "GrokRefreshTransientError";
51
+ constructor(
52
+ message: string,
53
+ readonly status?: number,
54
+ readonly oauthError?: string,
55
+ ) {
56
+ super(message);
57
+ }
58
+ }
59
+
60
+ /** Stable jitter in [0, 60 s] derived from a per-account seed. */
61
+ export function grokRefreshJitterMs(seed: string): number {
62
+ const digest = createHash("sha256").update(`grok-refresh:${seed}`).digest();
63
+
64
+ return digest.readUInt32BE(0) % (GROK_REFRESH_JITTER_MAX_MS + 1);
65
+ }
66
+
67
+ /** True once `expiresAt - 300 s - jitter(userId)` is at or before `now`. */
68
+ export function grokNeedsRefresh(credentials: GrokCredentials, now: number = Date.now()): boolean {
69
+ const expiresAt = Date.parse(credentials.expiresAt);
70
+
71
+ if (!Number.isFinite(expiresAt)) return true;
72
+
73
+ return expiresAt - GROK_REFRESH_MARGIN_MS - grokRefreshJitterMs(credentials.userId) <= now;
74
+ }
75
+
76
+ function abortable<T>(promise: Promise<T>, signal: AbortSignal | undefined): Promise<T> {
77
+ if (!signal) return promise;
78
+
79
+ if (signal.aborted) return Promise.reject(signal.reason);
80
+
81
+ return new Promise<T>((resolve, reject) => {
82
+ const onAbort = (): void => reject(signal.reason);
83
+ signal.addEventListener("abort", onAbort, { once: true });
84
+ promise.then(
85
+ (value) => {
86
+ signal.removeEventListener("abort", onAbort);
87
+ resolve(value);
88
+ },
89
+ (cause: unknown) => {
90
+ signal.removeEventListener("abort", onAbort);
91
+ reject(cause);
92
+ },
93
+ );
94
+ });
95
+ }
96
+
97
+ export type GrokPersistCredentials = (next: GrokCredentials) => Promise<void>;
98
+
99
+ export interface GrokRefreshCoordinatorOptions {
100
+ fetch?: GrokFetch;
101
+ now?: () => number;
102
+ /** Persistence used by the `GrokRefresher` port methods (`ensureFresh`, `refreshAfterUnauthorized`). */
103
+ persist?: (tokenId: number, next: GrokCredentials) => Promise<void>;
104
+ }
105
+
106
+ export class GrokRefreshCoordinator implements GrokRefresher {
107
+ private readonly fetchImpl: GrokFetch;
108
+ private readonly now: () => number;
109
+ private readonly persistPort: GrokRefreshCoordinatorOptions["persist"];
110
+ private readonly inflight = new Map<number, Promise<GrokCredentials>>();
111
+ // Last credentials this process persisted per token. A caller holding an older snapshot must not
112
+ // present a refresh token the IdP has already rotated away (that reads as invalid_grant).
113
+ private readonly latest = new Map<number, GrokCredentials>();
114
+
115
+ constructor(options: GrokRefreshCoordinatorOptions = {}) {
116
+ this.fetchImpl = options.fetch ?? globalThis.fetch;
117
+ this.now = options.now ?? Date.now;
118
+ this.persistPort = options.persist;
119
+ }
120
+
121
+ needsRefresh(credentials: GrokCredentials, now: number = this.now()): boolean {
122
+ return grokNeedsRefresh(credentials, now);
123
+ }
124
+
125
+ /**
126
+ * Refreshes `tokenId`, coalescing concurrent calls into one HTTP request. Resolves only after
127
+ * `persist` has stored the new credentials.
128
+ */
129
+ refresh(
130
+ tokenId: number,
131
+ credentials: GrokCredentials,
132
+ persist: GrokPersistCredentials,
133
+ ): Promise<GrokCredentials> {
134
+ const existing = this.inflight.get(tokenId);
135
+
136
+ if (existing) return existing;
137
+
138
+ const promise = this.doRefresh(tokenId, this.newest(tokenId, credentials), persist).finally(
139
+ () => this.inflight.delete(tokenId),
140
+ );
141
+
142
+ this.inflight.set(tokenId, promise);
143
+
144
+ return promise;
145
+ }
146
+
147
+ async ensureFresh(token: GrokTokenRef, signal?: AbortSignal): Promise<GrokCredentials> {
148
+ const current = this.newest(token.tokenId, token.credentials);
149
+
150
+ if (!this.needsRefresh(current)) return current;
151
+
152
+ return abortable(this.refresh(token.tokenId, current, this.portPersist(token.tokenId)), signal);
153
+ }
154
+
155
+ async refreshAfterUnauthorized(
156
+ token: GrokTokenRef,
157
+ signal?: AbortSignal,
158
+ ): Promise<GrokRefreshResult> {
159
+ const inflight = this.inflight.get(token.tokenId);
160
+ const latest = this.latest.get(token.tokenId);
161
+
162
+ // Another caller already replaced the rejected access token: reuse it instead of refreshing.
163
+ if (
164
+ !inflight &&
165
+ latest &&
166
+ latest.accessToken !== token.credentials.accessToken &&
167
+ !this.needsRefresh(latest)
168
+ )
169
+ return { status: "refreshed", credentials: latest };
170
+
171
+ try {
172
+ const credentials = await abortable(
173
+ this.refresh(token.tokenId, token.credentials, this.portPersist(token.tokenId)),
174
+ signal,
175
+ );
176
+
177
+ return { status: "refreshed", credentials };
178
+ } catch (error) {
179
+ if (error instanceof GrokReauthRequiredError)
180
+ return { status: "reauth_required", reason: error.oauthError };
181
+ throw error;
182
+ }
183
+ }
184
+
185
+ private portPersist(tokenId: number): GrokPersistCredentials {
186
+ const persist = this.persistPort;
187
+
188
+ if (!persist)
189
+ return () =>
190
+ Promise.reject(new Error("GrokRefreshCoordinator was created without a persist port"));
191
+
192
+ return (next) => persist(tokenId, next);
193
+ }
194
+
195
+ private newest(tokenId: number, credentials: GrokCredentials): GrokCredentials {
196
+ const latest = this.latest.get(tokenId);
197
+
198
+ if (!latest || latest.userId !== credentials.userId) return credentials;
199
+
200
+ return Date.parse(latest.expiresAt) > Date.parse(credentials.expiresAt) ? latest : credentials;
201
+ }
202
+
203
+ private async doRefresh(
204
+ tokenId: number,
205
+ credentials: GrokCredentials,
206
+ persist: GrokPersistCredentials,
207
+ ): Promise<GrokCredentials> {
208
+ const { tokenEndpoint } = await getGrokOAuthEndpoints({
209
+ fetch: this.fetchImpl,
210
+ now: this.now,
211
+ });
212
+
213
+ let response: Response;
214
+
215
+ try {
216
+ response = await this.fetchImpl(tokenEndpoint, {
217
+ method: "POST",
218
+ // oidc/protocol.rs:493-512: form body only; the shared client contributes the UA.
219
+ headers: {
220
+ "user-agent": grokShellUserAgent(),
221
+ "content-type": "application/x-www-form-urlencoded",
222
+ accept: "application/json",
223
+ },
224
+ body: new URLSearchParams([
225
+ ["grant_type", "refresh_token"],
226
+ ["refresh_token", credentials.refreshToken],
227
+ ["client_id", credentials.clientId],
228
+ ]),
229
+ signal: AbortSignal.timeout(REFRESH_TIMEOUT_MS),
230
+ });
231
+ } catch (error) {
232
+ const reason = error instanceof Error ? error.name : "unknown";
233
+ logger.warn(`grok refresh network failure token=${tokenId} reason=${reason}`);
234
+ throw new GrokRefreshTransientError(`Grok refresh request failed (${reason})`);
235
+ }
236
+
237
+ if (!response.ok) {
238
+ const oauthError = await readGrokOAuthErrorCode(response);
239
+ logger.warn(
240
+ `grok refresh failed token=${tokenId} status=${response.status} code=${oauthError ?? "unknown"}`,
241
+ );
242
+
243
+ // oidc/protocol.rs:413-430: 5xx and 429 are always transient; otherwise only a terminal code
244
+ // ends the token.
245
+ if (
246
+ response.status < 500 &&
247
+ response.status !== 429 &&
248
+ oauthError !== undefined &&
249
+ TERMINAL_ERROR_CODES.has(oauthError)
250
+ )
251
+ throw new GrokReauthRequiredError(oauthError, response.status);
252
+ throw new GrokRefreshTransientError(
253
+ `Grok refresh failed: HTTP ${response.status} ${oauthError ?? "unknown"}`,
254
+ response.status,
255
+ oauthError,
256
+ );
257
+ }
258
+
259
+ let body: unknown;
260
+
261
+ try {
262
+ body = await response.json();
263
+ } catch {
264
+ throw new GrokRefreshTransientError("Grok refresh response is not JSON", response.status);
265
+ }
266
+
267
+ const tokens = GrokTokenResponse.safeParse(body);
268
+
269
+ if (!tokens.success)
270
+ throw new GrokRefreshTransientError(
271
+ "Grok refresh response failed validation",
272
+ response.status,
273
+ );
274
+
275
+ const rotated = Boolean(tokens.data.refresh_token);
276
+ const exp = decodeGrokJwt(tokens.data.access_token)?.payload.exp;
277
+
278
+ const expiresIn = tokens.data.expires_in;
279
+
280
+ const expiresAtMs =
281
+ expiresIn !== undefined && expiresIn !== null
282
+ ? this.now() + expiresIn * 1000
283
+ : exp !== undefined
284
+ ? exp * 1000
285
+ : undefined;
286
+
287
+ if (expiresAtMs === undefined || !Number.isFinite(expiresAtMs))
288
+ throw new GrokRefreshTransientError("Grok refresh response has no expiry", response.status);
289
+
290
+ const next = GrokCredentials.safeParse({
291
+ ...credentials,
292
+ accessToken: tokens.data.access_token,
293
+ // oidc/refresh.rs:260-264: keep the old refresh token when the IdP did not rotate it.
294
+ refreshToken: tokens.data.refresh_token ?? credentials.refreshToken,
295
+ expiresAt: new Date(expiresAtMs).toISOString(),
296
+ });
297
+
298
+ if (!next.success) {
299
+ logger.error(
300
+ `grok refresh returned unusable credentials token=${tokenId} rotated=${rotated}`,
301
+ );
302
+ throw new GrokRefreshTransientError(
303
+ "Grok refresh returned an access token that is not an auth.x.ai at+jwt",
304
+ response.status,
305
+ );
306
+ }
307
+
308
+ try {
309
+ await persist(next.data);
310
+ } catch (error) {
311
+ // A rotated refresh token that is not persisted is lost: the next refresh with the old one
312
+ // fails with invalid_grant. Keep it in memory so this process can still use it.
313
+ if (rotated) this.latest.set(tokenId, next.data);
314
+ logger.error(
315
+ `grok refresh persist FAILED token=${tokenId} rotated=${rotated} err=${error instanceof Error ? error.name : "unknown"}`,
316
+ );
317
+ throw error;
318
+ }
319
+
320
+ this.latest.set(tokenId, next.data);
321
+ logger.info(`grok token ${tokenId} refreshed (rotated=${rotated})`);
322
+
323
+ return next.data;
324
+ }
325
+ }
@@ -0,0 +1,252 @@
1
+ import { describe, expect, it } from "vitest";
2
+
3
+ import { GROK_OAUTH_CLIENT_ID, GROK_OAUTH_SCOPES } from "./grok-constants";
4
+ import {
5
+ GrokApiImageModelCatalog,
6
+ GrokApiLanguageModelCatalog,
7
+ GrokBillingConfig,
8
+ GrokCredentials,
9
+ GrokImageResponse,
10
+ GrokModelsResponse,
11
+ GrokSettings,
12
+ GrokSttResponse,
13
+ GrokTtsRequest,
14
+ GrokUserSubscription,
15
+ GrokVideoStatus,
16
+ decodeGrokJwt,
17
+ grokCatalogModels,
18
+ grokVideoUrl,
19
+ isGrokAccessToken,
20
+ } from "./grok-types";
21
+
22
+ function segment(value: unknown): string {
23
+ return Buffer.from(JSON.stringify(value)).toString("base64url");
24
+ }
25
+
26
+ function fakeJwt(header: Record<string, unknown>, payload: Record<string, unknown>): string {
27
+ return `${segment(header)}.${segment(payload)}.c2lnbmF0dXJl`;
28
+ }
29
+
30
+ const validJwt = fakeJwt(
31
+ { alg: "RS256", typ: "at+jwt" },
32
+ { iss: "https://auth.x.ai", sub: "user", exp: 2_000_000_000 },
33
+ );
34
+
35
+ const credentials = {
36
+ authMode: "oauth",
37
+ accessToken: validJwt,
38
+ refreshToken: "refresh",
39
+ expiresAt: "2026-10-16T09:00:00Z",
40
+ issuer: "https://auth.x.ai",
41
+ clientId: GROK_OAUTH_CLIENT_ID,
42
+ scopes: [...GROK_OAUTH_SCOPES],
43
+ userId: "19bfa1b2-a163-9e0c-8de1-832a73e2695e",
44
+ sessionId: "22222222-2222-4222-8222-222222222222",
45
+ agentId: "33333333-3333-4333-8333-333333333333",
46
+ subscriptionTier: "SuperGrokPlus",
47
+ };
48
+
49
+ describe("GrokCredentials", () => {
50
+ it("accepts a fabricated at+jwt from auth.x.ai", () => {
51
+ expect(GrokCredentials.parse(credentials)).toEqual(credentials);
52
+ expect(decodeGrokJwt(validJwt)?.payload.iss).toBe("https://auth.x.ai");
53
+ });
54
+
55
+ it("rejects an API key", () => {
56
+ expect(isGrokAccessToken("xai-0123456789abcdefghijklmnop")).toBe(false);
57
+ expect(GrokCredentials.safeParse({ ...credentials, accessToken: "xai-abc" }).success).toBe(
58
+ false,
59
+ );
60
+ });
61
+
62
+ it("rejects a JWT from another issuer or of another type", () => {
63
+ const foreign = fakeJwt({ alg: "RS256", typ: "at+jwt" }, { iss: "https://accounts.example" });
64
+ const idToken = fakeJwt({ alg: "RS256", typ: "JWT" }, { iss: "https://auth.x.ai" });
65
+ expect(GrokCredentials.safeParse({ ...credentials, accessToken: foreign }).success).toBe(false);
66
+ expect(GrokCredentials.safeParse({ ...credentials, accessToken: idToken }).success).toBe(false);
67
+ });
68
+
69
+ it("rejects malformed JWT segments", () => {
70
+ expect(isGrokAccessToken("a.b")).toBe(false);
71
+ expect(isGrokAccessToken("!!!.???.sig")).toBe(false);
72
+ expect(isGrokAccessToken(`${segment("x")}.${segment("y")}.sig`)).toBe(false);
73
+ });
74
+
75
+ it("requires userId, an RFC 3339 expiry and the auth.x.ai issuer", () => {
76
+ const { userId: _userId, ...withoutUser } = credentials;
77
+ expect(GrokCredentials.safeParse(withoutUser).success).toBe(false);
78
+ expect(GrokCredentials.safeParse({ ...credentials, expiresAt: "tomorrow" }).success).toBe(
79
+ false,
80
+ );
81
+ expect(GrokCredentials.safeParse({ ...credentials, expiresAt: 1_800_000_000 }).success).toBe(
82
+ false,
83
+ );
84
+ expect(
85
+ GrokCredentials.safeParse({ ...credentials, issuer: "https://auth.example" }).success,
86
+ ).toBe(false);
87
+ expect(GrokCredentials.safeParse({ ...credentials, authMode: "api_key" }).success).toBe(false);
88
+ });
89
+ });
90
+
91
+ describe("Grok proxy response schemas (U0 shapes, redacted)", () => {
92
+ it("parses /billing?format=credits", () => {
93
+ const billing = GrokBillingConfig.parse({
94
+ config: {
95
+ currentPeriod: {
96
+ type: "USAGE_PERIOD_TYPE_WEEKLY",
97
+ start: "2026-10-12T00:00:00Z",
98
+ end: "2026-10-19T00:00:00Z",
99
+ },
100
+ creditUsagePercent: 1,
101
+ productUsage: [{ product: "GrokBot", usagePercent: 1 }],
102
+ isUnifiedBillingUser: true,
103
+ prepaidBalance: { val: 0 },
104
+ topUpMethod: "TOP_UP_METHOD_UNSPECIFIED",
105
+ billingPeriodStart: "2026-10-12T00:00:00Z",
106
+ billingPeriodEnd: "2026-10-19T00:00:00Z",
107
+ },
108
+ });
109
+ expect(billing.config.creditUsagePercent).toBe(1);
110
+ expect(billing.config).not.toHaveProperty("onDemandEnabled");
111
+ expect(billing.config.topUpMethod).toBe("TOP_UP_METHOD_UNSPECIFIED");
112
+ expect(GrokBillingConfig.safeParse({ config: { credit_usage_percent: 1 } }).success).toBe(
113
+ false,
114
+ );
115
+ });
116
+
117
+ it("parses /settings with on_demand_enabled null", () => {
118
+ const settings = GrokSettings.parse({
119
+ min_client_version: "1.0.13",
120
+ force_update: true,
121
+ default_model: "grok-4.7",
122
+ subscription_tier_display: "SuperGrok Plus",
123
+ on_demand_enabled: null,
124
+ image_gen_enabled: true,
125
+ video_gen_enabled: true,
126
+ voice_mode_enabled: true,
127
+ accept_request_encodings: ["zstd"],
128
+ tips: ["passthrough"],
129
+ });
130
+ expect(settings.on_demand_enabled).toBeNull();
131
+ expect(settings.tips).toEqual(["passthrough"]);
132
+ expect(GrokSettings.safeParse({ on_demand_enabled: "yes" }).success).toBe(false);
133
+ });
134
+
135
+ it("parses /user?include=subscription", () => {
136
+ const user = GrokUserSubscription.parse({
137
+ userId: "19bfa1b2-a163-9e0c-8de1-832a73e2695e",
138
+ email: "user@example.com",
139
+ subscriptionTier: "SuperGrokPlus",
140
+ hasGrokCodeAccess: true,
141
+ teamId: null,
142
+ });
143
+ expect(user.subscriptionTier).toBe("SuperGrokPlus");
144
+ expect(user.codingDataRetentionOptOut).toBeUndefined();
145
+ expect(
146
+ GrokUserSubscription.parse({ userId: user.userId, codingDataRetentionOptOut: true })
147
+ .codingDataRetentionOptOut,
148
+ ).toBe(true);
149
+ expect(
150
+ GrokUserSubscription.safeParse({ userId: user.userId, codingDataRetentionOptOut: "yes" })
151
+ .success,
152
+ ).toBe(false);
153
+ expect(GrokUserSubscription.safeParse({ subscriptionTier: "x" }).success).toBe(false);
154
+ });
155
+
156
+ it("parses proxy /models", () => {
157
+ const efforts = ["xhigh", "high", "medium", "low"].map((id) => ({
158
+ id,
159
+ value: id,
160
+ label: id,
161
+ description: id,
162
+ default: id === "high",
163
+ }));
164
+ const models = GrokModelsResponse.parse({
165
+ object: "list",
166
+ data: ["grok-4.7", "grok-4.7-build-fast", "grok-4.6", "grok-4.5"].map((id) => ({
167
+ id,
168
+ object: "model",
169
+ context_window: 256000,
170
+ context_windows: [256000, 500000],
171
+ api_backend: "responses",
172
+ reasoning_effort: "high",
173
+ supports_reasoning_effort: true,
174
+ reasoning_efforts: efforts,
175
+ })),
176
+ });
177
+ expect(models.data.map((model) => model.id)).toEqual([
178
+ "grok-4.7",
179
+ "grok-4.7-build-fast",
180
+ "grok-4.6",
181
+ "grok-4.5",
182
+ ]);
183
+ });
184
+ });
185
+
186
+ describe("api.x.ai schemas", () => {
187
+ it("parses the language-model price catalog under either envelope", () => {
188
+ const model = {
189
+ id: "grok-4.7",
190
+ aliases: ["grok-4.7-latest"],
191
+ prompt_text_token_price: 20000,
192
+ cached_prompt_text_token_price: 5000,
193
+ completion_text_token_price: 60000,
194
+ prompt_image_token_price: 20000,
195
+ prompt_text_token_price_long_context: 40000,
196
+ long_context_threshold: 200000,
197
+ };
198
+ for (const envelope of [{ models: [model] }, { data: [model], object: "list" }]) {
199
+ const catalog = GrokApiLanguageModelCatalog.parse(envelope);
200
+ expect(grokCatalogModels(catalog)[0]?.prompt_text_token_price).toBe(20000);
201
+ }
202
+ });
203
+
204
+ it("parses the image-model price catalog", () => {
205
+ const catalog = GrokApiImageModelCatalog.parse({
206
+ models: [
207
+ {
208
+ id: "grok-imagine-image-quality",
209
+ image_price: 500000000,
210
+ pricing: [{ quality: "quality", resolution: "1k", price_per_image: 500000000 }],
211
+ },
212
+ ],
213
+ });
214
+ expect(grokCatalogModels(catalog)[0]?.pricing?.[0]?.price_per_image).toBe(500000000);
215
+ });
216
+
217
+ it("parses image, video, STT and TTS shapes", () => {
218
+ expect(
219
+ GrokImageResponse.parse({
220
+ data: [{ b64_json: "AAAA", mime_type: "image/jpeg" }],
221
+ usage: { cost_in_usd_ticks: 500000000 },
222
+ }).usage?.cost_in_usd_ticks,
223
+ ).toBe(500000000);
224
+ expect(GrokImageResponse.safeParse({ data: [] }).success).toBe(false);
225
+ expect(
226
+ grokVideoUrl(GrokVideoStatus.parse({ status: "done", video: { url: "https://v/1.mp4" } })),
227
+ ).toBe("https://v/1.mp4");
228
+ expect(
229
+ grokVideoUrl(
230
+ GrokVideoStatus.parse({
231
+ status: "done",
232
+ video: { url: null, file_output: { public_url: "https://v/2.mp4" } },
233
+ }),
234
+ ),
235
+ ).toBe("https://v/2.mp4");
236
+ // @ai-sdk/xai never reads a top-level file_output.
237
+ expect(
238
+ grokVideoUrl(
239
+ GrokVideoStatus.parse({ status: "done", file_output: { public_url: "https://v/3.mp4" } }),
240
+ ),
241
+ ).toBeUndefined();
242
+ expect(grokVideoUrl(GrokVideoStatus.parse({ status: "pending" }))).toBeUndefined();
243
+ expect(GrokSttResponse.parse({ text: "", language: "", duration: 1 }).duration).toBe(1);
244
+ expect(
245
+ GrokTtsRequest.safeParse({ text: "hi", voice: "ara", language: "en", format: "mp3" })
246
+ .success,
247
+ ).toBe(true);
248
+ expect(GrokTtsRequest.safeParse({ text: "hi", voice: "ara", format: "mp3" }).success).toBe(
249
+ false,
250
+ );
251
+ });
252
+ });