@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
@@ -1,9 +1,11 @@
1
1
  /** tokens_changed LISTEN subscriber. */
2
2
  import { getLogger } from "@logtape/logtape";
3
3
  import { Client, type ClientConfig } from "pg";
4
+ import { z } from "zod";
4
5
 
6
+ import { type TokenSubsetA } from "../sonamu.generated";
5
7
  import { TokenModel } from "../token/token.model";
6
- import { type AnthropicCredentials, type OpenAICredentials } from "../token/token.types";
8
+ import { AnthropicCredentials, OpenAICredentials } from "../token/token.types";
7
9
  import { type QgridDispatcherClass } from "./qgrid.dispatcher";
8
10
  import { type SubscriberStatus } from "./qgrid.types";
9
11
 
@@ -15,7 +17,25 @@ const CONNECTION_TIMEOUT_MS = 5_000;
15
17
  const BACKOFF_BASE_MS = 500;
16
18
  const BACKOFF_CAP_MS = 30_000;
17
19
 
18
- type Payload = { op: "INSERT" | "UPDATE" | "DELETE"; id: number };
20
+ const NotifyPayload = z.object({ op: z.enum(["INSERT", "UPDATE", "DELETE"]), id: z.number() });
21
+
22
+ type CredentialRow = Pick<TokenSubsetA, "name" | "credentials">;
23
+
24
+ function openAICredentialsOf(row: CredentialRow): OpenAICredentials | undefined {
25
+ const parsed = OpenAICredentials.safeParse(row.credentials);
26
+
27
+ if (!parsed.success) logger.warn(`skipping token ${row.name}: credentials are not OpenAI`);
28
+
29
+ return parsed.data;
30
+ }
31
+
32
+ function anthropicCredentialsOf(row: CredentialRow): AnthropicCredentials | undefined {
33
+ const parsed = AnthropicCredentials.safeParse(row.credentials);
34
+
35
+ if (!parsed.success) logger.warn(`skipping token ${row.name}: credentials are not Anthropic`);
36
+
37
+ return parsed.data;
38
+ }
19
39
 
20
40
  export class TokenSubscriber {
21
41
  client: Client | null = null;
@@ -37,7 +57,9 @@ export class TokenSubscriber {
37
57
  try {
38
58
  this.tokenChangeHandler?.();
39
59
  } catch (error) {
40
- logger.warn(`token change handler failed: ${(error as Error).message}`);
60
+ logger.warn(
61
+ `token change handler failed: ${error instanceof Error ? error.message : String(error)}`,
62
+ );
41
63
  }
42
64
  }
43
65
 
@@ -47,24 +69,31 @@ export class TokenSubscriber {
47
69
 
48
70
  try {
49
71
  await this.connectAndReconcile();
72
+
50
73
  return true;
51
74
  } catch (e) {
52
- logger.warn(`subscriber start failed: ${(e as Error).message}, scheduling retry`);
75
+ logger.warn(
76
+ `subscriber start failed: ${e instanceof Error ? e.message : String(e)}, scheduling retry`,
77
+ );
53
78
  this.retryLater();
79
+
54
80
  return false;
55
81
  }
56
82
  }
57
83
 
58
84
  async stop(): Promise<void> {
59
85
  this.shutdownRequested = true;
86
+
60
87
  if (this.reconcileTimer) {
61
88
  clearInterval(this.reconcileTimer);
62
89
  this.reconcileTimer = null;
63
90
  }
91
+
64
92
  if (this.reconnectTimer) {
65
93
  clearTimeout(this.reconnectTimer);
66
94
  this.reconnectTimer = null;
67
95
  }
96
+
68
97
  await this.closeClient();
69
98
  }
70
99
 
@@ -83,7 +112,7 @@ export class TokenSubscriber {
83
112
  // LISTEN/NOTIFY 는 끊긴 동안 유실될 수 있어 주기적으로 DB 기준으로 맞춘다.
84
113
  this.reconcileTimer = setInterval(() => {
85
114
  this.reconcile().catch((e) =>
86
- logger.warn(`periodic reconcile failed: ${(e as Error).message}`),
115
+ logger.warn(`periodic reconcile failed: ${e instanceof Error ? e.message : String(e)}`),
87
116
  );
88
117
  }, RECONCILE_INTERVAL_MS);
89
118
  }
@@ -113,7 +142,7 @@ export class TokenSubscriber {
113
142
  client.on("notification", (msg) => {
114
143
  if (msg.channel !== TOKENS_CHANGED || !msg.payload) return;
115
144
  this.handleNotification(msg.payload).catch((e) =>
116
- logger.warn(`handle NOTIFY failed: ${(e as Error).message}`),
145
+ logger.warn(`handle NOTIFY failed: ${e instanceof Error ? e.message : String(e)}`),
117
146
  );
118
147
  });
119
148
 
@@ -135,6 +164,7 @@ export class TokenSubscriber {
135
164
 
136
165
  retryLater(): void {
137
166
  if (this.shutdownRequested) return;
167
+
138
168
  if (this.reconnectTimer) return;
139
169
 
140
170
  void this.closeClient();
@@ -145,7 +175,7 @@ export class TokenSubscriber {
145
175
  this.reconnectTimer = setTimeout(() => {
146
176
  this.reconnectTimer = null;
147
177
  this.connectAndReconcile().catch((e) => {
148
- logger.warn(`subscriber reconnect failed: ${(e as Error).message}`);
178
+ logger.warn(`subscriber reconnect failed: ${e instanceof Error ? e.message : String(e)}`);
149
179
  this.retryLater();
150
180
  });
151
181
  }, delay);
@@ -153,6 +183,7 @@ export class TokenSubscriber {
153
183
 
154
184
  async closeClient(): Promise<void> {
155
185
  const client = this.client;
186
+
156
187
  if (!client) return;
157
188
 
158
189
  this.client = null;
@@ -165,68 +196,83 @@ export class TokenSubscriber {
165
196
  }
166
197
 
167
198
  async handleNotificationNow(payloadJson: string): Promise<void> {
168
- const payload = JSON.parse(payloadJson) as Payload;
199
+ const payload = NotifyPayload.parse(JSON.parse(payloadJson));
200
+
169
201
  const previousRow = this.tokenChangeHandler
170
202
  ? this.dispatcher.tokens.get(payload.id)
171
203
  : undefined;
204
+
172
205
  const wasKeepaliveTarget = previousRow?.active === true && previousRow.provider === "anthropic";
206
+
173
207
  if (payload.op === "DELETE") {
174
208
  this.dispatcher.tokens.delete(payload.id);
175
209
  await this.dispatcher.openaiDispatcher
176
210
  ?.onTokenRemoved(payload.id)
177
- .catch((e) => logger.warn(`openai token remove failed: ${(e as Error).message}`));
178
- // 삭제된 토큰의 provider를 모를 수 있으므로 양쪽 풀에서 제거한다.
211
+ .catch((e) =>
212
+ logger.warn(`openai token remove failed: ${e instanceof Error ? e.message : String(e)}`),
213
+ );
214
+ // 삭제된 토큰의 provider를 모를 수 있으므로 모든 풀에서 제거한다.
179
215
  this.dispatcher.anthropicDispatcher?.onTokenRemoved(payload.id);
216
+ await this.removeGrokToken(payload.id);
180
217
  logger.info(`NOTIFY ${payload.op} id=${payload.id} → removed from cache`);
218
+
181
219
  if (previousRow === undefined || wasKeepaliveTarget) this.notifyTokensChanged();
220
+
182
221
  return;
183
222
  }
223
+
184
224
  const row = await TokenModel.findOne("A", { id: payload.id });
225
+
185
226
  if (!row) {
186
227
  this.dispatcher.tokens.delete(payload.id);
187
228
  await this.dispatcher.openaiDispatcher
188
229
  ?.onTokenRemoved(payload.id)
189
- .catch((e) => logger.warn(`openai token remove failed: ${(e as Error).message}`));
230
+ .catch((e) =>
231
+ logger.warn(`openai token remove failed: ${e instanceof Error ? e.message : String(e)}`),
232
+ );
190
233
  this.dispatcher.anthropicDispatcher?.onTokenRemoved(payload.id);
234
+ await this.removeGrokToken(payload.id);
191
235
  logger.info(`NOTIFY ${payload.op} id=${payload.id} → missing, removed from cache`);
236
+
192
237
  if (previousRow === undefined || wasKeepaliveTarget) this.notifyTokensChanged();
238
+
193
239
  return;
194
240
  }
195
241
 
196
242
  this.dispatcher.tokens.set(payload.id, row);
197
243
 
198
244
  if (row.provider === "openai") {
199
- const creds = row.credentials as Record<string, unknown>;
245
+ const creds = openAICredentialsOf(row);
200
246
  const openaiDispatcher = this.dispatcher.openaiDispatcher;
201
- if (payload.op === "INSERT" && row.active) {
247
+
248
+ if (creds === undefined) {
249
+ openaiDispatcher?.onTokenDeactivated(payload.id);
250
+ } else if (payload.op === "INSERT" && row.active) {
202
251
  await openaiDispatcher
203
- ?.onTokenAdded(
204
- payload.id,
205
- row.name,
206
- creds as OpenAICredentials,
207
- row.quota_threshold,
208
- row.weight,
209
- )
210
- .catch((e) => logger.warn(`openai token add failed: ${(e as Error).message}`));
252
+ ?.onTokenAdded(payload.id, row.name, creds, row.quota_threshold, row.weight)
253
+ .catch((e) =>
254
+ logger.warn(`openai token add failed: ${e instanceof Error ? e.message : String(e)}`),
255
+ );
211
256
  } else if (row.active) {
212
257
  if (openaiDispatcher) {
213
258
  await openaiDispatcher
214
- .onTokenUpdated(
215
- payload.id,
216
- row.name,
217
- creds as OpenAICredentials,
218
- row.quota_threshold,
219
- row.weight,
220
- )
259
+ .onTokenUpdated(payload.id, row.name, creds, row.quota_threshold, row.weight)
221
260
  .then(() => openaiDispatcher.onTokenActivated(payload.id))
222
- .catch((e) => logger.warn(`openai token update failed: ${(e as Error).message}`));
261
+ .catch((e) =>
262
+ logger.warn(
263
+ `openai token update failed: ${e instanceof Error ? e.message : String(e)}`,
264
+ ),
265
+ );
223
266
  }
224
267
  } else {
225
268
  openaiDispatcher?.onTokenDeactivated(payload.id);
226
269
  }
227
270
  } else if (row.provider === "anthropic") {
228
- const creds = row.credentials as AnthropicCredentials;
229
- if (payload.op === "INSERT" && row.active) {
271
+ const creds = anthropicCredentialsOf(row);
272
+
273
+ if (creds === undefined) {
274
+ this.dispatcher.anthropicDispatcher?.onTokenRemoved(payload.id);
275
+ } else if (payload.op === "INSERT" && row.active) {
230
276
  this.dispatcher.anthropicDispatcher?.onTokenAdded(
231
277
  payload.id,
232
278
  row.name,
@@ -245,9 +291,43 @@ export class TokenSubscriber {
245
291
  } else {
246
292
  this.dispatcher.anthropicDispatcher?.onTokenRemoved(payload.id);
247
293
  }
294
+ } else if (row.provider === "grok") {
295
+ const grokDispatcher = this.dispatcher.grokDispatcher;
296
+
297
+ if (grokDispatcher) {
298
+ await Promise.resolve()
299
+ .then(() => {
300
+ if (payload.op === "INSERT" && row.active) {
301
+ return grokDispatcher.onTokenAdded(
302
+ payload.id,
303
+ row.name,
304
+ row.credentials,
305
+ row.quota_threshold,
306
+ row.weight,
307
+ );
308
+ }
309
+
310
+ if (row.active) {
311
+ return grokDispatcher.onTokenUpdated(
312
+ payload.id,
313
+ row.name,
314
+ row.credentials,
315
+ row.quota_threshold,
316
+ row.weight,
317
+ );
318
+ }
319
+
320
+ return grokDispatcher.onTokenRemoved(payload.id);
321
+ })
322
+ .catch((e) =>
323
+ logger.warn(`grok token sync failed: ${e instanceof Error ? e.message : String(e)}`),
324
+ );
325
+ }
248
326
  }
327
+
249
328
  logger.info(`NOTIFY ${payload.op} id=${payload.id} (${row.name}) active=${row.active}`);
250
329
  const isKeepaliveTarget = row.active && row.provider === "anthropic";
330
+
251
331
  if (wasKeepaliveTarget !== isKeepaliveTarget) this.notifyTokensChanged();
252
332
  }
253
333
 
@@ -258,37 +338,77 @@ export class TokenSubscriber {
258
338
  async reconcileNow(): Promise<void> {
259
339
  const previousAnthropicIds = this.tokenChangeHandler
260
340
  ? new Set(
261
- [...this.dispatcher.tokens.values()]
262
- .filter((row) => row.active && row.provider === "anthropic")
263
- .map((row) => row.id),
341
+ [...this.dispatcher.tokens.values()].flatMap((row) =>
342
+ row.active && row.provider === "anthropic" ? [row.id] : [],
343
+ ),
264
344
  )
265
345
  : null;
346
+
266
347
  await TokenModel.deactivateExpiredTokens();
267
348
  const rows = await TokenModel.findActive("A");
268
349
  this.dispatcher.replaceCache(rows);
269
- const anthropicRows = rows
270
- .filter((r) => r.provider === "anthropic")
271
- .map((r) => ({
272
- id: r.id,
273
- name: r.name,
274
- credentials: r.credentials as AnthropicCredentials,
275
- quotaThreshold: r.quota_threshold,
276
- weight: r.weight,
277
- }));
350
+
351
+ const anthropicRows = rows.flatMap((r) => {
352
+ const credentials = r.provider === "anthropic" ? anthropicCredentialsOf(r) : undefined;
353
+
354
+ return credentials === undefined
355
+ ? []
356
+ : [
357
+ {
358
+ id: r.id,
359
+ name: r.name,
360
+ credentials,
361
+ quotaThreshold: r.quota_threshold,
362
+ weight: r.weight,
363
+ },
364
+ ];
365
+ });
366
+
278
367
  this.dispatcher.anthropicDispatcher?.replaceTokens(anthropicRows);
279
- const openaiRows = rows
280
- .filter((r) => r.provider === "openai")
281
- .map((r) => ({
282
- id: r.id,
283
- name: r.name,
284
- credentials: r.credentials as OpenAICredentials,
285
- quotaThreshold: r.quota_threshold,
286
- weight: r.weight,
287
- }));
368
+
369
+ const openaiRows = rows.flatMap((r) => {
370
+ const credentials = r.provider === "openai" ? openAICredentialsOf(r) : undefined;
371
+
372
+ return credentials === undefined
373
+ ? []
374
+ : [
375
+ {
376
+ id: r.id,
377
+ name: r.name,
378
+ credentials,
379
+ quotaThreshold: r.quota_threshold,
380
+ weight: r.weight,
381
+ },
382
+ ];
383
+ });
384
+
288
385
  await this.dispatcher.openaiDispatcher
289
386
  ?.replaceTokens(openaiRows)
290
- .catch((e) => logger.warn(`openai reconcile failed: ${(e as Error).message}`));
387
+ .catch((e) =>
388
+ logger.warn(`openai reconcile failed: ${e instanceof Error ? e.message : String(e)}`),
389
+ );
390
+ const grokDispatcher = this.dispatcher.grokDispatcher;
391
+
392
+ if (grokDispatcher) {
393
+ const grokRows = rows
394
+ .filter((r) => r.provider === "grok")
395
+ .map((r) => ({
396
+ id: r.id,
397
+ name: r.name,
398
+ credentials: r.credentials,
399
+ quotaThreshold: r.quota_threshold,
400
+ weight: r.weight,
401
+ }));
402
+
403
+ await Promise.resolve()
404
+ .then(() => grokDispatcher.replaceTokens(grokRows))
405
+ .catch((e) =>
406
+ logger.warn(`grok reconcile failed: ${e instanceof Error ? e.message : String(e)}`),
407
+ );
408
+ }
409
+
291
410
  this.lastReconcileAt = new Date();
411
+
292
412
  if (
293
413
  previousAnthropicIds &&
294
414
  (previousAnthropicIds.size !== anthropicRows.length ||
@@ -298,9 +418,22 @@ export class TokenSubscriber {
298
418
  }
299
419
  }
300
420
 
421
+ // Grok dispatcher 는 선택적으로 주입된다. 동기/비동기 구현 모두의 실패를 격리한다.
422
+ async removeGrokToken(id: number): Promise<void> {
423
+ const grokDispatcher = this.dispatcher.grokDispatcher;
424
+
425
+ if (!grokDispatcher) return;
426
+ await Promise.resolve()
427
+ .then(() => grokDispatcher.onTokenRemoved(id))
428
+ .catch((e) =>
429
+ logger.warn(`grok token remove failed: ${e instanceof Error ? e.message : String(e)}`),
430
+ );
431
+ }
432
+
301
433
  enqueueOperation(operation: () => Promise<void>): Promise<void> {
302
434
  const next = this.operationChain.then(operation, operation);
303
435
  this.operationChain = next.catch(() => {});
436
+
304
437
  return next;
305
438
  }
306
439
  }
@@ -55,6 +55,7 @@ export async function ensureTokensTrigger(connConfig: ClientConfig): Promise<voi
55
55
  });
56
56
 
57
57
  let lockAcquired = false;
58
+
58
59
  try {
59
60
  await client.connect();
60
61
  await client.query("SET search_path TO public");
@@ -77,6 +78,7 @@ export async function ensureTokensTrigger(connConfig: ClientConfig): Promise<voi
77
78
  ])
78
79
  .catch(() => {});
79
80
  }
81
+
80
82
  await client.end().catch(() => {});
81
83
  }
82
84
  }
@@ -33,7 +33,7 @@ type TimerHandle = ReturnType<typeof setTimeout>;
33
33
  export type TokenWindowKeepaliveDeps = {
34
34
  findTokens: () => Promise<KeepaliveToken[]>;
35
35
  readUsage: (tokenId: number) => Promise<UsageResponse>;
36
- dispatch: (input: InternalQueryInput) => Promise<unknown>;
36
+ dispatch: (input: InternalQueryInput) => Promise<void>;
37
37
  readSetting: typeof getSetting;
38
38
  isRunnerEnabled: () => boolean;
39
39
  now: () => number;
@@ -44,7 +44,9 @@ export type TokenWindowKeepaliveDeps = {
44
44
  const defaultDeps: TokenWindowKeepaliveDeps = {
45
45
  findTokens: () => TokenModel.findActiveByProvider("A", "anthropic"),
46
46
  readUsage: (tokenId) => QgridFrame.usage(tokenId),
47
- dispatch: (input) => QgridFrame.query(input),
47
+ dispatch: async (input) => {
48
+ await QgridFrame.query(input);
49
+ },
48
50
  readSetting: getSetting,
49
51
  isRunnerEnabled: () => process.env[KEEPALIVE_ENV_KEY] === "true",
50
52
  now: () => Date.now(),
@@ -58,6 +60,7 @@ let generation = 0;
58
60
 
59
61
  function clearTokenTimer(tokenId: number): void {
60
62
  const timer = timers.get(tokenId);
63
+
61
64
  if (!timer) return;
62
65
  timer.clear(timer.handle);
63
66
  timers.delete(tokenId);
@@ -72,17 +75,22 @@ function scheduleToken(
72
75
  ): void {
73
76
  if (generation !== expectedGeneration) return;
74
77
  clearTokenTimer(token.id);
78
+
75
79
  const handle = deps.setTimer(
76
80
  () => {
77
81
  timers.delete(token.id);
82
+
78
83
  if (generation !== expectedGeneration) return;
79
84
  void inspectToken(token, deps, expectedGeneration, allowFire).catch((error) => {
80
- logger.warn(`keepalive cycle failed for ${token.name}: ${(error as Error).message}`);
85
+ logger.warn(
86
+ `keepalive cycle failed for ${token.name}: ${error instanceof Error ? error.message : String(error)}`,
87
+ );
81
88
  scheduleFallback(token, deps, expectedGeneration);
82
89
  });
83
90
  },
84
91
  Math.max(0, delayMs),
85
92
  );
93
+
86
94
  timers.set(token.id, { handle, clear: deps.clearTimer });
87
95
  }
88
96
 
@@ -114,40 +122,55 @@ async function inspectToken(
114
122
  ): Promise<void> {
115
123
  const now = deps.now();
116
124
  const guard = fireGuards.get(token.id);
125
+
117
126
  if (guard && guard.untilMs > now) {
118
127
  scheduleToken(token, deps, expectedGeneration, guard.untilMs - now, guard.allowFireAtExpiry);
128
+
119
129
  return;
120
130
  }
131
+
121
132
  if (guard) fireGuards.delete(token.id);
122
133
 
123
134
  let usage: UsageResponse;
135
+
124
136
  try {
125
137
  usage = await deps.readUsage(token.id);
126
138
  } catch (error) {
127
- logger.warn(`usage lookup failed for ${token.name}: ${(error as Error).message}`);
139
+ logger.warn(
140
+ `usage lookup failed for ${token.name}: ${error instanceof Error ? error.message : String(error)}`,
141
+ );
128
142
  scheduleFallback(token, deps, expectedGeneration);
143
+
129
144
  return;
130
145
  }
146
+
131
147
  if (generation !== expectedGeneration) return;
132
148
 
133
149
  if (usage.error) {
134
150
  logger.warn(`usage lookup failed for ${token.name}: ${usage.error}`);
135
151
  scheduleFallback(token, deps, expectedGeneration);
152
+
136
153
  return;
137
154
  }
138
155
 
139
156
  const resetsAt = usage.fiveHour?.resetsAt;
157
+
140
158
  if (resetsAt) {
141
159
  const resetMs = Date.parse(resetsAt);
160
+
142
161
  if (!Number.isFinite(resetMs)) {
143
162
  logger.warn(`invalid five-hour reset for ${token.name}: ${resetsAt}`);
144
163
  scheduleFallback(token, deps, expectedGeneration);
164
+
145
165
  return;
146
166
  }
167
+
147
168
  const inspectedAt = deps.now();
169
+
148
170
  if (resetMs > inspectedAt) {
149
171
  fireGuards.delete(token.id);
150
172
  scheduleToken(token, deps, expectedGeneration, resetMs - inspectedAt + RESET_GRACE_MS, true);
173
+
151
174
  return;
152
175
  }
153
176
  }
@@ -155,10 +178,12 @@ async function inspectToken(
155
178
  // 발사 직후 확인에서는 캐시된 null/과거 reset을 봐도 다시 발사하지 않는다.
156
179
  if (!allowFire) {
157
180
  scheduleFallback(token, deps, expectedGeneration);
181
+
158
182
  return;
159
183
  }
160
184
 
161
185
  deferToken(token, deps, expectedGeneration, POST_KEEPALIVE_USAGE_DELAY_MS, false);
186
+
162
187
  try {
163
188
  await deps.dispatch({
164
189
  prompt: "Reply OK.",
@@ -170,12 +195,16 @@ async function inspectToken(
170
195
  });
171
196
  logger.info(`keepalive sent for ${token.name}`);
172
197
  } catch (error) {
173
- logger.warn(`keepalive failed for ${token.name}: ${(error as Error).message}`);
198
+ logger.warn(
199
+ `keepalive failed for ${token.name}: ${error instanceof Error ? error.message : String(error)}`,
200
+ );
174
201
  scheduleFallback(token, deps, expectedGeneration);
202
+
175
203
  return;
176
204
  }
177
205
 
178
206
  const postFireGuard = fireGuards.get(token.id);
207
+
179
208
  if (postFireGuard) {
180
209
  scheduleToken(
181
210
  token,
@@ -189,6 +218,7 @@ async function inspectToken(
189
218
 
190
219
  function clearScheduledTimers(): void {
191
220
  generation++;
221
+
192
222
  for (const tokenId of timers.keys()) clearTokenTimer(tokenId);
193
223
  }
194
224
 
@@ -196,32 +226,45 @@ export async function startTokenWindowKeepalive(
196
226
  deps: TokenWindowKeepaliveDeps = defaultDeps,
197
227
  ): Promise<void> {
198
228
  clearScheduledTimers();
229
+
199
230
  if (!deps.isRunnerEnabled()) {
200
231
  logger.info("token window keepalive runner disabled for this instance");
232
+
201
233
  return;
202
234
  }
235
+
203
236
  if (deps.readSetting(KEEPALIVE_SETTING_KEY) === "false") {
204
237
  logger.info("token window keepalive disabled");
238
+
205
239
  return;
206
240
  }
241
+
207
242
  const expectedGeneration = generation;
208
243
 
209
244
  let tokens: KeepaliveToken[];
245
+
210
246
  try {
211
247
  tokens = await deps.findTokens();
212
248
  } catch (error) {
213
- logger.warn(`token discovery failed: ${(error as Error).message}`);
249
+ logger.warn(
250
+ `token discovery failed: ${error instanceof Error ? error.message : String(error)}`,
251
+ );
252
+
214
253
  return;
215
254
  }
255
+
216
256
  if (generation !== expectedGeneration) return;
217
257
 
218
258
  const targets = tokens.filter(
219
259
  (token) => token.active && token.provider === "anthropic" && token.keepalive_enabled,
220
260
  );
261
+
221
262
  const targetIds = new Set(targets.map((token) => token.id));
263
+
222
264
  for (const tokenId of fireGuards.keys()) {
223
265
  if (!targetIds.has(tokenId)) fireGuards.delete(tokenId);
224
266
  }
267
+
225
268
  logger.info(`token window keepalive scheduling ${targets.length} token(s)`);
226
269
  await Promise.all(targets.map((token) => inspectToken(token, deps, expectedGeneration, true)));
227
270
  }