cartethyia 2.5.4 → 2.5.6
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/.env.example +13 -4
- package/README.md +2 -2
- package/dist/dashboard/assets/{BannedPage-C2orBsI2.js → BannedPage-CADDq4IU.js} +1 -1
- package/dist/dashboard/assets/BannedPage-CADDq4IU.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-_z1hID7E.js +1 -0
- package/dist/dashboard/assets/CliToolDetailPage-_z1hID7E.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolsPage-BV6pxVQo.js → CliToolsPage-DuO1Z7dq.js} +1 -1
- package/dist/dashboard/assets/CliToolsPage-DuO1Z7dq.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-DNEPi3xU.js +1 -0
- package/dist/dashboard/assets/CombosPage-DNEPi3xU.js.gz +0 -0
- package/dist/dashboard/assets/{ConsoleLogPage-CW-wPmJ6.js → ConsoleLogPage-BVdpfiz1.js} +1 -1
- package/dist/dashboard/assets/ConsoleLogPage-BVdpfiz1.js.gz +0 -0
- package/dist/dashboard/assets/{CustomizationPage-0p8uX7vB.js → CustomizationPage-BR5HHvbi.js} +1 -1
- package/dist/dashboard/assets/CustomizationPage-BR5HHvbi.js.gz +0 -0
- package/dist/dashboard/assets/{HealthEventsModal-BjqSMfko.js → HealthEventsModal-mUfcknqm.js} +1 -1
- package/dist/dashboard/assets/HealthEventsModal-mUfcknqm.js.gz +0 -0
- package/dist/dashboard/assets/{LoginPage-BvzCx8Kr.js → LoginPage-V5faV9V2.js} +1 -1
- package/dist/dashboard/assets/LoginPage-V5faV9V2.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-C7pDb41R.js +1 -0
- package/dist/dashboard/assets/ModelPicker-C7pDb41R.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-BDu20Jdx.js +1 -0
- package/dist/dashboard/assets/OverviewPage-BDu20Jdx.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-Dllr_aFO.js +3 -0
- package/dist/dashboard/assets/ProviderDetailPage-Dllr_aFO.js.gz +0 -0
- package/dist/dashboard/assets/{ProvidersPage-Js9jUPSb.js → ProvidersPage-uPiGVTTK.js} +1 -1
- package/dist/dashboard/assets/ProvidersPage-uPiGVTTK.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-BaUkO-4O.js +7 -0
- package/dist/dashboard/assets/ProxyPage-BaUkO-4O.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-o0IFAsFb.js +1 -0
- package/dist/dashboard/assets/QuotaPage-o0IFAsFb.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-CYWqbXjp.js +1 -0
- package/dist/dashboard/assets/SettingsPage-CYWqbXjp.js.gz +0 -0
- package/dist/dashboard/assets/{SetupPage-DDbMQsoo.js → SetupPage-UB690SE2.js} +1 -1
- package/dist/dashboard/assets/SetupPage-UB690SE2.js.gz +0 -0
- package/dist/dashboard/assets/{SortableList-CB3Z7VTf.js → SortableList-BLujdX7N.js} +1 -1
- package/dist/dashboard/assets/SortableList-BLujdX7N.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-D95UCPHI.js → StudioPage-D5C2raAc.js} +3 -3
- package/dist/dashboard/assets/StudioPage-D5C2raAc.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-8uIcxa1W.js +56 -0
- package/dist/dashboard/assets/UsagePage-8uIcxa1W.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-DQYLQxpW.js +1 -0
- package/dist/dashboard/assets/api-keys-DQYLQxpW.js.gz +0 -0
- package/dist/dashboard/assets/app-CX_Z8cMs.css +2 -0
- package/dist/dashboard/assets/app-CX_Z8cMs.css.gz +0 -0
- package/dist/dashboard/assets/{assert-Chi7pCnt.js → assert-C9FD_JwJ.js} +1 -1
- package/dist/dashboard/assets/assert-C9FD_JwJ.js.gz +0 -0
- package/dist/dashboard/assets/{badge-D4pQapKj.js → badge-CR72dasT.js} +1 -1
- package/dist/dashboard/assets/badge-CR72dasT.js.gz +0 -0
- package/dist/dashboard/assets/{banner-DH6cg-_B.js → banner-CI9_eSfO.js} +1 -1
- package/dist/dashboard/assets/banner-CI9_eSfO.js.gz +0 -0
- package/dist/dashboard/assets/{card-BKqlUOe4.js → card-DS7CQEsO.js} +1 -1
- package/dist/dashboard/assets/card-DS7CQEsO.js.gz +0 -0
- package/dist/dashboard/assets/{cli-tools-fTgljTGA.js → cli-tools-CNqzrfdb.js} +1 -1
- package/dist/dashboard/assets/cli-tools-CNqzrfdb.js.gz +0 -0
- package/dist/dashboard/assets/{clipboard-button-D5Ys_sM0.js → clipboard-button-611pSsaX.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-611pSsaX.js.gz +0 -0
- package/dist/dashboard/assets/{fingerprint-pattern-BWuHtbW4.js → concurrency-BDF0ok7y.js} +1 -1
- package/dist/dashboard/assets/concurrency-BDF0ok7y.js.gz +0 -0
- package/dist/dashboard/assets/dialog-k37exjNt.js +1 -0
- package/dist/dashboard/assets/dialog-k37exjNt.js.gz +0 -0
- package/dist/dashboard/assets/drawer-CX7N8ucR.js +1 -0
- package/dist/dashboard/assets/drawer-CX7N8ucR.js.gz +0 -0
- package/dist/dashboard/assets/{entry-CQBr6U9c.js → entry-B9Xvtnfj.js} +1 -1
- package/dist/dashboard/assets/entry-B9Xvtnfj.js.gz +0 -0
- package/dist/dashboard/assets/entry-CTP8AyiS.js +1 -0
- package/dist/dashboard/assets/entry-CTP8AyiS.js.gz +0 -0
- package/dist/dashboard/assets/entry-DxBHnauy.js +4 -0
- package/dist/dashboard/assets/entry-DxBHnauy.js.gz +0 -0
- package/dist/dashboard/assets/{github-badge-BlptL8Uf.js → github-badge-sltk1qUi.js} +1 -1
- package/dist/dashboard/assets/github-badge-sltk1qUi.js.gz +0 -0
- package/dist/dashboard/assets/{index-js6TJvdr.js → index-CmxZr14F.js} +2 -2
- package/dist/dashboard/assets/index-CmxZr14F.js.gz +0 -0
- package/dist/dashboard/assets/{inline-DUWDSmDV.js → inline-BhY2sSnO.js} +1 -1
- package/dist/dashboard/assets/inline-BhY2sSnO.js.gz +0 -0
- package/dist/dashboard/assets/{input-LVjgZq4f.js → input-V1umFiBk.js} +1 -1
- package/dist/dashboard/assets/input-V1umFiBk.js.gz +0 -0
- package/dist/dashboard/assets/layout-BBOYycyS.js +1 -0
- package/dist/dashboard/assets/layout-BBOYycyS.js.gz +0 -0
- package/dist/dashboard/assets/{list-row-DupvLfDx.js → list-row-1zzZYM0B.js} +1 -1
- package/dist/dashboard/assets/list-row-1zzZYM0B.js.gz +0 -0
- package/dist/dashboard/assets/{live-C4pEJUqI.js → live-BivGfuUs.js} +1 -1
- package/dist/dashboard/assets/live-BivGfuUs.js.gz +0 -0
- package/dist/dashboard/assets/{model-bans-BRiuufkB.js → model-bans-CmffjLuG.js} +1 -1
- package/dist/dashboard/assets/model-bans-CmffjLuG.js.gz +0 -0
- package/dist/dashboard/assets/{mutation-B4ROwk8L.js → mutation-46P0oWMe.js} +1 -1
- package/dist/dashboard/assets/mutation-46P0oWMe.js.gz +0 -0
- package/dist/dashboard/assets/numbers-DkfMR5Qs.js +2 -0
- package/dist/dashboard/assets/numbers-DkfMR5Qs.js.gz +0 -0
- package/dist/dashboard/assets/{query-keys-gmjzC3RS.js → query-keys-BFhGpwR1.js} +1 -1
- package/dist/dashboard/assets/query-keys-BFhGpwR1.js.gz +0 -0
- package/dist/dashboard/assets/{routing-C7tBT5o-.js → routing-CAb4F37R.js} +1 -1
- package/dist/dashboard/assets/routing-CAb4F37R.js.gz +0 -0
- package/dist/dashboard/assets/{select-WUCXorXw.js → select-BYR_5K2c.js} +1 -1
- package/dist/dashboard/assets/select-BYR_5K2c.js.gz +0 -0
- package/dist/dashboard/assets/{settings-C_zKl1GS.js → settings-DNX9sydi.js} +1 -1
- package/dist/dashboard/assets/settings-DNX9sydi.js.gz +0 -0
- package/dist/dashboard/assets/{stack-_3O0F8Xn.js → stack-BHTfXdz_.js} +1 -1
- package/dist/dashboard/assets/stack-BHTfXdz_.js.gz +0 -0
- package/dist/dashboard/assets/{state-BgTxHcJS.js → state-mPDRlQkl.js} +1 -1
- package/dist/dashboard/assets/state-mPDRlQkl.js.gz +0 -0
- package/dist/dashboard/assets/{switch-DEjTtOWf.js → switch-DOmJxg7H.js} +1 -1
- package/dist/dashboard/assets/switch-DOmJxg7H.js.gz +0 -0
- package/dist/dashboard/assets/tokens-COzario9.js +1 -0
- package/dist/dashboard/assets/tokens-COzario9.js.gz +0 -0
- package/dist/dashboard/assets/use-modal-focus-By7oJoGd.js +1 -0
- package/dist/dashboard/assets/use-modal-focus-By7oJoGd.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-BWSC0UAS.js → useMutation-Dem0lXQj.js} +1 -1
- package/dist/dashboard/assets/useMutation-Dem0lXQj.js.gz +0 -0
- package/dist/dashboard/index.html +1 -1
- package/dist/dashboard/index.html.gz +0 -0
- package/dist/dashboard/precompressed-assets.json +54 -54
- package/migrations/README.md +52 -52
- package/migrations/lite-sqlite3/0000_baseline.sql +2 -2
- package/package.json +96 -95
- package/scripts/cli/admin.ts +125 -0
- package/scripts/cli/{ui/background.ts → background.ts} +1 -1
- package/scripts/cli/{command/console-menu.ts → console-menu.ts} +275 -244
- package/scripts/cli/doctor.ts +37 -0
- package/scripts/cli/{support/env.ts → env.ts} +99 -99
- package/scripts/cli/{gateway/spawn.ts → gateway.ts} +112 -24
- package/scripts/cli/instance.ts +224 -0
- package/scripts/cli/main.ts +61 -18
- package/scripts/cli/{support/platform.ts → platform.ts} +64 -64
- package/scripts/cli/port.ts +100 -0
- package/scripts/cli/{support/services.ts → services.ts} +88 -88
- package/scripts/cli/{command/setup.ts → setup.ts} +35 -34
- package/scripts/cli/{command/start.ts → start.ts} +10 -5
- package/scripts/cli/{support/startup-signal.ts → startup-signal.ts} +23 -23
- package/scripts/cli/tray/index.ts +62 -12
- package/scripts/cli/{command/tray.ts → tray.ts} +208 -208
- package/scripts/cli/{command/update.ts → update.ts} +39 -39
- package/scripts/cli/version.ts +67 -0
- package/scripts/cli/{ui/wordmark.ts → wordmark.ts} +58 -58
- package/src/boot/app.ts +697 -693
- package/src/boot/dependencies.ts +647 -646
- package/src/boot/timeout.ts +2 -13
- package/src/config/cooldowns.ts +186 -138
- package/src/config/network.ts +8 -14
- package/src/config/providers.ts +7 -10
- package/src/config/runtime.ts +76 -76
- package/src/config/security.ts +15 -0
- package/src/config/spec.ts +20 -30
- package/src/config/telemetry.ts +0 -20
- package/src/config/timeouts.ts +0 -6
- package/src/console/admin/model-abuse/contracts.ts +28 -6
- package/src/console/admin/model-abuse/routes.ts +56 -3
- package/src/console/api-keys/contracts.ts +410 -414
- package/src/console/api-keys/routes.ts +881 -880
- package/src/console/auth/credentials.ts +3 -3
- package/src/console/auth/setup-service.ts +2 -2
- package/src/console/backup/export-endpoint.ts +6 -2
- package/src/console/backup/nine-router.ts +2 -5
- package/src/console/backup/service.ts +4 -0
- package/src/console/backup/store.ts +192 -29
- package/src/console/backup/validate.ts +3 -45
- package/src/console/cli-tools/fs-ops.ts +487 -487
- package/src/console/console-router-guards.ts +1 -1
- package/src/console/data-management/contracts.ts +13 -6
- package/src/console/data-management/operations.ts +23 -18
- package/src/console/data-management/routes.ts +0 -1
- package/src/console/domain-registration.ts +10 -5
- package/src/console/observability/live.ts +22 -0
- package/src/console/observability/store.ts +39 -16
- package/src/console/observability/usage-dimensions.ts +4 -4
- package/src/console/providers/catalog/catalog-projections.ts +6 -7
- package/src/console/providers/catalog/model-operations.ts +5 -2
- package/src/console/providers/catalog/provider-operations.ts +4 -4
- package/src/console/providers/catalog/public-model-store.ts +8 -11
- package/src/console/providers/catalog/store.ts +1553 -1553
- package/src/console/providers/oauth/routes.ts +8 -12
- package/src/console/quota/account-quota-tenant.ts +6 -6
- package/src/console/quota/quota-view.ts +1 -1
- package/src/console/remote-support/contracts.ts +61 -0
- package/src/console/remote-support/routes.ts +65 -0
- package/src/console/remote-support/service.ts +207 -0
- package/src/console/response-compression.ts +0 -3
- package/src/console/routing/pools/probe-result.ts +3 -2
- package/src/console/routing/pools/probe-verdict.ts +132 -132
- package/src/console/routing/pools/store.ts +8 -8
- package/src/console/settings/contracts.ts +58 -2
- package/src/console/settings/store.ts +30 -3
- package/src/console/shared/cursor.ts +8 -8
- package/src/console/shared/errors.ts +1 -3
- package/src/network/http2-bidi.ts +2 -4
- package/src/network/pool/agent.ts +478 -478
- package/src/network/pool/selector.ts +5 -4
- package/src/network/retry.ts +1 -2
- package/src/network/ssrf.ts +5 -4
- package/src/observability/event-compaction.ts +80 -78
- package/src/observability/event-day-index.ts +13 -15
- package/src/observability/event-log.ts +4 -4
- package/src/observability/logger.ts +6 -6
- package/src/observability/payload-capture.ts +151 -33
- package/src/observability/payload-store.ts +397 -440
- package/src/observability/redaction.ts +5 -6
- package/src/observability/request-notify.ts +43 -0
- package/src/observability/telemetry-status.ts +8 -9
- package/src/observability/token-speed.ts +5 -4
- package/src/persistence/db-mode.ts +7 -7
- package/src/persistence/readiness.ts +1 -1
- package/src/persistence/schema.ts +1406 -1402
- package/src/persistence/shared-cache.ts +1 -2
- package/src/persistence/stores/api-key-store.ts +18 -6
- package/src/persistence/stores/share-store.ts +4 -3
- package/src/persistence/stores/telemetry-store.ts +236 -237
- package/src/protocol/canonical-model.ts +11 -3
- package/src/protocol/codecs.ts +138 -4
- package/src/protocol/gateway-error.ts +6 -4
- package/src/protocol/request/chat.ts +21 -1
- package/src/protocol/request/codex.ts +10 -1
- package/src/protocol/request/messages.ts +4 -5
- package/src/protocol/request/responses.ts +9 -1
- package/src/protocol/response/chat.ts +34 -4
- package/src/protocol/response/codex.ts +7 -4
- package/src/protocol/response/dsml.ts +140 -28
- package/src/protocol/response/responses.ts +11 -5
- package/src/protocol/stream-error-frames.ts +4 -5
- package/src/providers/authentication/jwt-validator.ts +9 -10
- package/src/providers/authentication/oauth-refresh-service.ts +9 -11
- package/src/providers/configured-openai-providers.ts +134 -136
- package/src/providers/discovery/catalog/catalog-blob.ts +18 -18
- package/src/providers/discovery/catalog/catalog-format.ts +3 -6
- package/src/providers/discovery/models-dev-catalog.ts +354 -354
- package/src/providers/discovery/probe-phases.ts +4 -4
- package/src/providers/discovery/probe-wire.ts +6 -13
- package/src/providers/discovery/probing-service.ts +1117 -1045
- package/src/providers/integrations/alibabacp/alibabacp.ts +1 -1
- package/src/providers/integrations/antigravity/antigravity-quota.ts +5 -7
- package/src/providers/integrations/buddy/buddy-chat-shared.ts +23 -2
- package/src/providers/integrations/buddy/buddy-checkin.ts +1 -4
- package/src/providers/integrations/buddy/buddy-cli-headers.ts +23 -6
- package/src/providers/integrations/buddy/buddy-conversation-headers.ts +0 -1
- package/src/providers/integrations/buddy/buddy-identity.ts +52 -0
- package/src/providers/integrations/buddy/buddy-media.ts +2 -2
- package/src/providers/integrations/buddy/buddy-usage-store.ts +3 -4
- package/src/providers/integrations/buddy/codebuddy/quota.ts +2 -13
- package/src/providers/integrations/buddy/codebuddy/shared.ts +118 -115
- package/src/providers/integrations/buddy/workbuddy/quota.ts +0 -11
- package/src/providers/integrations/buddy/workbuddy/shared.ts +5 -2
- package/src/providers/integrations/claude/claude.ts +608 -608
- package/src/providers/integrations/cline/cline-quota.ts +132 -132
- package/src/providers/integrations/cline/cline.ts +537 -539
- package/src/providers/integrations/codex/codex-oauth.ts +1 -1
- package/src/providers/integrations/commandcode/commandcode.ts +674 -675
- package/src/providers/integrations/commandcode/discovery.ts +117 -116
- package/src/providers/integrations/cursor/cursor-auth.ts +418 -418
- package/src/providers/integrations/cursor/cursor-quota.ts +309 -310
- package/src/providers/integrations/cursor/cursor.ts +11 -163
- package/src/providers/integrations/factory/factory-models.ts +3 -5
- package/src/providers/integrations/grok/grok-install-id.ts +2 -3
- package/src/providers/integrations/grok/grok.ts +6 -7
- package/src/providers/integrations/hermes.ts +14 -18
- package/src/providers/integrations/kiro/kiro-discovery.ts +1 -1
- package/src/providers/integrations/kiro/kiro.ts +1 -1
- package/src/providers/integrations/mistral.ts +6 -6
- package/src/providers/integrations/opencode/fingerprint.ts +3 -3
- package/src/providers/integrations/opencode/provider.ts +8 -8
- package/src/providers/integrations/qoder/qoder-catalog.ts +450 -450
- package/src/providers/integrations/qoder/qoder-oauth.ts +388 -310
- package/src/providers/integrations/qoder/qoder.ts +884 -865
- package/src/providers/integrations/shared/connect.ts +6 -6
- package/src/providers/integrations/trae/trae-catalog.ts +1 -2
- package/src/providers/integrations/trae/trae.ts +2 -2
- package/src/providers/integrations/zcode/oauth.ts +6 -1
- package/src/providers/operations/account-health-service.ts +1528 -1523
- package/src/providers/operations/account-reset-service.ts +1 -1
- package/src/providers/operations/client-versions.ts +6 -6
- package/src/providers/operations/custom-cli-headers.ts +3 -4
- package/src/providers/operations/provider-catalog-seeder.ts +95 -95
- package/src/providers/operations/provider-catalog-service.ts +3 -4
- package/src/providers/provider-metadata.ts +4 -8
- package/src/providers/provider-registry.ts +6 -7
- package/src/providers/provider-usage.ts +1 -1
- package/src/security/abuse.ts +15 -10
- package/src/security/access-control.ts +144 -146
- package/src/security/admission/contracts.ts +11 -0
- package/src/security/admission/in-memory-store.ts +4 -3
- package/src/security/admission/lease-sweep.ts +6 -6
- package/src/security/admission/reasons.ts +5 -1
- package/src/security/admission/redis-store.ts +211 -210
- package/src/security/admission/service.ts +4 -0
- package/src/security/api-key-auth.ts +504 -504
- package/src/security/ip-boundary.ts +155 -15
- package/src/security/model-abuse.ts +366 -94
- package/src/security/outbound-headers.ts +3 -4
- package/src/shared/sleep.ts +4 -0
- package/src/transport/dispatch/attempt-finalize.ts +47 -5
- package/src/transport/dispatch/attempt-loop.ts +531 -530
- package/src/transport/dispatch/stream-bridge.ts +1 -3
- package/src/transport/dispatch/streaming/finalize.ts +1 -1
- package/src/transport/dispatch/streaming/stream.ts +5 -6
- package/src/transport/failure-policy.ts +162 -8
- package/src/transport/guards/auth-guard.ts +7 -4
- package/src/transport/guards/concurrency-guard.ts +0 -5
- package/src/transport/guards/error-lifecycle.ts +13 -6
- package/src/transport/guards/pipeline.ts +15 -3
- package/src/transport/guards/request-context.ts +8 -2
- package/src/transport/request/inflight.ts +5 -6
- package/src/transport/request/preparer.ts +2 -2
- package/src/transport/request/state.ts +12 -8
- package/src/transport/routing/admission.ts +4 -0
- package/src/transport/routing/route-catalog.ts +8 -1
- package/src/transport/routing/route-model.ts +46 -14
- package/src/transport/routing/router.ts +21 -14
- package/src/transport/surface/completion.ts +2 -3
- package/src/transport/surface/dialects.ts +5 -6
- package/src/transport/surface/responses/encode.ts +38 -0
- package/src/transport/surface/responses/parse.ts +4 -0
- package/src/transport/translation/capabilities.ts +2 -3
- package/src/transport/translation/quirks.ts +3 -3
- package/src/workers/buddy-growth-tasks.ts +5 -10
- package/src/workers/daily-checkin.ts +17 -23
- package/src/workers/quota-refresh-sweep.ts +26 -26
- package/dist/dashboard/assets/BannedPage-C2orBsI2.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-DUcZ6pMB.js +0 -1
- package/dist/dashboard/assets/CliToolDetailPage-DUcZ6pMB.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-BV6pxVQo.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-Bsan4679.js +0 -1
- package/dist/dashboard/assets/CombosPage-Bsan4679.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-CW-wPmJ6.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-0p8uX7vB.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-BjqSMfko.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-BvzCx8Kr.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-BY1aKEkP.js +0 -1
- package/dist/dashboard/assets/ModelPicker-BY1aKEkP.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-DOQpy1y0.js +0 -1
- package/dist/dashboard/assets/OverviewPage-DOQpy1y0.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-ecxyUMzt.js +0 -3
- package/dist/dashboard/assets/ProviderDetailPage-ecxyUMzt.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-Js9jUPSb.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-J3TBi3Kx.js +0 -7
- package/dist/dashboard/assets/ProxyPage-J3TBi3Kx.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-C1gZS6xr.js +0 -1
- package/dist/dashboard/assets/QuotaPage-C1gZS6xr.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-CS86ho5X.js +0 -1
- package/dist/dashboard/assets/SettingsPage-CS86ho5X.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-DDbMQsoo.js.gz +0 -0
- package/dist/dashboard/assets/SortableList-CB3Z7VTf.js.gz +0 -0
- package/dist/dashboard/assets/StudioPage-D95UCPHI.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-Cnae9SWA.js +0 -56
- package/dist/dashboard/assets/UsagePage-Cnae9SWA.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-VY68Mhd1.js +0 -1
- package/dist/dashboard/assets/api-keys-VY68Mhd1.js.gz +0 -0
- package/dist/dashboard/assets/app-COlK95oi.css +0 -2
- package/dist/dashboard/assets/app-COlK95oi.css.gz +0 -0
- package/dist/dashboard/assets/assert-Chi7pCnt.js.gz +0 -0
- package/dist/dashboard/assets/badge-D4pQapKj.js.gz +0 -0
- package/dist/dashboard/assets/banner-DH6cg-_B.js.gz +0 -0
- package/dist/dashboard/assets/card-BKqlUOe4.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-fTgljTGA.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-D5Ys_sM0.js.gz +0 -0
- package/dist/dashboard/assets/dialog-BYj-8WuK.js +0 -1
- package/dist/dashboard/assets/dialog-BYj-8WuK.js.gz +0 -0
- package/dist/dashboard/assets/drawer-CmXIZ6H1.js +0 -1
- package/dist/dashboard/assets/drawer-CmXIZ6H1.js.gz +0 -0
- package/dist/dashboard/assets/entry-CBg5zeu8.js +0 -1
- package/dist/dashboard/assets/entry-CBg5zeu8.js.gz +0 -0
- package/dist/dashboard/assets/entry-CQBr6U9c.js.gz +0 -0
- package/dist/dashboard/assets/entry-CgdSAzEV.js +0 -4
- package/dist/dashboard/assets/entry-CgdSAzEV.js.gz +0 -0
- package/dist/dashboard/assets/fingerprint-pattern-BWuHtbW4.js.gz +0 -0
- package/dist/dashboard/assets/github-badge-BlptL8Uf.js.gz +0 -0
- package/dist/dashboard/assets/index-js6TJvdr.js.gz +0 -0
- package/dist/dashboard/assets/inline-DUWDSmDV.js.gz +0 -0
- package/dist/dashboard/assets/input-LVjgZq4f.js.gz +0 -0
- package/dist/dashboard/assets/layout-CPfxpsfy.js +0 -1
- package/dist/dashboard/assets/layout-CPfxpsfy.js.gz +0 -0
- package/dist/dashboard/assets/list-row-DupvLfDx.js.gz +0 -0
- package/dist/dashboard/assets/live-C4pEJUqI.js.gz +0 -0
- package/dist/dashboard/assets/model-bans-BRiuufkB.js.gz +0 -0
- package/dist/dashboard/assets/mutation-B4ROwk8L.js.gz +0 -0
- package/dist/dashboard/assets/numbers-YNcHvRY7.js +0 -2
- package/dist/dashboard/assets/numbers-YNcHvRY7.js.gz +0 -0
- package/dist/dashboard/assets/query-keys-gmjzC3RS.js.gz +0 -0
- package/dist/dashboard/assets/routing-C7tBT5o-.js.gz +0 -0
- package/dist/dashboard/assets/select-WUCXorXw.js.gz +0 -0
- package/dist/dashboard/assets/settings-C_zKl1GS.js.gz +0 -0
- package/dist/dashboard/assets/stack-_3O0F8Xn.js.gz +0 -0
- package/dist/dashboard/assets/state-BgTxHcJS.js.gz +0 -0
- package/dist/dashboard/assets/switch-DEjTtOWf.js.gz +0 -0
- package/dist/dashboard/assets/tokens-DDxMbSRL.js +0 -1
- package/dist/dashboard/assets/tokens-DDxMbSRL.js.gz +0 -0
- package/dist/dashboard/assets/use-presence-BSZEBx7U.js +0 -1
- package/dist/dashboard/assets/use-presence-BSZEBx7U.js.gz +0 -0
- package/dist/dashboard/assets/useMutation-BWSC0UAS.js.gz +0 -0
- package/scripts/cli/command/admin.ts +0 -65
- package/scripts/cli/command/doctor.ts +0 -257
- package/scripts/cli/gateway/drain-token.ts +0 -78
- package/scripts/cli/gateway/instance-lock.ts +0 -105
- package/scripts/cli/gateway/stop.ts +0 -51
- package/scripts/cli/support/bun.ts +0 -27
- package/scripts/cli/support/version.ts +0 -37
- /package/dist/dashboard/assets/{app-DCQZRmHY.js → app-BIt446yE.js} +0 -0
- /package/dist/dashboard/assets/{app-DCQZRmHY.js.gz → app-BIt446yE.js.gz} +0 -0
- /package/scripts/cli/{ui/prompt.ts → prompt.ts} +0 -0
|
@@ -102,8 +102,7 @@ export class DrizzleOAuthAccountStore implements OAuthAccountStore {
|
|
|
102
102
|
lastErrorAt: null,
|
|
103
103
|
lastRecoveredAt: null,
|
|
104
104
|
// A fresh login supplies a refresh token, so the account returns
|
|
105
|
-
// to normal OAuth refresh handling
|
|
106
|
-
// the previous (refresh-less) credential no longer applies.
|
|
105
|
+
// to normal OAuth refresh handling and is not a static token.
|
|
107
106
|
staticToken: false,
|
|
108
107
|
// Only what this login reported: a flow that learned no profile or
|
|
109
108
|
// region leaves the stored configuration alone rather than blanking it.
|
|
@@ -164,16 +163,13 @@ export class DrizzleOAuthAccountStore implements OAuthAccountStore {
|
|
|
164
163
|
* and device-code start/poll — driven by whichever `OAuthLoginClient` is
|
|
165
164
|
* registered for a given provider id.
|
|
166
165
|
*
|
|
167
|
-
*
|
|
168
|
-
*
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
174
|
-
* > every browser login needed the redirect pasted back by hand. The gateway
|
|
175
|
-
* > is a server process, but it is not *the* server the redirect arrives at;
|
|
176
|
-
* > `callback-listener.ts` is what makes those the same endpoint.
|
|
166
|
+
* The redirect URIs these clients advertise are loopback (`localhost:1455`,
|
|
167
|
+
* `127.0.0.1:54549`, …), which name the machine the *operator's browser* is on
|
|
168
|
+
* — not this process. Advertising one without binding the port leaves the code
|
|
169
|
+
* in the browser's address bar, so every browser login needs the redirect
|
|
170
|
+
* pasted back by hand. The gateway is a server process, but it is not *the*
|
|
171
|
+
* server the redirect arrives at; `callback-listener.ts` is what makes those
|
|
172
|
+
* the same endpoint.
|
|
177
173
|
*/
|
|
178
174
|
export interface PersistedOAuthAccount {
|
|
179
175
|
readonly accountId: string;
|
|
@@ -354,12 +354,12 @@ export function registerAccountQuotaTenantRoutes(
|
|
|
354
354
|
const accounts = rows.map((row) => {
|
|
355
355
|
const entry =
|
|
356
356
|
row.tenantId === null ? globalCache.get(row.id) : ownedCache.get(row.id);
|
|
357
|
-
// No background refresh is enqueued here
|
|
358
|
-
//
|
|
359
|
-
//
|
|
360
|
-
//
|
|
361
|
-
//
|
|
362
|
-
//
|
|
357
|
+
// No background refresh is enqueued here: enqueuing one for every
|
|
358
|
+
// account whose cached value had aged out means a single page open hits
|
|
359
|
+
// every provider's quota endpoint at once — and because the page also
|
|
360
|
+
// polls, the operator sees that fan-out as "the page refreshes
|
|
361
|
+
// everything when I open it". The periodic sweep owns freshness; this
|
|
362
|
+
// route renders what is cached.
|
|
363
363
|
//
|
|
364
364
|
// `refreshing` still counts accounts with no cached value, so the client
|
|
365
365
|
// can show them as pending while the sweep fills them.
|
|
@@ -154,7 +154,7 @@ export function toQuotaAccountHealth(row: {
|
|
|
154
154
|
cooldownUntil: row.cooldownUntil ? row.cooldownUntil.toISOString() : null,
|
|
155
155
|
// Only deadlines still in force are returned: the sweep prunes the column
|
|
156
156
|
// on a timer, so a raw read can carry an entry that has already elapsed and
|
|
157
|
-
// would render as a cooldown that
|
|
157
|
+
// would render as a cooldown that is not in force.
|
|
158
158
|
modelCooldowns: liveModelCooldowns(row.modelCooldowns),
|
|
159
159
|
};
|
|
160
160
|
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remote-support tunnel contracts.
|
|
3
|
+
*
|
|
4
|
+
* A tunnel publishes the loopback gateway to a public or tailnet address. This
|
|
5
|
+
* module owns the vocabulary (which tool, which states, what a status reports)
|
|
6
|
+
* and is deliberately free of Elysia and Node imports so the dashboard can
|
|
7
|
+
* import the same shape without dragging a server runtime into the browser
|
|
8
|
+
* bundle.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/** The two supported publishers. Both must already be installed. */
|
|
12
|
+
export const REMOTE_SUPPORT_TOOLS = ["cloudflared", "tailscale"] as const;
|
|
13
|
+
|
|
14
|
+
export type RemoteSupportTool = (typeof REMOTE_SUPPORT_TOOLS)[number];
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Why a tunnel cannot start. `not_installed` is the common one: this product
|
|
18
|
+
* never installs third-party binaries, so a missing `cloudflared` or
|
|
19
|
+
* `tailscale` is reported for the operator to install, never downloaded.
|
|
20
|
+
*/
|
|
21
|
+
export type RemoteSupportBlocker =
|
|
22
|
+
| "not_installed"
|
|
23
|
+
| "unsupported_platform"
|
|
24
|
+
| "launch_failed";
|
|
25
|
+
|
|
26
|
+
export interface RemoteSupportStatus {
|
|
27
|
+
/** Which publisher is up, or `null` when neither is. */
|
|
28
|
+
readonly active: RemoteSupportTool | null;
|
|
29
|
+
/** The published base URL, present once the publisher reported one. */
|
|
30
|
+
readonly url: string | null;
|
|
31
|
+
/** Set when a start was refused, with the operator-facing reason. */
|
|
32
|
+
readonly blocker: RemoteSupportBlocker | null;
|
|
33
|
+
/** Operator-facing detail for `blocker`, e.g. the install hint. */
|
|
34
|
+
readonly message: string | null;
|
|
35
|
+
/** Local port the tunnel forwards to. */
|
|
36
|
+
readonly port: number;
|
|
37
|
+
/** Which publishers are installed on this host. Cached so the UI doesn't poll. */
|
|
38
|
+
readonly installed: Readonly<Record<RemoteSupportTool, boolean>>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface RemoteSupportStartRequest {
|
|
42
|
+
readonly tool: RemoteSupportTool;
|
|
43
|
+
/** Defaults to the gateway's own port when omitted. */
|
|
44
|
+
readonly port?: number;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface RemoteSupportStartResponse extends RemoteSupportStatus {
|
|
48
|
+
readonly started: boolean;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Human-readable install guidance, keyed by tool. Never an installer. */
|
|
52
|
+
export const REMOTE_SUPPORT_INSTALL_HINT: Readonly<Record<RemoteSupportTool, string>> = {
|
|
53
|
+
cloudflared:
|
|
54
|
+
"cloudflared is not installed. Install it yourself (winget install Cloudflare.cloudflared) and try again.",
|
|
55
|
+
tailscale:
|
|
56
|
+
"tailscale is not installed. Install it yourself (winget install Tailscale.Tailscale), run `tailscale up` once, then try again.",
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export function isRemoteSupportTool(value: unknown): value is RemoteSupportTool {
|
|
60
|
+
return typeof value === "string" && (REMOTE_SUPPORT_TOOLS as readonly string[]).includes(value);
|
|
61
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remote-support HTTP surface.
|
|
3
|
+
*
|
|
4
|
+
* Three routes over one external launcher script: read the current tunnel,
|
|
5
|
+
* start one, stop one. Every mutating route needs `dashboard:write` because it
|
|
6
|
+
* runs a process on the host running the gateway.
|
|
7
|
+
*
|
|
8
|
+
* The start response never contains a secret and never performs an install; it
|
|
9
|
+
* reports `not_installed` with guidance when the publisher is missing.
|
|
10
|
+
*/
|
|
11
|
+
import { Elysia, t } from "elysia";
|
|
12
|
+
import type { ConsoleAccessResolver } from "../auth/access";
|
|
13
|
+
import { consoleErrorHandler, requireTenantScope } from "../shared/errors";
|
|
14
|
+
import { REMOTE_SUPPORT_TOOLS } from "./contracts";
|
|
15
|
+
import type { RemoteSupportService } from "./service";
|
|
16
|
+
|
|
17
|
+
export interface RemoteSupportRoutesConfig {
|
|
18
|
+
readonly accessResolver: ConsoleAccessResolver;
|
|
19
|
+
readonly service: RemoteSupportService;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const toolSchema = t.Union([
|
|
23
|
+
t.Literal(REMOTE_SUPPORT_TOOLS[0]),
|
|
24
|
+
t.Literal(REMOTE_SUPPORT_TOOLS[1]),
|
|
25
|
+
]);
|
|
26
|
+
|
|
27
|
+
const startBody = t.Object({
|
|
28
|
+
tool: toolSchema,
|
|
29
|
+
port: t.Optional(t.Number()),
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const stopBody = t.Object({
|
|
33
|
+
tool: t.Optional(toolSchema),
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The return type is inferred rather than annotated as `Elysia`: `.error()`
|
|
38
|
+
* widens the instance type with its handler's response envelope, so the
|
|
39
|
+
* annotated base type no longer matches the chained result.
|
|
40
|
+
*/
|
|
41
|
+
export function createRemoteSupportRoutes(config: RemoteSupportRoutesConfig) {
|
|
42
|
+
return new Elysia()
|
|
43
|
+
.error(consoleErrorHandler("Remote support failed"))
|
|
44
|
+
.get("/remote-support/status", async ({ request }) => {
|
|
45
|
+
requireTenantScope(config.accessResolver(request), "dashboard:read");
|
|
46
|
+
return await config.service.status();
|
|
47
|
+
})
|
|
48
|
+
.post(
|
|
49
|
+
"/remote-support/start",
|
|
50
|
+
{ body: startBody },
|
|
51
|
+
async ({ request, body }) => {
|
|
52
|
+
requireTenantScope(config.accessResolver(request), "dashboard:write");
|
|
53
|
+
const status = await config.service.start(body.tool, body.port);
|
|
54
|
+
return { started: status.active !== null, ...status };
|
|
55
|
+
},
|
|
56
|
+
)
|
|
57
|
+
.post(
|
|
58
|
+
"/remote-support/stop",
|
|
59
|
+
{ body: stopBody },
|
|
60
|
+
async ({ request, body }) => {
|
|
61
|
+
requireTenantScope(config.accessResolver(request), "dashboard:write");
|
|
62
|
+
return await config.service.stop(body.tool);
|
|
63
|
+
},
|
|
64
|
+
);
|
|
65
|
+
}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remote-support tunnel service.
|
|
3
|
+
*
|
|
4
|
+
* The tunnel is launched by an external script rather than by spawning
|
|
5
|
+
* `cloudflared`/`tailscale` directly, for one reason that matters: the script
|
|
6
|
+
* hands the publisher to the OS session, so the tunnel is not a child of this
|
|
7
|
+
* gateway. A gateway restart, a hot reload, or a Ctrl-C in the owning terminal
|
|
8
|
+
* therefore cannot kill an operator's live remote-support session.
|
|
9
|
+
*
|
|
10
|
+
* This service never installs anything. A missing binary is reported as a
|
|
11
|
+
* blocker with install guidance; downloading a third-party agent on the
|
|
12
|
+
* operator's behalf is out of scope.
|
|
13
|
+
*/
|
|
14
|
+
import { spawn } from "node:child_process";
|
|
15
|
+
import { existsSync } from "node:fs";
|
|
16
|
+
import { join } from "node:path";
|
|
17
|
+
import { PACKAGE_ROOT } from "../../config/package-paths";
|
|
18
|
+
import {
|
|
19
|
+
REMOTE_SUPPORT_INSTALL_HINT,
|
|
20
|
+
type RemoteSupportBlocker,
|
|
21
|
+
type RemoteSupportStatus,
|
|
22
|
+
type RemoteSupportTool,
|
|
23
|
+
} from "./contracts";
|
|
24
|
+
|
|
25
|
+
/** How long the launcher may take to report a URL before we stop waiting. */
|
|
26
|
+
const LAUNCH_TIMEOUT_MS = 40_000;
|
|
27
|
+
|
|
28
|
+
/** Cached install detection so we don't spawn `where` on every status poll. */
|
|
29
|
+
const installedCache = new Map<RemoteSupportTool, boolean>();
|
|
30
|
+
|
|
31
|
+
export interface RemoteSupportServiceConfig {
|
|
32
|
+
/** Defaults to the gateway's configured port. */
|
|
33
|
+
readonly port: number;
|
|
34
|
+
/** Overridable for tests; defaults to the package root. */
|
|
35
|
+
readonly scriptDir?: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface CommandResult {
|
|
39
|
+
readonly status: number;
|
|
40
|
+
readonly stdout: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function scriptPath(config: RemoteSupportServiceConfig, name: string): string {
|
|
44
|
+
const dir = config.scriptDir ?? join(PACKAGE_ROOT, "scripts");
|
|
45
|
+
return join(dir, name);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function runScript(
|
|
49
|
+
config: RemoteSupportServiceConfig,
|
|
50
|
+
args: readonly string[],
|
|
51
|
+
): Promise<CommandResult> {
|
|
52
|
+
const { promise, resolve, reject } = Promise.withResolvers<CommandResult>();
|
|
53
|
+
const isWindows = process.platform === "win32";
|
|
54
|
+
// Windows cannot spawn a `.cmd` directly — `spawn` rejects it with EINVAL
|
|
55
|
+
// because a batch file is not an executable image. It has to be handed to
|
|
56
|
+
// `cmd.exe /c`, which is also what lets the batch call `exit /b 3` and have
|
|
57
|
+
// that code reach us as the child's status.
|
|
58
|
+
const command = isWindows ? "cmd.exe" : "powershell";
|
|
59
|
+
const fileName = isWindows ? "remote-support.cmd" : "remote-support.ps1";
|
|
60
|
+
const file = scriptPath(config, fileName);
|
|
61
|
+
const spawnArgs = isWindows
|
|
62
|
+
? ["/d", "/s", "/c", file, ...args]
|
|
63
|
+
: ["-NoProfile", "-ExecutionPolicy", "Bypass", "-File", file, ...args];
|
|
64
|
+
const child = spawn(command, spawnArgs, {
|
|
65
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
66
|
+
windowsHide: true,
|
|
67
|
+
});
|
|
68
|
+
let stdout = "";
|
|
69
|
+
let stderr = "";
|
|
70
|
+
child.stdout.setEncoding("utf8");
|
|
71
|
+
child.stderr.setEncoding("utf8");
|
|
72
|
+
child.stdout.on("data", (chunk: string) => {
|
|
73
|
+
stdout += chunk;
|
|
74
|
+
});
|
|
75
|
+
child.stderr.on("data", (chunk: string) => {
|
|
76
|
+
stderr += chunk;
|
|
77
|
+
});
|
|
78
|
+
const timer = setTimeout(() => child.kill(), LAUNCH_TIMEOUT_MS);
|
|
79
|
+
child.on("error", reject);
|
|
80
|
+
child.on("close", (status) => {
|
|
81
|
+
clearTimeout(timer);
|
|
82
|
+
resolve({ status: status ?? 1, stdout: `${stdout}${stderr}`.trim() });
|
|
83
|
+
});
|
|
84
|
+
return promise;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* `where`/`which` lookup, mirroring what the script does itself so the route
|
|
89
|
+
* can answer `not_installed` with a clear message instead of a generic failure.
|
|
90
|
+
*/
|
|
91
|
+
function toolAvailable(tool: RemoteSupportTool): Promise<boolean> {
|
|
92
|
+
const cached = installedCache.get(tool);
|
|
93
|
+
if (cached !== undefined) return Promise.resolve(cached);
|
|
94
|
+
const { promise, resolve } = Promise.withResolvers<boolean>();
|
|
95
|
+
const command = process.platform === "win32" ? "where" : "which";
|
|
96
|
+
const child = spawn(command, [tool], { stdio: "ignore", windowsHide: true });
|
|
97
|
+
child.on("error", () => {
|
|
98
|
+
installedCache.set(tool, false);
|
|
99
|
+
resolve(false);
|
|
100
|
+
});
|
|
101
|
+
child.on("close", (status) => {
|
|
102
|
+
const ok = status === 0;
|
|
103
|
+
installedCache.set(tool, ok);
|
|
104
|
+
resolve(ok);
|
|
105
|
+
});
|
|
106
|
+
return promise;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export interface RemoteSupportService {
|
|
110
|
+
status(): Promise<RemoteSupportStatus>;
|
|
111
|
+
start(tool: RemoteSupportTool, port?: number): Promise<RemoteSupportStatus>;
|
|
112
|
+
stop(tool?: RemoteSupportTool): Promise<RemoteSupportStatus>;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function createRemoteSupportService(config: RemoteSupportServiceConfig): RemoteSupportService {
|
|
116
|
+
const port = config.port;
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Install state for both publishers, cached per process.
|
|
120
|
+
*
|
|
121
|
+
* Every status carries it so the card can show "not installed" without the
|
|
122
|
+
* browser probing on its own — but the probe is `where`/`which`, which is a
|
|
123
|
+
* process spawn, so it is memoised here rather than run on each poll.
|
|
124
|
+
*/
|
|
125
|
+
async function detectInstalled(): Promise<Readonly<Record<RemoteSupportTool, boolean>>> {
|
|
126
|
+
return {
|
|
127
|
+
cloudflared: await toolAvailable("cloudflared"),
|
|
128
|
+
tailscale: await toolAvailable("tailscale"),
|
|
129
|
+
} as const;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async function readStatus(blocker: RemoteSupportBlocker | null = null, message: string | null = null): Promise<RemoteSupportStatus> {
|
|
133
|
+
const result = await runScript(config, ["status"]);
|
|
134
|
+
const text = result.stdout;
|
|
135
|
+
const active: RemoteSupportTool | null = text.includes("cloudflared running")
|
|
136
|
+
? "cloudflared"
|
|
137
|
+
: text.includes("tailscale running")
|
|
138
|
+
? "tailscale"
|
|
139
|
+
: null;
|
|
140
|
+
let url: string | null = null;
|
|
141
|
+
if (active !== null) {
|
|
142
|
+
const urlResult = await runScript(config, ["url"]);
|
|
143
|
+
const candidate = urlResult.stdout.trim().split(/\s+/).find((line) => line.startsWith("https://"));
|
|
144
|
+
url = candidate ?? null;
|
|
145
|
+
}
|
|
146
|
+
const installed = await detectInstalled();
|
|
147
|
+
return { active, url, blocker, message, port, installed };
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return {
|
|
151
|
+
async status() {
|
|
152
|
+
return await readStatus();
|
|
153
|
+
},
|
|
154
|
+
|
|
155
|
+
async start(tool, requestedPort) {
|
|
156
|
+
if (process.platform !== "win32") {
|
|
157
|
+
return {
|
|
158
|
+
active: null,
|
|
159
|
+
url: null,
|
|
160
|
+
blocker: "unsupported_platform",
|
|
161
|
+
message: "Remote support is currently scripted for Windows hosts only.",
|
|
162
|
+
port,
|
|
163
|
+
installed: { cloudflared: false, tailscale: false },
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
const launcher = scriptPath(config, "remote-support.cmd");
|
|
167
|
+
if (!existsSync(launcher)) {
|
|
168
|
+
return {
|
|
169
|
+
active: null,
|
|
170
|
+
url: null,
|
|
171
|
+
blocker: "launch_failed",
|
|
172
|
+
message: `Launcher not found: ${launcher}`,
|
|
173
|
+
port,
|
|
174
|
+
installed: await detectInstalled(),
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
if (!(await toolAvailable(tool))) {
|
|
178
|
+
return {
|
|
179
|
+
active: null,
|
|
180
|
+
url: null,
|
|
181
|
+
blocker: "not_installed",
|
|
182
|
+
message: REMOTE_SUPPORT_INSTALL_HINT[tool],
|
|
183
|
+
port,
|
|
184
|
+
installed: await detectInstalled(),
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
const target = requestedPort ?? port;
|
|
188
|
+
const result = await runScript(config, ["start", tool, String(target)]);
|
|
189
|
+
if (result.status !== 0 && !result.stdout.includes("started")) {
|
|
190
|
+
return {
|
|
191
|
+
active: null,
|
|
192
|
+
url: null,
|
|
193
|
+
blocker: "launch_failed",
|
|
194
|
+
message: result.stdout || `Failed to start ${tool}.`,
|
|
195
|
+
port,
|
|
196
|
+
installed: await detectInstalled(),
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
return await readStatus();
|
|
200
|
+
},
|
|
201
|
+
|
|
202
|
+
async stop(tool) {
|
|
203
|
+
await runScript(config, tool === undefined ? ["stop"] : ["stop", tool]);
|
|
204
|
+
return await readStatus();
|
|
205
|
+
},
|
|
206
|
+
};
|
|
207
|
+
}
|
|
@@ -18,9 +18,6 @@
|
|
|
18
18
|
* only". A path test can, and it lives inside `gzipResponse` — the one place
|
|
19
19
|
* that runs on every request.
|
|
20
20
|
*
|
|
21
|
-
* Consequence: `/v1/models` was previously compressed as a side effect of
|
|
22
|
-
* that `hoc` propagation. It deliberately is not now.
|
|
23
|
-
*
|
|
24
21
|
* ## Why this is scoped to the console, not the proxy
|
|
25
22
|
*
|
|
26
23
|
* `/v1/*` is deliberately excluded, for two independent reasons:
|
|
@@ -51,8 +51,9 @@ export function classifyPoolConnectError(
|
|
|
51
51
|
error: unknown,
|
|
52
52
|
latencyMs: number,
|
|
53
53
|
): HealthCheckResult | undefined {
|
|
54
|
-
// The agent throws a typed refusal carrying the status, so this
|
|
55
|
-
// re-
|
|
54
|
+
// The agent throws a typed refusal carrying the status, so this reads the
|
|
55
|
+
// status rather than re-parsing message text (which a wording change would
|
|
56
|
+
// silently break).
|
|
56
57
|
if (!(error instanceof ProxyConnectRefusalError)) return undefined;
|
|
57
58
|
if (error.statusCode !== 402 && error.statusCode !== 407) return undefined;
|
|
58
59
|
return classifyPoolProbeResponse(poolId, { ok: false, status: error.statusCode }, latencyMs);
|