@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,446 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
|
|
3
|
+
import { GrokProtocolError } from "./grok-backend-protocol";
|
|
4
|
+
import { GROK_BUILD_VERSION } from "./grok-constants";
|
|
5
|
+
import { GrokHttpError } from "./grok-direct-client";
|
|
6
|
+
import { GrokInvalidRequestError } from "./grok-dispatcher";
|
|
7
|
+
import {
|
|
8
|
+
decodeGrokImages,
|
|
9
|
+
GrokContentPolicyError,
|
|
10
|
+
GrokMediaClient,
|
|
11
|
+
GrokMediaDeliveryError,
|
|
12
|
+
GrokMediaTimeoutError,
|
|
13
|
+
summarizeGrokVideoStatus,
|
|
14
|
+
} from "./grok-media-client";
|
|
15
|
+
|
|
16
|
+
interface Sent {
|
|
17
|
+
url: string;
|
|
18
|
+
method: string | undefined;
|
|
19
|
+
headers: Record<string, string>;
|
|
20
|
+
body: unknown;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const JPEG = Buffer.from([0xff, 0xd8, 0xff, 0xe0, 1, 2, 3, 4]);
|
|
24
|
+
const PNG = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
|
|
25
|
+
|
|
26
|
+
function fakeFetch(respond: (sent: Sent, init: RequestInit | undefined) => Promise<Response>) {
|
|
27
|
+
const calls: Sent[] = [];
|
|
28
|
+
const impl: typeof fetch = async (input, init) => {
|
|
29
|
+
const headers: Record<string, string> = {};
|
|
30
|
+
new Headers(init?.headers).forEach((value, key) => {
|
|
31
|
+
headers[key] = value;
|
|
32
|
+
});
|
|
33
|
+
const sent: Sent = {
|
|
34
|
+
url: String(input),
|
|
35
|
+
method: init?.method,
|
|
36
|
+
headers,
|
|
37
|
+
body: typeof init?.body === "string" ? JSON.parse(init.body) : init?.body,
|
|
38
|
+
};
|
|
39
|
+
calls.push(sent);
|
|
40
|
+
return respond(sent, init);
|
|
41
|
+
};
|
|
42
|
+
return { impl, calls };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function json(value: unknown, status = 200): Response {
|
|
46
|
+
return new Response(JSON.stringify(value), {
|
|
47
|
+
status,
|
|
48
|
+
headers: { "content-type": "application/json" },
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function hang(init: RequestInit | undefined): Promise<Response> {
|
|
53
|
+
return new Promise((_, reject) => {
|
|
54
|
+
init?.signal?.addEventListener("abort", () => reject(init.signal?.reason));
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function codedError(code: string): TypeError {
|
|
59
|
+
return new TypeError("fetch failed", { cause: Object.assign(new Error(code), { code }) });
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const imageResponse = {
|
|
63
|
+
data: [{ b64_json: JPEG.toString("base64"), mime_type: "image/jpeg" }],
|
|
64
|
+
usage: { cost_in_usd_ticks: 500_000_000 },
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const AUTH = { bearer: "jwt", sessionId: "sess-1" };
|
|
68
|
+
|
|
69
|
+
describe("GrokMediaClient images", () => {
|
|
70
|
+
it("sends the U0-verified generation body with the media fingerprint", async () => {
|
|
71
|
+
const { impl, calls } = fakeFetch(async () => json(imageResponse));
|
|
72
|
+
const client = new GrokMediaClient({ fetch: impl });
|
|
73
|
+
const result = await client.generateImages(AUTH, {
|
|
74
|
+
model: "",
|
|
75
|
+
prompt: "red square",
|
|
76
|
+
aspectRatio: "1:1",
|
|
77
|
+
});
|
|
78
|
+
expect(calls).toHaveLength(1);
|
|
79
|
+
const [sent] = calls;
|
|
80
|
+
expect(sent?.url).toBe("https://api.x.ai/v1/images/generations");
|
|
81
|
+
expect(sent?.method).toBe("POST");
|
|
82
|
+
expect(sent?.headers).toEqual({
|
|
83
|
+
"content-type": "application/json",
|
|
84
|
+
"user-agent": `xai-grok-build/${GROK_BUILD_VERSION}`,
|
|
85
|
+
authorization: "Bearer jwt",
|
|
86
|
+
"x-grok-session-id": "sess-1",
|
|
87
|
+
"x-grok-client-version": GROK_BUILD_VERSION,
|
|
88
|
+
"x-grok-client-identifier": "grok-shell",
|
|
89
|
+
});
|
|
90
|
+
expect(sent?.headers["content-encoding"]).toBeUndefined();
|
|
91
|
+
expect(sent?.headers["x-xai-token-auth"]).toBeUndefined();
|
|
92
|
+
// Key order matters for byte-level parity with grok-build.
|
|
93
|
+
expect(JSON.stringify(sent?.body)).toBe(
|
|
94
|
+
JSON.stringify({
|
|
95
|
+
model: "grok-imagine-image-quality",
|
|
96
|
+
prompt: "red square",
|
|
97
|
+
n: 1,
|
|
98
|
+
aspect_ratio: "1:1",
|
|
99
|
+
resolution: "1k",
|
|
100
|
+
response_format: "b64_json",
|
|
101
|
+
}),
|
|
102
|
+
);
|
|
103
|
+
expect(result.usage?.cost_in_usd_ticks).toBe(500_000_000);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it("maps SDK option names and keeps response_format b64_json", async () => {
|
|
107
|
+
const { impl, calls } = fakeFetch(async () => json(imageResponse));
|
|
108
|
+
await new GrokMediaClient({ fetch: impl }).generateImages(AUTH, {
|
|
109
|
+
model: "grok-imagine-image-pro",
|
|
110
|
+
prompt: "p",
|
|
111
|
+
n: 3,
|
|
112
|
+
resolution: "2k",
|
|
113
|
+
quality: "high",
|
|
114
|
+
outputFormat: "png",
|
|
115
|
+
});
|
|
116
|
+
expect(calls[0]?.body).toEqual({
|
|
117
|
+
model: "grok-imagine-image-pro",
|
|
118
|
+
prompt: "p",
|
|
119
|
+
n: 3,
|
|
120
|
+
resolution: "2k",
|
|
121
|
+
response_format: "b64_json",
|
|
122
|
+
quality: "high",
|
|
123
|
+
output_format: "png",
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it("decodes b64 bytes with mime type, sniffing when mime_type is absent", () => {
|
|
128
|
+
const decoded = decodeGrokImages({
|
|
129
|
+
data: [
|
|
130
|
+
{ b64_json: JPEG.toString("base64"), mime_type: "image/jpeg", respect_moderation: true },
|
|
131
|
+
{ b64_json: PNG.toString("base64"), revised_prompt: "a red square" },
|
|
132
|
+
],
|
|
133
|
+
usage: { cost_in_usd_ticks: 7 },
|
|
134
|
+
});
|
|
135
|
+
expect(Buffer.from(decoded.images[0]!.bytes).equals(JPEG)).toBe(true);
|
|
136
|
+
expect(decoded.images[0]).toMatchObject({ mediaType: "image/jpeg", respectModeration: true });
|
|
137
|
+
expect(Buffer.from(decoded.images[1]!.bytes).equals(PNG)).toBe(true);
|
|
138
|
+
expect(decoded.images[1]).toMatchObject({ mediaType: "image/png", revisedPrompt: "a red square" });
|
|
139
|
+
expect(decoded.costInUsdTicks).toBe(7);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it("rejects n outside 1..3 before any request", async () => {
|
|
143
|
+
const { impl, calls } = fakeFetch(async () => json(imageResponse));
|
|
144
|
+
const client = new GrokMediaClient({ fetch: impl });
|
|
145
|
+
await expect(client.generateImages(AUTH, { model: "", prompt: "p", n: 4 })).rejects.toThrow(
|
|
146
|
+
RangeError,
|
|
147
|
+
);
|
|
148
|
+
await expect(client.generateImages(AUTH, { model: "", prompt: "p", n: 0 })).rejects.toThrow(
|
|
149
|
+
RangeError,
|
|
150
|
+
);
|
|
151
|
+
expect(calls).toHaveLength(0);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it("turns respect_moderation false into a content-policy error", async () => {
|
|
155
|
+
const { impl } = fakeFetch(async () =>
|
|
156
|
+
json({ data: [{ b64_json: JPEG.toString("base64"), respect_moderation: false }] }),
|
|
157
|
+
);
|
|
158
|
+
await expect(
|
|
159
|
+
new GrokMediaClient({ fetch: impl }).generateImages(AUTH, { model: "", prompt: "p" }),
|
|
160
|
+
).rejects.toBeInstanceOf(GrokContentPolicyError);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it("rejects invalid base64 and malformed bodies as protocol errors", async () => {
|
|
164
|
+
const bad = fakeFetch(async () => json({ data: [{ b64_json: "not base64!" }] }));
|
|
165
|
+
await expect(
|
|
166
|
+
new GrokMediaClient({ fetch: bad.impl }).generateImages(AUTH, { model: "", prompt: "p" }),
|
|
167
|
+
).rejects.toBeInstanceOf(GrokProtocolError);
|
|
168
|
+
const empty = fakeFetch(async () => json({ data: [] }));
|
|
169
|
+
await expect(
|
|
170
|
+
new GrokMediaClient({ fetch: empty.impl }).generateImages(AUTH, { model: "", prompt: "p" }),
|
|
171
|
+
).rejects.toBeInstanceOf(GrokProtocolError);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it("wraps 403 into GrokHttpError kind entitlement", async () => {
|
|
175
|
+
const { impl, calls } = fakeFetch(async () =>
|
|
176
|
+
json({ code: "forbidden", error: "Imagine is not available on this tier" }, 403),
|
|
177
|
+
);
|
|
178
|
+
const error = await new GrokMediaClient({ fetch: impl })
|
|
179
|
+
.generateImages(AUTH, { model: "", prompt: "p" })
|
|
180
|
+
.catch((e: unknown) => e);
|
|
181
|
+
expect(error).toBeInstanceOf(GrokHttpError);
|
|
182
|
+
expect(error).toMatchObject({
|
|
183
|
+
status: 403,
|
|
184
|
+
classification: { kind: "entitlement", message: "Imagine is not available on this tier" },
|
|
185
|
+
});
|
|
186
|
+
expect(calls).toHaveLength(1);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it("aborts on the read timeout and on the total timeout", async () => {
|
|
190
|
+
const stalled = fakeFetch(async (_, init) => hang(init));
|
|
191
|
+
const readError = await new GrokMediaClient({
|
|
192
|
+
fetch: stalled.impl,
|
|
193
|
+
timeouts: { imageReadMs: 20, imageTotalMs: 1_000 },
|
|
194
|
+
})
|
|
195
|
+
.generateImages(AUTH, { model: "", prompt: "p" })
|
|
196
|
+
.catch((e: unknown) => e);
|
|
197
|
+
expect(readError).toBeInstanceOf(GrokMediaTimeoutError);
|
|
198
|
+
expect(readError).toMatchObject({ phase: "read", timeoutMs: 20 });
|
|
199
|
+
expect(stalled.calls).toHaveLength(1);
|
|
200
|
+
|
|
201
|
+
// A body that keeps trickling resets the read timer but not the total budget.
|
|
202
|
+
const trickle = fakeFetch(async (_, init) => {
|
|
203
|
+
const body = new ReadableStream<Uint8Array>({
|
|
204
|
+
start(controller) {
|
|
205
|
+
const timer = setInterval(() => controller.enqueue(new Uint8Array([32])), 5);
|
|
206
|
+
init?.signal?.addEventListener("abort", () => {
|
|
207
|
+
clearInterval(timer);
|
|
208
|
+
controller.error(init.signal?.reason);
|
|
209
|
+
});
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
return new Response(body, { status: 200 });
|
|
213
|
+
});
|
|
214
|
+
const totalError = await new GrokMediaClient({
|
|
215
|
+
fetch: trickle.impl,
|
|
216
|
+
timeouts: { imageReadMs: 30, imageTotalMs: 80 },
|
|
217
|
+
})
|
|
218
|
+
.generateImages(AUTH, { model: "", prompt: "p" })
|
|
219
|
+
.catch((e: unknown) => e);
|
|
220
|
+
expect(totalError).toBeInstanceOf(GrokMediaTimeoutError);
|
|
221
|
+
expect(totalError).toMatchObject({ phase: "total" });
|
|
222
|
+
expect(trickle.calls).toHaveLength(1);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it("propagates the caller's abort reason", async () => {
|
|
226
|
+
const { impl } = fakeFetch(async (_, init) => hang(init));
|
|
227
|
+
const controller = new AbortController();
|
|
228
|
+
const pending = new GrokMediaClient({ fetch: impl }).generateImages(AUTH, {
|
|
229
|
+
model: "",
|
|
230
|
+
prompt: "p",
|
|
231
|
+
signal: controller.signal,
|
|
232
|
+
});
|
|
233
|
+
const reason = new Error("client went away");
|
|
234
|
+
controller.abort(reason);
|
|
235
|
+
await expect(pending).rejects.toBe(reason);
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
it("sends edits as JSON with image/images url objects", async () => {
|
|
239
|
+
const { impl, calls } = fakeFetch(async () => json(imageResponse));
|
|
240
|
+
const client = new GrokMediaClient({ fetch: impl });
|
|
241
|
+
await client.editImages(AUTH, {
|
|
242
|
+
model: "",
|
|
243
|
+
prompt: "make it blue",
|
|
244
|
+
images: ["data:image/png;base64,AAAA"],
|
|
245
|
+
});
|
|
246
|
+
await client.editImages(AUTH, {
|
|
247
|
+
model: "grok-imagine-image",
|
|
248
|
+
prompt: "merge",
|
|
249
|
+
aspectRatio: "16:9",
|
|
250
|
+
images: ["https://example.com/a.png", "https://example.com/b.png"],
|
|
251
|
+
});
|
|
252
|
+
expect(calls.map((c) => c.url)).toEqual([
|
|
253
|
+
"https://api.x.ai/v1/images/edits",
|
|
254
|
+
"https://api.x.ai/v1/images/edits",
|
|
255
|
+
]);
|
|
256
|
+
expect(calls[0]?.headers["content-type"]).toBe("application/json");
|
|
257
|
+
expect(calls[0]?.body).toEqual({
|
|
258
|
+
model: "grok-imagine-image-quality",
|
|
259
|
+
prompt: "make it blue",
|
|
260
|
+
n: 1,
|
|
261
|
+
resolution: "1k",
|
|
262
|
+
response_format: "b64_json",
|
|
263
|
+
image: { url: "data:image/png;base64,AAAA" },
|
|
264
|
+
});
|
|
265
|
+
expect(calls[1]?.body).toEqual({
|
|
266
|
+
model: "grok-imagine-image",
|
|
267
|
+
prompt: "merge",
|
|
268
|
+
n: 1,
|
|
269
|
+
aspect_ratio: "16:9",
|
|
270
|
+
resolution: "1k",
|
|
271
|
+
response_format: "b64_json",
|
|
272
|
+
images: [{ url: "https://example.com/a.png" }, { url: "https://example.com/b.png" }],
|
|
273
|
+
});
|
|
274
|
+
await expect(client.editImages(AUTH, { model: "", prompt: "p", images: [] })).rejects.toThrow(
|
|
275
|
+
GrokInvalidRequestError,
|
|
276
|
+
);
|
|
277
|
+
expect(calls).toHaveLength(2);
|
|
278
|
+
});
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
describe("GrokMediaClient retry safety", () => {
|
|
282
|
+
it("does not re-issue a POST that failed after send", async () => {
|
|
283
|
+
const { impl, calls } = fakeFetch(async () => {
|
|
284
|
+
throw codedError("ECONNRESET");
|
|
285
|
+
});
|
|
286
|
+
const error = await new GrokMediaClient({ fetch: impl })
|
|
287
|
+
.generateImages(AUTH, { model: "", prompt: "p" })
|
|
288
|
+
.catch((e: unknown) => e);
|
|
289
|
+
expect(error).toBeInstanceOf(GrokMediaDeliveryError);
|
|
290
|
+
expect(error).not.toBeInstanceOf(GrokHttpError);
|
|
291
|
+
expect(calls).toHaveLength(1);
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
it("treats a body read failure after the response as delivered", async () => {
|
|
295
|
+
const { impl, calls } = fakeFetch(async () => {
|
|
296
|
+
const body = new ReadableStream<Uint8Array>({
|
|
297
|
+
pull(controller) {
|
|
298
|
+
controller.error(codedError("ECONNREFUSED"));
|
|
299
|
+
},
|
|
300
|
+
});
|
|
301
|
+
return new Response(body, { status: 200 });
|
|
302
|
+
});
|
|
303
|
+
const error = await new GrokMediaClient({ fetch: impl })
|
|
304
|
+
.startVideo(AUTH, { model: "", prompt: "p" })
|
|
305
|
+
.catch((e: unknown) => e);
|
|
306
|
+
expect(error).toBeInstanceOf(GrokMediaDeliveryError);
|
|
307
|
+
expect(calls).toHaveLength(1);
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
it("surfaces a pre-send connect failure as a transient GrokHttpError", async () => {
|
|
311
|
+
const { impl, calls } = fakeFetch(async () => {
|
|
312
|
+
throw codedError("ECONNREFUSED");
|
|
313
|
+
});
|
|
314
|
+
const error = await new GrokMediaClient({ fetch: impl })
|
|
315
|
+
.startVideo(AUTH, { model: "", prompt: "p" })
|
|
316
|
+
.catch((e: unknown) => e);
|
|
317
|
+
expect(error).toBeInstanceOf(GrokHttpError);
|
|
318
|
+
expect(error).toMatchObject({ status: 0, classification: { kind: "transient" } });
|
|
319
|
+
expect(calls).toHaveLength(1);
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
it("maps any GET network failure to a transient GrokHttpError", async () => {
|
|
323
|
+
const { impl } = fakeFetch(async () => {
|
|
324
|
+
throw codedError("ECONNRESET");
|
|
325
|
+
});
|
|
326
|
+
const error = await new GrokMediaClient({ fetch: impl })
|
|
327
|
+
.getVideo(AUTH, "req-1")
|
|
328
|
+
.catch((e: unknown) => e);
|
|
329
|
+
expect(error).toBeInstanceOf(GrokHttpError);
|
|
330
|
+
expect(error).toMatchObject({ classification: { kind: "transient" } });
|
|
331
|
+
});
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
describe("GrokMediaClient video", () => {
|
|
335
|
+
it("starts a video with grok-build field names and returns the request id", async () => {
|
|
336
|
+
const { impl, calls } = fakeFetch(async () => json({ request_id: "req-1" }));
|
|
337
|
+
const client = new GrokMediaClient({ fetch: impl });
|
|
338
|
+
const started = await client.startVideo(AUTH, {
|
|
339
|
+
model: "",
|
|
340
|
+
prompt: "a cat",
|
|
341
|
+
duration: 6,
|
|
342
|
+
aspectRatio: "16:9",
|
|
343
|
+
resolution: "720p",
|
|
344
|
+
image: "https://example.com/first.png",
|
|
345
|
+
lastFrame: "https://example.com/last.png",
|
|
346
|
+
referenceImages: ["https://example.com/ref.png"],
|
|
347
|
+
mode: "reference-to-video",
|
|
348
|
+
});
|
|
349
|
+
expect(started.request_id).toBe("req-1");
|
|
350
|
+
const [sent] = calls;
|
|
351
|
+
expect(sent?.url).toBe("https://api.x.ai/v1/videos/generations");
|
|
352
|
+
expect(sent?.headers["user-agent"]).toBe(`xai-grok-build/${GROK_BUILD_VERSION}`);
|
|
353
|
+
expect(JSON.stringify(sent?.body)).toBe(
|
|
354
|
+
JSON.stringify({
|
|
355
|
+
model: "grok-imagine-video-1.5",
|
|
356
|
+
prompt: "a cat",
|
|
357
|
+
image: { url: "https://example.com/first.png" },
|
|
358
|
+
duration: 6,
|
|
359
|
+
aspect_ratio: "16:9",
|
|
360
|
+
resolution: "720p",
|
|
361
|
+
reference_images: [{ url: "https://example.com/ref.png" }],
|
|
362
|
+
last_frame: { url: "https://example.com/last.png" },
|
|
363
|
+
}),
|
|
364
|
+
);
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
it("rejects reference-to-video without references and bad durations without a request", async () => {
|
|
368
|
+
const { impl, calls } = fakeFetch(async () => json({ request_id: "x" }));
|
|
369
|
+
const client = new GrokMediaClient({ fetch: impl });
|
|
370
|
+
await expect(
|
|
371
|
+
client.startVideo(AUTH, { model: "", prompt: "p", mode: "reference-to-video" }),
|
|
372
|
+
).rejects.toThrow(GrokInvalidRequestError);
|
|
373
|
+
await expect(client.startVideo(AUTH, { model: "", prompt: "p", duration: 16 })).rejects.toThrow(
|
|
374
|
+
GrokInvalidRequestError,
|
|
375
|
+
);
|
|
376
|
+
expect(calls).toHaveLength(0);
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
it("polls GET /videos/{id} and returns the upstream URL unchanged", async () => {
|
|
380
|
+
const url = "https://vidgen.x.ai/output/abc.mp4?sig=XYZ&exp=1";
|
|
381
|
+
const replies = [
|
|
382
|
+
new Response("", { status: 202 }),
|
|
383
|
+
json({ status: "pending", progress: 40 }),
|
|
384
|
+
json({ status: "done", video: { url, duration: 6 }, usage: { cost_in_usd_ticks: 9 } }),
|
|
385
|
+
];
|
|
386
|
+
const { impl, calls } = fakeFetch(async () => replies.shift() ?? json({}, 500));
|
|
387
|
+
const client = new GrokMediaClient({ fetch: impl });
|
|
388
|
+
const first = await client.getVideo(AUTH, "req/1");
|
|
389
|
+
const second = await client.getVideo(AUTH, "req/1");
|
|
390
|
+
const done = await client.getVideo(AUTH, "req/1");
|
|
391
|
+
expect(calls[0]?.url).toBe("https://api.x.ai/v1/videos/req%2F1");
|
|
392
|
+
expect(calls[0]?.method).toBe("GET");
|
|
393
|
+
expect(calls[0]?.body).toBeUndefined();
|
|
394
|
+
expect(calls[0]?.headers["x-grok-session-id"]).toBe("sess-1");
|
|
395
|
+
expect(first.status).toBe("pending");
|
|
396
|
+
expect(summarizeGrokVideoStatus(second)).toEqual({ status: "pending", mimeType: "video/mp4" });
|
|
397
|
+
expect(summarizeGrokVideoStatus(done)).toEqual({
|
|
398
|
+
status: "done",
|
|
399
|
+
url,
|
|
400
|
+
mimeType: "video/mp4",
|
|
401
|
+
usageTicks: 9,
|
|
402
|
+
});
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
it("summarizes failures, expiry, nested file_output and moderation", async () => {
|
|
406
|
+
expect(
|
|
407
|
+
summarizeGrokVideoStatus({ status: "failed", error: { code: "x", message: "boom" } }),
|
|
408
|
+
).toMatchObject({ status: "failed", error: "boom" });
|
|
409
|
+
expect(summarizeGrokVideoStatus({ status: "expired" })).toMatchObject({ status: "failed" });
|
|
410
|
+
expect(summarizeGrokVideoStatus({ status: "done" })).toMatchObject({ status: "failed" });
|
|
411
|
+
expect(
|
|
412
|
+
summarizeGrokVideoStatus({
|
|
413
|
+
status: "done",
|
|
414
|
+
video: { file_output: { public_url: "https://files.x.ai/v.mp4" } },
|
|
415
|
+
}),
|
|
416
|
+
).toMatchObject({ status: "done", url: "https://files.x.ai/v.mp4" });
|
|
417
|
+
// A top-level file_output is not where @ai-sdk/xai looks for the URL.
|
|
418
|
+
expect(
|
|
419
|
+
summarizeGrokVideoStatus({
|
|
420
|
+
status: "done",
|
|
421
|
+
file_output: { public_url: "https://files.x.ai/v.mp4" },
|
|
422
|
+
}),
|
|
423
|
+
).toMatchObject({ status: "failed" });
|
|
424
|
+
|
|
425
|
+
const { impl } = fakeFetch(async () =>
|
|
426
|
+
json({ status: "done", video: { url: "https://x/v.mp4", respect_moderation: false } }),
|
|
427
|
+
);
|
|
428
|
+
await expect(
|
|
429
|
+
new GrokMediaClient({ fetch: impl }).getVideo(AUTH, "req-1"),
|
|
430
|
+
).rejects.toBeInstanceOf(GrokContentPolicyError);
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
it("classifies a poll 401 and a poll timeout", async () => {
|
|
434
|
+
const unauthorized = fakeFetch(async () => json({ error: "expired token" }, 401));
|
|
435
|
+
await expect(
|
|
436
|
+
new GrokMediaClient({ fetch: unauthorized.impl }).getVideo(AUTH, "req-1"),
|
|
437
|
+
).rejects.toMatchObject({ classification: { kind: "auth" } });
|
|
438
|
+
const stalled = fakeFetch(async (_, init) => hang(init));
|
|
439
|
+
await expect(
|
|
440
|
+
new GrokMediaClient({ fetch: stalled.impl, timeouts: { videoPollMs: 20 } }).getVideo(
|
|
441
|
+
AUTH,
|
|
442
|
+
"req-1",
|
|
443
|
+
),
|
|
444
|
+
).rejects.toBeInstanceOf(GrokMediaTimeoutError);
|
|
445
|
+
});
|
|
446
|
+
});
|