@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.
- package/bundle/dist/_virtual/_rolldown/runtime.js +1 -10
- package/bundle/dist/application/monit/log-buffer.js +12 -6
- package/bundle/dist/application/monit/monit.frame.js +11 -3
- package/bundle/dist/application/monit/monit.types.js +1 -1
- package/bundle/dist/application/qgrid/conv-routing.js +9 -6
- package/bundle/dist/application/qgrid/expired-token-reminder.js +2 -2
- package/bundle/dist/application/qgrid/oauth.js +31 -4
- package/bundle/dist/application/qgrid/qgrid-image-generation.js +1 -1
- package/bundle/dist/application/qgrid/qgrid-response-format.js +21 -19
- package/bundle/dist/application/qgrid/qgrid-run-lifecycle.js +40 -33
- package/bundle/dist/application/qgrid/qgrid.dispatcher.js +128 -93
- package/bundle/dist/application/qgrid/qgrid.frame.js +930 -49
- package/bundle/dist/application/qgrid/qgrid.types.js +212 -5
- package/bundle/dist/application/qgrid/schema-prompt.js +75 -48
- package/bundle/dist/application/qgrid/slack-user-map.js +1 -1
- package/bundle/dist/application/qgrid/token-death.js +1 -1
- package/bundle/dist/application/qgrid/token-subscriber.js +83 -30
- package/bundle/dist/application/qgrid/token-trigger-setup.js +1 -1
- package/bundle/dist/application/qgrid/token-window-keepalive.js +8 -6
- package/bundle/dist/application/qgrid/tool-emulation-schema.js +13 -13
- package/bundle/dist/application/qgrid/tool-emulation.js +19 -15
- package/bundle/dist/application/request-log/json-schema-type-text.js +29 -12
- package/bundle/dist/application/request-log/request-log.model.js +83 -70
- package/bundle/dist/application/request-log/request-log.types.js +1 -1
- package/bundle/dist/application/request-log/zod-code-format.js +1 -1
- package/bundle/dist/application/request-log-step/request-log-step.model.js +1 -1
- package/bundle/dist/application/request-log-step/request-log-step.types.js +1 -1
- package/bundle/dist/application/setting/setting.constant.js +1 -1
- package/bundle/dist/application/setting/setting.model.js +2 -2
- package/bundle/dist/application/setting/setting.store.js +2 -2
- package/bundle/dist/application/setting/setting.types.js +1 -1
- package/bundle/dist/application/sonamu.generated.js +32 -16
- package/bundle/dist/application/sonamu.generated.sso.js +1 -1
- package/bundle/dist/application/token/token.model.js +26 -9
- package/bundle/dist/application/token/token.types.js +20 -3
- package/bundle/dist/i18n/en.js +15 -1
- package/bundle/dist/i18n/ko.js +15 -1
- package/bundle/dist/i18n/sd.generated.js +1 -1
- package/bundle/dist/migrations/20260807133553_manual__backfill_request_log_cost.js +8 -6
- package/bundle/dist/server-bootstrap.js +4 -2
- package/bundle/dist/server-error-handler.js +1 -1
- package/bundle/dist/sonamu.config.js +37 -8
- package/bundle/dist/startup-migrations.js +17 -7
- package/bundle/dist/testing/global.js +1 -1
- package/bundle/dist/testing/schema-bench.js +0 -0
- package/bundle/dist/testing/test-database-name.js +1 -1
- package/bundle/dist/utils/providers/anthropic/anthropic-constants.js +1 -1
- package/bundle/dist/utils/providers/anthropic/anthropic-dispatcher.js +11 -5
- package/bundle/dist/utils/providers/anthropic/anthropic-quota.js +4 -4
- package/bundle/dist/utils/providers/anthropic/anthropic-thinking.js +1 -1
- package/bundle/dist/utils/providers/anthropic/claude-session.js +20 -19
- package/bundle/dist/utils/providers/anthropic/fence-strip.js +1 -1
- package/bundle/dist/utils/providers/anthropic/stream-json-adapter.js +49 -23
- package/bundle/dist/utils/providers/common/credentials.js +5 -1
- package/bundle/dist/utils/providers/common/effort.js +3 -3
- package/bundle/dist/utils/providers/common/model-cost.js +69 -14
- package/bundle/dist/utils/providers/common/provider-types.js +29 -1
- package/bundle/dist/utils/providers/common/schema-validation.js +78 -40
- package/bundle/dist/utils/providers/common/smooth-weighted-round-robin.js +1 -1
- package/bundle/dist/utils/providers/common/strictifier.js +62 -43
- package/bundle/dist/utils/providers/common/thinking.js +3 -2
- package/bundle/dist/utils/providers/common/ttft.js +1 -1
- package/bundle/dist/utils/providers/grok/grok-backend-protocol.js +427 -0
- package/bundle/dist/utils/providers/grok/grok-constants.js +55 -0
- package/bundle/dist/utils/providers/grok/grok-direct-client.js +196 -0
- package/bundle/dist/utils/providers/grok/grok-dispatcher.js +1313 -0
- package/bundle/dist/utils/providers/grok/grok-headers.js +118 -0
- package/bundle/dist/utils/providers/grok/grok-media-client.js +420 -0
- package/bundle/dist/utils/providers/grok/grok-models.js +114 -0
- package/bundle/dist/utils/providers/grok/grok-oauth.js +455 -0
- package/bundle/dist/utils/providers/grok/grok-quota.js +119 -0
- package/bundle/dist/utils/providers/grok/grok-refresh.js +192 -0
- package/bundle/dist/utils/providers/grok/grok-types.js +186 -0
- package/bundle/dist/utils/providers/grok/grok-voice-client.js +199 -0
- package/bundle/dist/utils/providers/grok/index.js +11 -0
- package/bundle/dist/utils/providers/openai/openai-backend-protocol.js +111 -83
- package/bundle/dist/utils/providers/openai/openai-callback-relay.js +3 -2
- package/bundle/dist/utils/providers/openai/openai-constants.js +1 -1
- package/bundle/dist/utils/providers/openai/openai-direct-client.js +23 -7
- package/bundle/dist/utils/providers/openai/openai-dispatcher.js +143 -106
- package/bundle/dist/utils/providers/openai/openai-image-generation.js +48 -33
- package/bundle/dist/utils/providers/openai/openai-oauth.js +28 -13
- package/bundle/dist/utils/providers/openai/openai-quota.js +38 -27
- package/bundle/dist/utils/providers/openai/openai-refresh.js +17 -9
- package/bundle/dist/utils/providers/openai/openai-sse.js +1 -1
- package/bundle/dist/utils/providers/openai/openai-transport-config.js +1 -1
- package/bundle/dist/utils/quiet-hours.js +1 -1
- package/bundle/dist/utils/slack-notify.js +14 -8
- package/bundle/dist/utils/subset-loaders.js +4 -5
- package/bundle/src/application/monit/log-buffer.ts +23 -9
- package/bundle/src/application/monit/monit.frame.ts +22 -5
- package/bundle/src/application/monit/monit.types.ts +6 -0
- package/bundle/src/application/qgrid/conv-routing.test.ts +76 -4
- package/bundle/src/application/qgrid/conv-routing.ts +35 -9
- package/bundle/src/application/qgrid/expired-token-reminder.ts +9 -1
- package/bundle/src/application/qgrid/oauth.ts +51 -15
- package/bundle/src/application/qgrid/qgrid-image-generation.ts +9 -0
- package/bundle/src/application/qgrid/qgrid-response-format.ts +50 -25
- package/bundle/src/application/qgrid/qgrid-run-lifecycle.test.ts +19 -0
- package/bundle/src/application/qgrid/qgrid-run-lifecycle.ts +84 -40
- package/bundle/src/application/qgrid/qgrid.dispatcher.test.ts +97 -0
- package/bundle/src/application/qgrid/qgrid.dispatcher.ts +274 -126
- package/bundle/src/application/qgrid/qgrid.frame.test.ts +1013 -3
- package/bundle/src/application/qgrid/qgrid.frame.ts +1415 -60
- package/bundle/src/application/qgrid/qgrid.types.ts +335 -0
- package/bundle/src/application/qgrid/schema-prompt.ts +105 -41
- package/bundle/src/application/qgrid/slack-user-map.ts +5 -0
- package/bundle/src/application/qgrid/token-death.ts +4 -0
- package/bundle/src/application/qgrid/token-subscriber.test.ts +21 -19
- package/bundle/src/application/qgrid/token-subscriber.ts +186 -53
- package/bundle/src/application/qgrid/token-trigger-setup.ts +2 -0
- package/bundle/src/application/qgrid/token-window-keepalive.ts +49 -6
- package/bundle/src/application/qgrid/tool-emulation-schema.ts +33 -29
- package/bundle/src/application/qgrid/tool-emulation.ts +52 -27
- package/bundle/src/application/request-log/json-schema-type-text.test.ts +5 -5
- package/bundle/src/application/request-log/json-schema-type-text.ts +81 -26
- package/bundle/src/application/request-log/request-log.model.test.ts +111 -4
- package/bundle/src/application/request-log/request-log.model.ts +277 -146
- package/bundle/src/application/request-log/request-log.types.ts +6 -0
- package/bundle/src/application/request-log/zod-code-format.ts +11 -0
- package/bundle/src/application/request-log-step/request-log-step.model.ts +1 -0
- package/bundle/src/application/request-log-step/request-log-step.types.ts +2 -0
- package/bundle/src/application/setting/setting.constant.ts +9 -0
- package/bundle/src/application/setting/setting.model.ts +12 -2
- package/bundle/src/application/setting/setting.store.ts +5 -1
- package/bundle/src/application/setting/setting.types.ts +8 -0
- package/bundle/src/application/sonamu.generated.http +133 -0
- package/bundle/src/application/sonamu.generated.sso.ts +2 -0
- package/bundle/src/application/sonamu.generated.ts +15 -0
- package/bundle/src/application/token/token.identity.test.ts +20 -0
- package/bundle/src/application/token/token.model.ts +66 -7
- package/bundle/src/application/token/token.types.ts +24 -1
- package/bundle/src/i18n/en.ts +19 -0
- package/bundle/src/i18n/ko.ts +21 -0
- package/bundle/src/i18n/sd.generated.ts +23 -2
- package/bundle/src/migrations/20260807133553_manual__backfill_request_log_cost.ts +16 -8
- package/bundle/src/server-bootstrap.ts +4 -2
- package/bundle/src/server-error-handler.ts +1 -0
- package/bundle/src/sonamu.config.ts +69 -7
- package/bundle/src/startup-migrations.ts +27 -9
- package/bundle/src/testing/global.ts +20 -0
- package/bundle/src/testing/schema-bench.ts +105 -30
- package/bundle/src/testing/test-database-name.ts +2 -0
- package/bundle/src/utils/providers/anthropic/anthropic-constants.ts +8 -0
- package/bundle/src/utils/providers/anthropic/anthropic-dispatcher.ts +41 -6
- package/bundle/src/utils/providers/anthropic/anthropic-quota.ts +7 -3
- package/bundle/src/utils/providers/anthropic/anthropic-thinking.ts +6 -0
- package/bundle/src/utils/providers/anthropic/claude-session.ts +58 -24
- package/bundle/src/utils/providers/anthropic/fence-strip.ts +11 -0
- package/bundle/src/utils/providers/anthropic/stream-json-adapter.ts +110 -45
- package/bundle/src/utils/providers/common/credentials.ts +8 -0
- package/bundle/src/utils/providers/common/effort.ts +9 -2
- package/bundle/src/utils/providers/common/model-cost.test.ts +48 -1
- package/bundle/src/utils/providers/common/model-cost.ts +259 -158
- package/bundle/src/utils/providers/common/provider-dispatcher.ts +98 -2
- package/bundle/src/utils/providers/common/provider-types.ts +46 -3
- package/bundle/src/utils/providers/common/schema-validation.test.ts +5 -1
- package/bundle/src/utils/providers/common/schema-validation.ts +124 -56
- package/bundle/src/utils/providers/common/smooth-weighted-round-robin.ts +4 -0
- package/bundle/src/utils/providers/common/strictifier.ts +185 -59
- package/bundle/src/utils/providers/common/thinking.ts +2 -2
- package/bundle/src/utils/providers/common/ttft.ts +4 -2
- package/bundle/src/utils/providers/grok/__fixtures__/golden-responses-1.0.60.json +164 -0
- package/bundle/src/utils/providers/grok/grok-backend-protocol.test.ts +325 -0
- package/bundle/src/utils/providers/grok/grok-backend-protocol.ts +721 -0
- package/bundle/src/utils/providers/grok/grok-constants.ts +105 -0
- package/bundle/src/utils/providers/grok/grok-direct-client.test.ts +179 -0
- package/bundle/src/utils/providers/grok/grok-direct-client.ts +313 -0
- package/bundle/src/utils/providers/grok/grok-dispatcher.test.ts +1395 -0
- package/bundle/src/utils/providers/grok/grok-dispatcher.ts +2105 -0
- package/bundle/src/utils/providers/grok/grok-headers.test.ts +149 -0
- package/bundle/src/utils/providers/grok/grok-headers.ts +199 -0
- package/bundle/src/utils/providers/grok/grok-media-client.test.ts +446 -0
- package/bundle/src/utils/providers/grok/grok-media-client.ts +703 -0
- package/bundle/src/utils/providers/grok/grok-models.test.ts +96 -0
- package/bundle/src/utils/providers/grok/grok-models.ts +161 -0
- package/bundle/src/utils/providers/grok/grok-oauth.test.ts +406 -0
- package/bundle/src/utils/providers/grok/grok-oauth.ts +771 -0
- package/bundle/src/utils/providers/grok/grok-quota.test.ts +142 -0
- package/bundle/src/utils/providers/grok/grok-quota.ts +198 -0
- package/bundle/src/utils/providers/grok/grok-refresh.test.ts +239 -0
- package/bundle/src/utils/providers/grok/grok-refresh.ts +325 -0
- package/bundle/src/utils/providers/grok/grok-types.test.ts +252 -0
- package/bundle/src/utils/providers/grok/grok-types.ts +374 -0
- package/bundle/src/utils/providers/grok/grok-voice-client.test.ts +321 -0
- package/bundle/src/utils/providers/grok/grok-voice-client.ts +284 -0
- package/bundle/src/utils/providers/grok/index.ts +20 -0
- package/bundle/src/utils/providers/openai/openai-backend-protocol.ts +221 -137
- package/bundle/src/utils/providers/openai/openai-callback-relay.ts +12 -1
- package/bundle/src/utils/providers/openai/openai-constants.ts +2 -0
- package/bundle/src/utils/providers/openai/openai-direct-client.ts +111 -21
- package/bundle/src/utils/providers/openai/openai-dispatcher.ts +322 -169
- package/bundle/src/utils/providers/openai/openai-image-generation.ts +116 -36
- package/bundle/src/utils/providers/openai/openai-oauth.ts +48 -20
- package/bundle/src/utils/providers/openai/openai-quota.ts +77 -46
- package/bundle/src/utils/providers/openai/openai-refresh.ts +42 -18
- package/bundle/src/utils/providers/openai/openai-sse.ts +27 -3
- package/bundle/src/utils/providers/openai/openai-transport-config.ts +2 -0
- package/bundle/src/utils/quiet-hours.ts +12 -1
- package/bundle/src/utils/slack-notify.ts +44 -13
- package/bundle/src/utils/subset-loaders.ts +7 -7
- package/bundle/web-dist/client/assets/clsx-5XM_pHvN.js +1 -0
- package/bundle/web-dist/client/assets/index-Be2qCZmI.css +1 -0
- package/bundle/web-dist/client/assets/index-C96h3feE.js +32 -0
- package/bundle/web-dist/client/assets/logs-DBM6U0QD.js +1 -0
- package/bundle/web-dist/client/assets/monit-BBjXa7vB.js +1 -0
- package/bundle/web-dist/client/assets/routes-Crz_PQzi.js +5 -0
- package/bundle/web-dist/client/assets/{services.generated-NP-ValHd.js → services.generated-B5lc3aIC.js} +5 -5
- package/bundle/web-dist/client/assets/settings-Cdwm3OBB.js +1 -0
- package/bundle/web-dist/client/assets/show-DBUhALT1.js +2 -0
- package/bundle/web-dist/client/assets/tokens-CVQAVJTo.js +1 -0
- package/bundle/web-dist/client/assets/total-cost-MnfKU8IN.js +1 -0
- package/bundle/web-dist/client/assets/use-oauth-login-flow-BfxYI4Oh.js +1 -0
- package/bundle/web-dist/client/assets/use-update-token-mutation-qKBC2sn7.js +1 -0
- package/bundle/web-dist/client/assets/{vendor-tanstack-DJRQAbrY.js → vendor-tanstack-DRPs09ai.js} +1 -1
- package/bundle/web-dist/client/index.html +5 -5
- package/bundle/web-dist/server/assets/clsx-D_N5PN_2.js +16 -0
- package/bundle/web-dist/server/assets/{lazyRouteComponent-CgBdabgd.js → lazyRouteComponent-AKiHfZWp.js} +129 -2
- package/bundle/web-dist/server/assets/{logs-BMyR98rU.js → logs-B7QmGJq3.js} +18 -14
- package/bundle/web-dist/server/assets/{monit-ZPt66W5M.js → monit-DFt7x3bh.js} +13 -12
- package/bundle/web-dist/server/assets/{plus-BZ9I0VgC.js → plus-DaYQRpBt.js} +1 -1
- package/bundle/web-dist/server/assets/{react-dom-Ds75e4Jl.js → react-dom-Cv23mm1K.js} +2 -129
- package/bundle/web-dist/server/assets/{refresh-cw-CztCFis-.js → refresh-cw-BpXNsAy-.js} +1 -1
- package/bundle/web-dist/server/assets/{request-model-DjGldb-h.js → request-model-eHE6YbaF.js} +1 -1
- package/bundle/web-dist/server/assets/{routes-Ct-k7nBA.js → routes-_Y-PJfgO.js} +110 -39
- package/bundle/web-dist/server/assets/{services.generated-BA4AcAf5.js → services.generated-CMJ8AHbO.js} +582 -172
- package/bundle/web-dist/server/assets/{settings-CBUZ47Xx.js → settings-JwJKxn2e.js} +14 -14
- package/bundle/web-dist/server/assets/{show-LSFIzvO2.js → show-BEv6z1v_.js} +90 -60
- package/bundle/web-dist/server/assets/{tokens-DxrBW512.js → tokens-CNT4mI-f.js} +162 -50
- package/bundle/web-dist/server/assets/total-cost-fTE9Xy82.js +18 -0
- package/bundle/web-dist/server/assets/use-oauth-login-flow-jkAkT3X6.js +2345 -0
- package/bundle/web-dist/server/assets/{use-update-token-mutation-pkArIzCP.js → use-update-token-mutation-Sm6kh02m.js} +1 -1
- package/bundle/web-dist/server/entry-server.generated.js +1644 -21655
- package/dist/cli.js +9 -9
- package/package.json +4 -11
- package/postinstall.mjs +12 -0
- package/skills/qgrid/SKILL.md +1 -0
- package/skills/qgrid/references/cli-env-and-server-boot.md +4 -1
- package/skills/qgrid/references/decision-rationale.md +23 -0
- package/skills/qgrid/references/grok-runtime.md +89 -0
- package/skills/qgrid/references/provider-runtime-differences.md +30 -29
- package/skills/qgrid/references/repo-map.md +10 -0
- package/skills/qgrid/references/token-auth-quota-lifecycle.md +56 -3
- package/bundle/dist/node_modules/.pnpm/@jridgewell_sourcemap-codec@1.5.5/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.js +0 -406
- package/bundle/dist/node_modules/.pnpm/@vitest_expect@4.1.10/node_modules/@vitest/expect/dist/index.js +0 -1507
- package/bundle/dist/node_modules/.pnpm/@vitest_pretty-format@4.1.10/node_modules/@vitest/pretty-format/dist/index.js +0 -887
- package/bundle/dist/node_modules/.pnpm/@vitest_runner@4.1.10/node_modules/@vitest/runner/dist/chunk-artifact.js +0 -2756
- package/bundle/dist/node_modules/.pnpm/@vitest_runner@4.1.10/node_modules/@vitest/runner/dist/index.js +0 -8
- package/bundle/dist/node_modules/.pnpm/@vitest_runner@4.1.10/node_modules/@vitest/runner/dist/utils.js +0 -8
- package/bundle/dist/node_modules/.pnpm/@vitest_snapshot@4.1.10/node_modules/@vitest/snapshot/dist/index.js +0 -922
- package/bundle/dist/node_modules/.pnpm/@vitest_spy@4.1.10/node_modules/@vitest/spy/dist/index.js +0 -386
- package/bundle/dist/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/chunk-pathe.M-eThtNZ.js +0 -121
- package/bundle/dist/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/constants.js +0 -51
- package/bundle/dist/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/diff.js +0 -1317
- package/bundle/dist/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/display.js +0 -561
- package/bundle/dist/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/error.js +0 -40
- package/bundle/dist/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/helpers.js +0 -272
- package/bundle/dist/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/offset.js +0 -27
- package/bundle/dist/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/serialize.js +0 -77
- package/bundle/dist/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/source-map.js +0 -381
- package/bundle/dist/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/timers.js +0 -51
- package/bundle/dist/node_modules/.pnpm/chai@6.2.2/node_modules/chai/index.js +0 -2986
- package/bundle/dist/node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/branding.js +0 -10
- package/bundle/dist/node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/index.js +0 -109
- package/bundle/dist/node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/messages.js +0 -10
- package/bundle/dist/node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/overloads.js +0 -10
- package/bundle/dist/node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/utils.js +0 -10
- package/bundle/dist/node_modules/.pnpm/magic-string@0.30.21/node_modules/magic-string/dist/magic-string.es.js +0 -1126
- package/bundle/dist/node_modules/.pnpm/pathe@2.0.3/node_modules/pathe/dist/index.js +0 -21
- package/bundle/dist/node_modules/.pnpm/pathe@2.0.3/node_modules/pathe/dist/shared/pathe.M-eThtNZ.js +0 -432
- package/bundle/dist/node_modules/.pnpm/tinybench@2.9.0/node_modules/tinybench/dist/index.js +0 -517
- package/bundle/dist/node_modules/.pnpm/tinyrainbow@3.1.0/node_modules/tinyrainbow/dist/index.js +0 -101
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- package/bundle/dist/testing/setup-mocks.js +0 -16
- package/bundle/src/testing/setup-mocks.ts +0 -44
- package/bundle/web-dist/client/assets/index-Bw2s88Ot.css +0 -1
- package/bundle/web-dist/client/assets/index-DZwzUfI-.js +0 -106
- package/bundle/web-dist/client/assets/logs-Cog79DU5.js +0 -1
- package/bundle/web-dist/client/assets/monit-2auznBys.js +0 -1
- package/bundle/web-dist/client/assets/routes-B0lTeLK-.js +0 -5
- package/bundle/web-dist/client/assets/settings-D8ZxZGSJ.js +0 -1
- package/bundle/web-dist/client/assets/show-CFSERlnM.js +0 -2
- package/bundle/web-dist/client/assets/tokens-CZXUyI7O.js +0 -1
- package/bundle/web-dist/client/assets/use-oauth-login-flow-hboOs3wG.js +0 -1
- package/bundle/web-dist/client/assets/use-update-token-mutation-BQHS0byG.js +0 -1
- package/bundle/web-dist/server/assets/use-oauth-login-flow-Gn8flMK5.js +0 -178
- package/dist/cli.d.ts +0 -10
- /package/bundle/web-dist/client/assets/{cost-DH2jZbrw.js → cost-DB3HbU7T.js} +0 -0
- /package/bundle/web-dist/client/assets/{plus-B62CuqT8.js → plus-BsQdY7PS.js} +0 -0
- /package/bundle/web-dist/client/assets/{preload-helper-ca-nBW7U.js → preload-helper-DWTEM3RW.js} +0 -0
- /package/bundle/web-dist/client/assets/{refresh-cw-NNcU0Cmq.js → refresh-cw-D9IUmT23.js} +0 -0
- /package/bundle/web-dist/client/assets/{request-model-CojtinqK.js → request-model-Bpp_rkK8.js} +0 -0
- /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 {
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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: ${
|
|
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: ${
|
|
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: ${
|
|
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)
|
|
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) =>
|
|
178
|
-
|
|
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) =>
|
|
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
|
|
245
|
+
const creds = openAICredentialsOf(row);
|
|
200
246
|
const openaiDispatcher = this.dispatcher.openaiDispatcher;
|
|
201
|
-
|
|
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
|
-
|
|
205
|
-
|
|
206
|
-
|
|
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) =>
|
|
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
|
|
229
|
-
|
|
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
|
-
|
|
263
|
-
|
|
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
|
-
|
|
270
|
-
|
|
271
|
-
.
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
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
|
-
|
|
280
|
-
|
|
281
|
-
.
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
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) =>
|
|
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<
|
|
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) =>
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
}
|