@cartanova/qgrid-cli 2.10.10 → 2.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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-BA7uQ-6c.js +31 -0
- package/bundle/web-dist/client/assets/index-Be2qCZmI.css +1 -0
- package/bundle/web-dist/client/assets/logs-C47OJGEI.js +1 -0
- package/bundle/web-dist/client/assets/monit-BS9no0dU.js +4 -0
- package/bundle/web-dist/client/assets/plus-sdACTfH0.js +1 -0
- package/bundle/web-dist/client/assets/refresh-cw-CY8o_1cT.js +1 -0
- package/bundle/web-dist/client/assets/request-model-BRtFuZvj.js +1 -0
- package/bundle/web-dist/client/assets/routes-kqgkUxE8.js +5 -0
- package/bundle/web-dist/client/assets/{services.generated-NP-ValHd.js → services.generated-CefiDXEv.js} +5 -5
- package/bundle/web-dist/client/assets/settings-DNDvFGSw.js +1 -0
- package/bundle/web-dist/client/assets/show-u8Np69q3.js +2 -0
- package/bundle/web-dist/client/assets/tokens-QvP410NW.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-Bzdh28Ws.js +1 -0
- package/bundle/web-dist/client/assets/use-update-token-mutation-CfJKaxmR.js +1 -0
- package/bundle/web-dist/client/assets/vendor-react-CUFFrVN9.js +244 -0
- package/bundle/web-dist/client/assets/vendor-tanstack-Bf_jag9G.js +1 -0
- package/bundle/web-dist/client/index.html +6 -6
- package/bundle/web-dist/server/assets/clsx-D_N5PN_2.js +16 -0
- package/bundle/web-dist/server/assets/{lazyRouteComponent-CgBdabgd.js → lazyRouteComponent-D8iTIHyn.js} +129 -2
- package/bundle/web-dist/server/assets/logs-B3OTd9hy.js +825 -0
- package/bundle/web-dist/server/assets/{monit-ZPt66W5M.js → monit-B2hqorPe.js} +385 -126
- package/bundle/web-dist/server/assets/plus-DwG7h9kq.js +28 -0
- package/bundle/web-dist/server/assets/{react-dom-Ds75e4Jl.js → react-dom-BIYQzBqG.js} +2 -129
- package/bundle/web-dist/server/assets/refresh-cw-mMBkwOq0.js +44 -0
- package/bundle/web-dist/server/assets/{request-model-DjGldb-h.js → request-model-I6OdODSH.js} +15 -6
- package/bundle/web-dist/server/assets/{routes-Ct-k7nBA.js → routes-B95YlUv2.js} +760 -213
- package/bundle/web-dist/server/assets/{services.generated-BA4AcAf5.js → services.generated-DQATJ6qf.js} +12820 -12194
- package/bundle/web-dist/server/assets/settings-C3gbGk0m.js +830 -0
- package/bundle/web-dist/server/assets/{show-LSFIzvO2.js → show-Bxaj3rhH.js} +1350 -418
- package/bundle/web-dist/server/assets/tokens-CZjH3cB7.js +1038 -0
- package/bundle/web-dist/server/assets/total-cost-fTE9Xy82.js +18 -0
- package/bundle/web-dist/server/assets/use-oauth-login-flow-CKDHgc3U.js +2379 -0
- package/bundle/web-dist/server/assets/{use-update-token-mutation-pkArIzCP.js → use-update-token-mutation-BEbgOzaa.js} +1 -1
- package/bundle/web-dist/server/entry-server.generated.js +4295 -22810
- 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/plus-B62CuqT8.js +0 -1
- package/bundle/web-dist/client/assets/refresh-cw-NNcU0Cmq.js +0 -1
- package/bundle/web-dist/client/assets/request-model-CojtinqK.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/client/assets/vendor-react-DH4kUfK2.js +0 -9
- package/bundle/web-dist/client/assets/vendor-tanstack-DJRQAbrY.js +0 -1
- package/bundle/web-dist/server/assets/logs-BMyR98rU.js +0 -509
- package/bundle/web-dist/server/assets/plus-BZ9I0VgC.js +0 -19
- package/bundle/web-dist/server/assets/refresh-cw-CztCFis-.js +0 -23
- package/bundle/web-dist/server/assets/settings-CBUZ47Xx.js +0 -461
- package/bundle/web-dist/server/assets/tokens-DxrBW512.js +0 -448
- 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/{preload-helper-ca-nBW7U.js → preload-helper-DWTEM3RW.js} +0 -0
- /package/bundle/web-dist/server/assets/{cost-eEHeTRT5.js → cost-BC9yzyA9.js} +0 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// Grok (xAI) fingerprint literals. Bumping GROK_BUILD_VERSION means: edit the constant,
|
|
2
|
+
// re-capture __fixtures__/golden-responses-<ver>.json from the installed binary, and record
|
|
3
|
+
// its build id in GROK_BUILD_COMMIT.
|
|
4
|
+
|
|
5
|
+
// grok-build 1.0.45 xai-grok-env/src/lib.rs:21
|
|
6
|
+
export const GROK_CHAT_PROXY_URL = "https://cli-chat-proxy.grok.com/v1";
|
|
7
|
+
|
|
8
|
+
// grok-build 1.0.45 xai-grok-config/src/endpoints.rs:4
|
|
9
|
+
export const GROK_PUBLIC_API_URL = "https://api.x.ai/v1";
|
|
10
|
+
|
|
11
|
+
// grok-build 1.0.45 xai-grok-login/src/config.rs:124
|
|
12
|
+
export const GROK_AUTH_ISSUER = "https://auth.x.ai";
|
|
13
|
+
|
|
14
|
+
// grok-build 1.0.45 xai-grok-login/src/config.rs:380 (plaintext in manager_tests.rs:164)
|
|
15
|
+
export const GROK_OAUTH_CLIENT_ID = "b1a00492-073a-47ea-816f-4c329264a828";
|
|
16
|
+
|
|
17
|
+
// grok-build 1.0.45 xai-grok-login/src/config.rs:16-28 (order frozen by config.rs:624)
|
|
18
|
+
export const GROK_OAUTH_SCOPES = Object.freeze([
|
|
19
|
+
"openid",
|
|
20
|
+
"profile",
|
|
21
|
+
"email",
|
|
22
|
+
"offline_access",
|
|
23
|
+
"grok-cli:access",
|
|
24
|
+
"api:access",
|
|
25
|
+
"conversations:read",
|
|
26
|
+
"conversations:write",
|
|
27
|
+
"workspaces:read",
|
|
28
|
+
"workspaces:write",
|
|
29
|
+
] as const);
|
|
30
|
+
|
|
31
|
+
// grok-build 1.0.45 xai-grok-login/src/config.rs:152
|
|
32
|
+
export const GROK_OAUTH_REFERRER = "grok-build";
|
|
33
|
+
|
|
34
|
+
// grok-build 1.0.45 xai-grok-login/src/device_code.rs:117
|
|
35
|
+
export const GROK_DEVICE_CODE_URL = "https://auth.x.ai/oauth2/device/code";
|
|
36
|
+
|
|
37
|
+
// grok-build 1.0.45 xai-grok-login/src/device_code.rs:187
|
|
38
|
+
export const GROK_DEVICE_TOKEN_URL = "https://auth.x.ai/oauth2/token";
|
|
39
|
+
|
|
40
|
+
// U0 capture 2026-10-16 (installed grok-build 1.0.60, user decision)
|
|
41
|
+
export const GROK_BUILD_VERSION = "1.0.60" as const;
|
|
42
|
+
|
|
43
|
+
// U0 capture 2026-10-16 (Homebrew cask build id of 1.0.60)
|
|
44
|
+
export const GROK_BUILD_COMMIT = "01a8e1c591c0";
|
|
45
|
+
|
|
46
|
+
// U0 capture 2026-10-16 (GET /settings min_client_version; HTTP 426 below it)
|
|
47
|
+
export const GROK_MIN_CLIENT_VERSION_OBSERVED = "1.0.13";
|
|
48
|
+
|
|
49
|
+
// grok-build 1.0.45 xai-grok-http/src/lib.rs:246-248
|
|
50
|
+
export const GROK_CLIENT_IDENTIFIER = "grok-shell";
|
|
51
|
+
|
|
52
|
+
// U0 capture 2026-10-16 (grok -p sends headless; xai-grok-http/src/lib.rs:250-260)
|
|
53
|
+
export const GROK_CLIENT_MODE = "headless";
|
|
54
|
+
|
|
55
|
+
// U0 capture 2026-10-16 (GET /models default and /settings default_model)
|
|
56
|
+
export const GROK_DEFAULT_MODEL = "grok-4.7";
|
|
57
|
+
|
|
58
|
+
// grok-build 1.0.45 image_gen/mod.rs:33
|
|
59
|
+
export const GROK_IMAGE_DEFAULT_MODEL = "grok-imagine-image-quality";
|
|
60
|
+
|
|
61
|
+
// grok-build 1.0.45 video_gen/mod.rs:36-40
|
|
62
|
+
export const GROK_VIDEO_DEFAULT_MODEL = "grok-imagine-video-1.5";
|
|
63
|
+
|
|
64
|
+
// U0 capture 2026-10-16 (packet contract; the STT probe did not name a model)
|
|
65
|
+
export const GROK_STT_DEFAULT_MODEL = "grok-voice-transcribe-2.0";
|
|
66
|
+
|
|
67
|
+
// U0 capture 2026-10-16 (api.x.ai /language-models prices vs public per-token pricing)
|
|
68
|
+
export const GROK_USD_PER_TICK = 1e-10;
|
|
69
|
+
|
|
70
|
+
// U0 capture 2026-10-16 (GET /models reasoning_efforts, identical on all four models)
|
|
71
|
+
export const GROK_REASONING_EFFORTS = Object.freeze(["xhigh", "high", "medium", "low"] as const);
|
|
72
|
+
|
|
73
|
+
export type GrokReasoningEffort = (typeof GROK_REASONING_EFFORTS)[number];
|
|
74
|
+
|
|
75
|
+
export interface GrokSeedModel {
|
|
76
|
+
readonly id: string;
|
|
77
|
+
readonly contextWindow: number;
|
|
78
|
+
readonly reasoningEfforts: readonly GrokReasoningEffort[];
|
|
79
|
+
readonly defaultEffort: GrokReasoningEffort;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// U0 capture 2026-10-16 (GET /models: four ids, context_window 256000, default effort high)
|
|
83
|
+
export const GROK_SEED_MODELS: readonly GrokSeedModel[] = Object.freeze(
|
|
84
|
+
["grok-4.7", "grok-4.7-build-fast", "grok-4.6", "grok-4.5"].map(
|
|
85
|
+
(id): GrokSeedModel =>
|
|
86
|
+
Object.freeze({
|
|
87
|
+
id,
|
|
88
|
+
contextWindow: 256_000,
|
|
89
|
+
reasoningEfforts: GROK_REASONING_EFFORTS,
|
|
90
|
+
defaultEffort: "high",
|
|
91
|
+
}),
|
|
92
|
+
),
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
// U0 capture 2026-10-16 (x-compaction-at request header)
|
|
96
|
+
export const GROK_COMPACTION_AT = 204_800;
|
|
97
|
+
|
|
98
|
+
// U0 capture 2026-10-16 (x-compactions-remaining request header)
|
|
99
|
+
export const GROK_COMPACTIONS_REMAINING_DEFAULT = 1;
|
|
100
|
+
|
|
101
|
+
// U0 capture 2026-10-16 (x-grok-doom-loop-check request header)
|
|
102
|
+
export const GROK_DOOM_LOOP_CHECK = 1024;
|
|
103
|
+
|
|
104
|
+
// U0 capture 2026-10-16 (x-grok-exact-repetition-check request header)
|
|
105
|
+
export const GROK_EXACT_REPETITION_CHECK = 64;
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { zstdDecompressSync } from "node:zlib";
|
|
2
|
+
|
|
3
|
+
import { describe, expect, it } from "vitest";
|
|
4
|
+
|
|
5
|
+
import golden from "./__fixtures__/golden-responses-1.0.60.json";
|
|
6
|
+
import { buildGrokResponsesRequest } from "./grok-backend-protocol";
|
|
7
|
+
import {
|
|
8
|
+
GrokChatClient,
|
|
9
|
+
GrokHttpError,
|
|
10
|
+
GrokIdleTimeoutError,
|
|
11
|
+
grokProxyGet,
|
|
12
|
+
} from "./grok-direct-client";
|
|
13
|
+
|
|
14
|
+
const body = buildGrokResponsesRequest({
|
|
15
|
+
model: "grok-4.7",
|
|
16
|
+
history: [{ type: "message", role: "user", content: "ping" }],
|
|
17
|
+
promptCacheKey: "11111111-1111-4111-8111-111111111111",
|
|
18
|
+
effort: "high",
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const headers = { "content-type": "application/json", authorization: "Bearer t" };
|
|
22
|
+
|
|
23
|
+
function sse(events: Array<Record<string, unknown>>): string {
|
|
24
|
+
return events.map((e) => `data: ${JSON.stringify(e)}\n\n`).join("");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const completedStream = sse(
|
|
28
|
+
golden.response.eventTypes.map((type) =>
|
|
29
|
+
type === "response.output_text.delta"
|
|
30
|
+
? { type, delta: "pong", item_id: "m" }
|
|
31
|
+
: type === "response.completed"
|
|
32
|
+
? { type, response: { id: "r", usage: golden.response.terminalUsage } }
|
|
33
|
+
: { type, response: { id: "r" } },
|
|
34
|
+
),
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
function openStream(): { stream: ReadableStream<Uint8Array>; push(text: string): void } {
|
|
38
|
+
let controller!: ReadableStreamDefaultController<Uint8Array>;
|
|
39
|
+
const stream = new ReadableStream<Uint8Array>({
|
|
40
|
+
start(c) {
|
|
41
|
+
controller = c;
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
return { stream, push: (text) => controller.enqueue(new TextEncoder().encode(text)) };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
describe("GrokChatClient", () => {
|
|
48
|
+
it("POSTs a zstd body and parses the fixture stream without [DONE]", async () => {
|
|
49
|
+
let sent: { url: string; init: RequestInit | undefined } | undefined;
|
|
50
|
+
const client = new GrokChatClient({
|
|
51
|
+
fetch: async (input, init) => {
|
|
52
|
+
sent = { url: String(input), init };
|
|
53
|
+
return new Response(completedStream, { status: 200, headers: golden.response.headers });
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
const result = await client.stream({ body, headers });
|
|
57
|
+
expect(sent?.url).toBe("https://cli-chat-proxy.grok.com/v1/responses");
|
|
58
|
+
expect(new Headers(sent?.init?.headers).get("content-encoding")).toBe("zstd");
|
|
59
|
+
const raw = sent?.init?.body;
|
|
60
|
+
expect(raw).toBeInstanceOf(Uint8Array);
|
|
61
|
+
expect(JSON.parse(zstdDecompressSync(raw as Uint8Array).toString("utf8"))).toEqual(body);
|
|
62
|
+
expect(result.headers).toMatchObject({
|
|
63
|
+
contextWindow: 256000,
|
|
64
|
+
modelsEtag: '"4849638883941606653"',
|
|
65
|
+
rateLimitLimitTokens: 53000000,
|
|
66
|
+
rateLimitRemainingRequests: 8300,
|
|
67
|
+
});
|
|
68
|
+
const events = [];
|
|
69
|
+
for await (const event of result.events) events.push(event);
|
|
70
|
+
expect(events.map((e) => e.type)).toEqual(["created", "text-delta", "completed"]);
|
|
71
|
+
const last = events.at(-1);
|
|
72
|
+
expect(last?.type === "completed" && last.usage?.costInUsdTicks).toBe(465240000);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it("can send a plain JSON body", async () => {
|
|
76
|
+
let sent: RequestInit | undefined;
|
|
77
|
+
const client = new GrokChatClient({
|
|
78
|
+
fetch: async (_input, init) => {
|
|
79
|
+
sent = init;
|
|
80
|
+
return new Response(completedStream, { status: 200 });
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
await client.stream({ body, headers: { ...headers, "content-encoding": "zstd" }, encoding: "plain" });
|
|
84
|
+
expect(new Headers(sent?.headers).has("content-encoding")).toBe(false);
|
|
85
|
+
expect(JSON.parse(new TextDecoder().decode(sent?.body as Uint8Array))).toEqual(body);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("rejects non-2xx with the classified error and body text", async () => {
|
|
89
|
+
const client = new GrokChatClient({
|
|
90
|
+
fetch: async () =>
|
|
91
|
+
new Response('{"error":"Your Grok CLI version (0.0.1) is outdated. Please update to version 1.0.13 or later"}', {
|
|
92
|
+
status: 426,
|
|
93
|
+
}),
|
|
94
|
+
});
|
|
95
|
+
const error = await client.stream({ body, headers }).catch((e: unknown) => e);
|
|
96
|
+
expect(error).toBeInstanceOf(GrokHttpError);
|
|
97
|
+
const http = error as GrokHttpError;
|
|
98
|
+
expect(http.classification).toMatchObject({ kind: "version_gate", minClientVersion: "1.0.13" });
|
|
99
|
+
expect(http.bodyText).toContain("outdated");
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it("classifies a connect failure as transient status 0", async () => {
|
|
103
|
+
const client = new GrokChatClient({
|
|
104
|
+
fetch: async () => {
|
|
105
|
+
throw new TypeError("fetch failed");
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
const error = await client.stream({ body, headers }).catch((e: unknown) => e);
|
|
109
|
+
expect(error).toBeInstanceOf(GrokHttpError);
|
|
110
|
+
expect((error as GrokHttpError).classification).toMatchObject({ kind: "transient", status: 0 });
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("fails with GrokIdleTimeoutError and cancels the request when the stream stalls", async () => {
|
|
114
|
+
const { stream, push } = openStream();
|
|
115
|
+
let requestSignal: AbortSignal | undefined;
|
|
116
|
+
const client = new GrokChatClient({
|
|
117
|
+
fetch: async (_input, init) => {
|
|
118
|
+
requestSignal = init?.signal ?? undefined;
|
|
119
|
+
return new Response(stream, { status: 200 });
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
const result = await client.stream({ body, headers, idleTimeoutMs: 20 });
|
|
123
|
+
push(sse([{ type: "response.created", response: { id: "r" } }]));
|
|
124
|
+
const seen: string[] = [];
|
|
125
|
+
const error = await (async () => {
|
|
126
|
+
for await (const event of result.events) seen.push(event.type);
|
|
127
|
+
})().catch((e: unknown) => e);
|
|
128
|
+
expect(seen).toEqual(["created"]);
|
|
129
|
+
expect(error).toBeInstanceOf(GrokIdleTimeoutError);
|
|
130
|
+
expect(requestSignal?.aborted).toBe(true);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it("honors caller abort mid-stream", async () => {
|
|
134
|
+
const { stream, push } = openStream();
|
|
135
|
+
const controller = new AbortController();
|
|
136
|
+
const client = new GrokChatClient({ fetch: async () => new Response(stream, { status: 200 }) });
|
|
137
|
+
const result = await client.stream({ body, headers, signal: controller.signal });
|
|
138
|
+
push(sse([{ type: "response.created", response: { id: "r" } }]));
|
|
139
|
+
const pending = (async () => {
|
|
140
|
+
for await (const event of result.events) {
|
|
141
|
+
if (event.type === "created") controller.abort(new Error("caller gone"));
|
|
142
|
+
}
|
|
143
|
+
})();
|
|
144
|
+
await expect(pending).rejects.toThrow("caller gone");
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it("rejects before fetch when already aborted", async () => {
|
|
148
|
+
let called = false;
|
|
149
|
+
const client = new GrokChatClient({
|
|
150
|
+
fetch: async () => {
|
|
151
|
+
called = true;
|
|
152
|
+
return new Response("");
|
|
153
|
+
},
|
|
154
|
+
});
|
|
155
|
+
await expect(
|
|
156
|
+
client.stream({ body, headers, signal: AbortSignal.abort(new Error("stop")) }),
|
|
157
|
+
).rejects.toThrow("stop");
|
|
158
|
+
expect(called).toBe(false);
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
describe("grokProxyGet", () => {
|
|
163
|
+
it("returns JSON and response header info", async () => {
|
|
164
|
+
const result = await grokProxyGet("/models", {
|
|
165
|
+
headers: {},
|
|
166
|
+
fetch: async (input) => {
|
|
167
|
+
expect(String(input)).toBe("https://cli-chat-proxy.grok.com/v1/models");
|
|
168
|
+
return new Response('{"data":[]}', { headers: { "x-models-etag": '"1"' } });
|
|
169
|
+
},
|
|
170
|
+
});
|
|
171
|
+
expect(result).toMatchObject({ status: 200, json: { data: [] }, headers: { modelsEtag: '"1"' } });
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it("throws GrokHttpError on non-2xx", async () => {
|
|
175
|
+
await expect(
|
|
176
|
+
grokProxyGet("/billing", { headers: {}, fetch: async () => new Response("no", { status: 401 }) }),
|
|
177
|
+
).rejects.toMatchObject({ classification: { kind: "auth", status: 401 } });
|
|
178
|
+
});
|
|
179
|
+
});
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
import {
|
|
2
|
+
classifyGrokHttpError,
|
|
3
|
+
encodeGrokRequestBody,
|
|
4
|
+
normalizeGrokSSE,
|
|
5
|
+
readGrokResponseHeaders,
|
|
6
|
+
type GrokHttpErrorClassification,
|
|
7
|
+
type GrokNormalizedEvent,
|
|
8
|
+
type GrokResponseHeaderInfo,
|
|
9
|
+
type GrokResponsesRequest,
|
|
10
|
+
} from "./grok-backend-protocol";
|
|
11
|
+
import { GROK_CHAT_PROXY_URL } from "./grok-constants";
|
|
12
|
+
|
|
13
|
+
// HTTPS/SSE transport for the chat proxy. Headers are built by the caller (grok-headers.ts) so the
|
|
14
|
+
// fingerprint stays in one place; this module only moves bytes, classifies HTTP failures, and
|
|
15
|
+
// enforces abort and idle limits.
|
|
16
|
+
|
|
17
|
+
export const GROK_DEFAULT_IDLE_TIMEOUT_MS = 300_000;
|
|
18
|
+
export const GROK_PROXY_GET_TIMEOUT_MS = 10_000;
|
|
19
|
+
const MAX_ERROR_BODY_LENGTH = 2_000;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* A non-2xx response or a transport failure before any response arrived (`status = 0`).
|
|
23
|
+
* Media/voice ports should throw this class too, so the dispatcher can apply §5.4 handling.
|
|
24
|
+
*/
|
|
25
|
+
export class GrokHttpError extends Error {
|
|
26
|
+
readonly classification: GrokHttpErrorClassification;
|
|
27
|
+
readonly bodyText: string;
|
|
28
|
+
readonly responseHeaders: GrokResponseHeaderInfo;
|
|
29
|
+
|
|
30
|
+
constructor(
|
|
31
|
+
classification: GrokHttpErrorClassification,
|
|
32
|
+
bodyText: string,
|
|
33
|
+
responseHeaders: GrokResponseHeaderInfo = {},
|
|
34
|
+
options?: ErrorOptions,
|
|
35
|
+
) {
|
|
36
|
+
const detail = classification.message ? `: ${classification.message}` : "";
|
|
37
|
+
super(
|
|
38
|
+
classification.status === 0
|
|
39
|
+
? `Grok request failed before a response${detail}`
|
|
40
|
+
: `Grok HTTP ${classification.status} (${classification.kind})${detail}`,
|
|
41
|
+
options,
|
|
42
|
+
);
|
|
43
|
+
this.name = "GrokHttpError";
|
|
44
|
+
this.classification = classification;
|
|
45
|
+
this.bodyText = bodyText.slice(0, MAX_ERROR_BODY_LENGTH);
|
|
46
|
+
this.responseHeaders = responseHeaders;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
get status(): number {
|
|
50
|
+
return this.classification.status;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** The stream produced no bytes for `idleTimeoutMs`. Upstream may already be executing. */
|
|
55
|
+
export class GrokIdleTimeoutError extends Error {
|
|
56
|
+
readonly idleTimeoutMs: number;
|
|
57
|
+
|
|
58
|
+
constructor(idleTimeoutMs: number) {
|
|
59
|
+
super(`Grok stream idle for ${idleTimeoutMs} ms`);
|
|
60
|
+
this.name = "GrokIdleTimeoutError";
|
|
61
|
+
this.idleTimeoutMs = idleTimeoutMs;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function abortReason(signal: AbortSignal): Error {
|
|
66
|
+
const reason: unknown = signal.reason;
|
|
67
|
+
|
|
68
|
+
return reason instanceof Error ? reason : new DOMException("Aborted", "AbortError");
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function connectError(cause: unknown): GrokHttpError {
|
|
72
|
+
const message = cause instanceof Error ? cause.message : String(cause);
|
|
73
|
+
|
|
74
|
+
return new GrokHttpError({ ...classifyGrokHttpError(0, "", {}), message }, "", {}, { cause });
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async function readErrorBody(response: Response): Promise<string> {
|
|
78
|
+
try {
|
|
79
|
+
return (await response.text()).slice(0, MAX_ERROR_BODY_LENGTH);
|
|
80
|
+
} catch {
|
|
81
|
+
return "";
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export async function grokHttpErrorFromResponse(response: Response): Promise<GrokHttpError> {
|
|
86
|
+
const bodyText = await readErrorBody(response);
|
|
87
|
+
|
|
88
|
+
return new GrokHttpError(
|
|
89
|
+
classifyGrokHttpError(response.status, bodyText, response.headers),
|
|
90
|
+
bodyText,
|
|
91
|
+
readGrokResponseHeaders(response.headers),
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Yields from `source` but fails with GrokIdleTimeoutError when no item arrives within
|
|
97
|
+
* `idleTimeoutMs`, and with the abort reason when `signal` aborts. `onStop` runs exactly once
|
|
98
|
+
* when iteration ends for any reason, so the caller can cancel the underlying request.
|
|
99
|
+
*/
|
|
100
|
+
async function* guardStream<T>(
|
|
101
|
+
source: AsyncIterable<T>,
|
|
102
|
+
idleTimeoutMs: number,
|
|
103
|
+
signal: AbortSignal | undefined,
|
|
104
|
+
onStop: (reason: Error | undefined) => void,
|
|
105
|
+
): AsyncGenerator<T> {
|
|
106
|
+
const iterator = source[Symbol.asyncIterator]();
|
|
107
|
+
let settledNormally = false;
|
|
108
|
+
let pendingNext = false;
|
|
109
|
+
let stopped = false;
|
|
110
|
+
|
|
111
|
+
const stop = (reason: Error | undefined) => {
|
|
112
|
+
if (stopped) return;
|
|
113
|
+
stopped = true;
|
|
114
|
+
onStop(reason);
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
try {
|
|
118
|
+
while (true) {
|
|
119
|
+
if (signal?.aborted) throw abortReason(signal);
|
|
120
|
+
let timer: NodeJS.Timeout | undefined;
|
|
121
|
+
let removeAbort: (() => void) | undefined;
|
|
122
|
+
|
|
123
|
+
const interrupt = new Promise<never>((_, reject) => {
|
|
124
|
+
timer = setTimeout(() => reject(new GrokIdleTimeoutError(idleTimeoutMs)), idleTimeoutMs);
|
|
125
|
+
|
|
126
|
+
if (signal) {
|
|
127
|
+
const onAbort = () => reject(abortReason(signal));
|
|
128
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
129
|
+
removeAbort = () => signal.removeEventListener("abort", onAbort);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
pendingNext = true;
|
|
134
|
+
|
|
135
|
+
const next = iterator.next().finally(() => {
|
|
136
|
+
pendingNext = false;
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
let result: IteratorResult<T>;
|
|
140
|
+
|
|
141
|
+
try {
|
|
142
|
+
result = await Promise.race([next, interrupt]);
|
|
143
|
+
} finally {
|
|
144
|
+
clearTimeout(timer);
|
|
145
|
+
removeAbort?.();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (result.done) {
|
|
149
|
+
settledNormally = true;
|
|
150
|
+
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
yield result.value;
|
|
155
|
+
}
|
|
156
|
+
} catch (error) {
|
|
157
|
+
stop(error instanceof Error ? error : new Error(String(error)));
|
|
158
|
+
throw error;
|
|
159
|
+
} finally {
|
|
160
|
+
if (settledNormally) stop(undefined);
|
|
161
|
+
else stop(new DOMException("Grok stream consumer stopped", "AbortError"));
|
|
162
|
+
|
|
163
|
+
// A pending next() would block return(); the cancelled request settles it instead.
|
|
164
|
+
if (!pendingNext) await iterator.return?.(undefined).catch(() => undefined);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export interface GrokChatStreamRequest {
|
|
169
|
+
body: GrokResponsesRequest;
|
|
170
|
+
/** Complete header map from buildGrokChatHeaders (bearer included). */
|
|
171
|
+
headers: Record<string, string>;
|
|
172
|
+
signal?: AbortSignal;
|
|
173
|
+
idleTimeoutMs?: number;
|
|
174
|
+
/** Defaults to zstd, as the pinned grok-build sends. */
|
|
175
|
+
encoding?: "zstd" | "plain";
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export interface GrokChatStream {
|
|
179
|
+
status: number;
|
|
180
|
+
headers: GrokResponseHeaderInfo;
|
|
181
|
+
events: AsyncGenerator<GrokNormalizedEvent>;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export interface GrokChatClientOptions {
|
|
185
|
+
fetch?: typeof fetch;
|
|
186
|
+
baseUrl?: string;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export class GrokChatClient {
|
|
190
|
+
readonly fetchImpl: typeof fetch;
|
|
191
|
+
readonly baseUrl: string;
|
|
192
|
+
|
|
193
|
+
constructor(options: GrokChatClientOptions = {}) {
|
|
194
|
+
this.fetchImpl = options.fetch ?? fetch;
|
|
195
|
+
this.baseUrl = options.baseUrl ?? GROK_CHAT_PROXY_URL;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* POST {proxy}/responses. Resolves once response headers arrive; rejects with GrokHttpError on
|
|
200
|
+
* a non-2xx status or a transport failure, or with the abort reason when `signal` aborts.
|
|
201
|
+
* The stream ends at the first terminal event; `[DONE]` is not required.
|
|
202
|
+
*/
|
|
203
|
+
async stream(request: GrokChatStreamRequest): Promise<GrokChatStream> {
|
|
204
|
+
request.signal?.throwIfAborted();
|
|
205
|
+
|
|
206
|
+
const encoded =
|
|
207
|
+
request.encoding === "plain"
|
|
208
|
+
? {
|
|
209
|
+
bytes: new TextEncoder().encode(JSON.stringify(request.body)),
|
|
210
|
+
contentEncoding: undefined,
|
|
211
|
+
}
|
|
212
|
+
: await encodeGrokRequestBody(request.body);
|
|
213
|
+
|
|
214
|
+
const headers = { ...request.headers };
|
|
215
|
+
|
|
216
|
+
if (encoded.contentEncoding) headers["content-encoding"] = encoded.contentEncoding;
|
|
217
|
+
else delete headers["content-encoding"];
|
|
218
|
+
|
|
219
|
+
const controller = new AbortController();
|
|
220
|
+
|
|
221
|
+
const signal = request.signal
|
|
222
|
+
? AbortSignal.any([request.signal, controller.signal])
|
|
223
|
+
: controller.signal;
|
|
224
|
+
|
|
225
|
+
let response: Response;
|
|
226
|
+
|
|
227
|
+
try {
|
|
228
|
+
response = await this.fetchImpl(`${this.baseUrl}/responses`, {
|
|
229
|
+
method: "POST",
|
|
230
|
+
headers,
|
|
231
|
+
body: new Uint8Array(encoded.bytes),
|
|
232
|
+
signal,
|
|
233
|
+
});
|
|
234
|
+
} catch (error) {
|
|
235
|
+
if (request.signal?.aborted) throw abortReason(request.signal);
|
|
236
|
+
throw connectError(error);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (!response.ok) throw await grokHttpErrorFromResponse(response);
|
|
240
|
+
|
|
241
|
+
if (!response.body) {
|
|
242
|
+
throw new GrokHttpError(
|
|
243
|
+
{ ...classifyGrokHttpError(502, "", response.headers), message: "empty body" },
|
|
244
|
+
"",
|
|
245
|
+
readGrokResponseHeaders(response.headers),
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const events = guardStream(
|
|
250
|
+
normalizeGrokSSE(response.body),
|
|
251
|
+
request.idleTimeoutMs ?? GROK_DEFAULT_IDLE_TIMEOUT_MS,
|
|
252
|
+
request.signal,
|
|
253
|
+
(reason) => {
|
|
254
|
+
if (reason) controller.abort(reason);
|
|
255
|
+
},
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
return {
|
|
259
|
+
status: response.status,
|
|
260
|
+
headers: readGrokResponseHeaders(response.headers),
|
|
261
|
+
events,
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export interface GrokProxyGetResult {
|
|
267
|
+
status: number;
|
|
268
|
+
headers: GrokResponseHeaderInfo;
|
|
269
|
+
json: unknown;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/** GET {proxy}{path} returning parsed JSON; rejects with GrokHttpError on failure. */
|
|
273
|
+
export async function grokProxyGet(
|
|
274
|
+
path: string,
|
|
275
|
+
options: {
|
|
276
|
+
headers: Record<string, string>;
|
|
277
|
+
fetch?: typeof fetch;
|
|
278
|
+
signal?: AbortSignal;
|
|
279
|
+
baseUrl?: string;
|
|
280
|
+
timeoutMs?: number;
|
|
281
|
+
},
|
|
282
|
+
): Promise<GrokProxyGetResult> {
|
|
283
|
+
const timeout = AbortSignal.timeout(options.timeoutMs ?? GROK_PROXY_GET_TIMEOUT_MS);
|
|
284
|
+
const signal = options.signal ? AbortSignal.any([options.signal, timeout]) : timeout;
|
|
285
|
+
let response: Response;
|
|
286
|
+
|
|
287
|
+
try {
|
|
288
|
+
response = await (options.fetch ?? fetch)(`${options.baseUrl ?? GROK_CHAT_PROXY_URL}${path}`, {
|
|
289
|
+
method: "GET",
|
|
290
|
+
headers: options.headers,
|
|
291
|
+
signal,
|
|
292
|
+
});
|
|
293
|
+
} catch (error) {
|
|
294
|
+
if (options.signal?.aborted) throw abortReason(options.signal);
|
|
295
|
+
throw connectError(error);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if (!response.ok) throw await grokHttpErrorFromResponse(response);
|
|
299
|
+
let json: unknown;
|
|
300
|
+
|
|
301
|
+
try {
|
|
302
|
+
json = await response.json();
|
|
303
|
+
} catch (error) {
|
|
304
|
+
throw new GrokHttpError(
|
|
305
|
+
{ ...classifyGrokHttpError(502, "", response.headers), message: "invalid JSON body" },
|
|
306
|
+
"",
|
|
307
|
+
readGrokResponseHeaders(response.headers),
|
|
308
|
+
{ cause: error },
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
return { status: response.status, headers: readGrokResponseHeaders(response.headers), json };
|
|
313
|
+
}
|