@elizaos/plugin-elizacloud 2.0.0-alpha.7 → 2.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +220 -0
- package/auto-enable.ts +17 -0
- package/dist/browser/index.browser.js +2 -21
- package/dist/browser/index.browser.js.map +5 -37
- package/dist/cjs/index.d.ts +2 -2
- package/dist/cjs/index.node.cjs +12173 -2271
- package/dist/cjs/index.node.js.map +135 -27
- package/dist/cloud/auth.d.ts +19 -0
- package/dist/cloud/auth.d.ts.map +1 -0
- package/dist/cloud/auth.js +330 -0
- package/dist/cloud/auth.js.map +12 -0
- package/dist/cloud/backup.d.ts +18 -0
- package/dist/cloud/backup.d.ts.map +1 -0
- package/dist/cloud/backup.js +63 -0
- package/dist/cloud/backup.js.map +10 -0
- package/dist/cloud/base-url.d.ts +3 -0
- package/dist/cloud/base-url.d.ts.map +1 -0
- package/dist/cloud/base-url.js +77 -0
- package/dist/cloud/base-url.js.map +10 -0
- package/dist/cloud/bridge-client.d.ts +126 -0
- package/dist/cloud/bridge-client.d.ts.map +1 -0
- package/dist/cloud/bridge-client.js +432 -0
- package/dist/cloud/bridge-client.js.map +11 -0
- package/dist/cloud/cloud-api-key.d.ts +26 -0
- package/dist/cloud/cloud-api-key.d.ts.map +1 -0
- package/dist/cloud/cloud-api-key.js +60 -0
- package/dist/cloud/cloud-api-key.js.map +10 -0
- package/dist/cloud/cloud-manager.d.ts +33 -0
- package/dist/cloud/cloud-manager.d.ts.map +1 -0
- package/dist/cloud/cloud-manager.js +853 -0
- package/dist/cloud/cloud-manager.js.map +16 -0
- package/dist/cloud/cloud-proxy.d.ts +20 -0
- package/dist/cloud/cloud-proxy.d.ts.map +1 -0
- package/dist/cloud/cloud-proxy.js +54 -0
- package/dist/cloud/cloud-proxy.js.map +10 -0
- package/dist/cloud/cloud-wallet.d.ts +94 -0
- package/dist/cloud/cloud-wallet.d.ts.map +1 -0
- package/dist/cloud/cloud-wallet.js +5195 -0
- package/dist/cloud/cloud-wallet.js.map +92 -0
- package/dist/cloud/index.d.ts +9 -0
- package/dist/cloud/index.d.ts.map +1 -0
- package/dist/cloud/index.js +30 -0
- package/dist/cloud/index.js.map +9 -0
- package/dist/cloud/reconnect.d.ts +26 -0
- package/dist/cloud/reconnect.d.ts.map +1 -0
- package/dist/cloud/reconnect.js +104 -0
- package/dist/cloud/reconnect.js.map +10 -0
- package/dist/cloud/validate-url.d.ts +2 -0
- package/dist/cloud/validate-url.d.ts.map +1 -0
- package/dist/cloud/validate-url.js +174 -0
- package/dist/cloud/validate-url.js.map +10 -0
- package/dist/cloud-providers/cloud-status.d.ts.map +1 -1
- package/dist/cloud-providers/cloud-status.js +78 -0
- package/dist/cloud-providers/cloud-status.js.map +10 -0
- package/dist/cloud-providers/container-health.d.ts.map +1 -1
- package/dist/cloud-providers/container-health.js +74 -0
- package/dist/cloud-providers/container-health.js.map +10 -0
- package/dist/cloud-providers/credit-balance.d.ts.map +1 -1
- package/dist/cloud-providers/credit-balance.js +85 -0
- package/dist/cloud-providers/credit-balance.js.map +10 -0
- package/dist/cloud-providers/index.d.ts.map +1 -1
- package/dist/cloud-providers/index.js +24 -0
- package/dist/cloud-providers/index.js.map +9 -0
- package/dist/cloud-providers/model-registry.d.ts.map +1 -1
- package/dist/cloud-providers/model-registry.js +71 -0
- package/dist/cloud-providers/model-registry.js.map +10 -0
- package/dist/index.browser.d.ts +4 -2
- package/dist/index.browser.d.ts.map +1 -1
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12851 -0
- package/dist/index.js.map +145 -0
- package/dist/index.node.d.ts +15 -2
- package/dist/index.node.d.ts.map +1 -1
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +169 -0
- package/dist/init.js.map +12 -0
- package/dist/lib/cloud-connection.d.ts +78 -0
- package/dist/lib/cloud-connection.d.ts.map +1 -0
- package/dist/lib/cloud-connection.js +731 -0
- package/dist/lib/cloud-connection.js.map +14 -0
- package/dist/lib/cloud-secrets.d.ts +23 -0
- package/dist/lib/cloud-secrets.d.ts.map +1 -0
- package/dist/lib/cloud-secrets.js +64 -0
- package/dist/lib/cloud-secrets.js.map +10 -0
- package/dist/lib/config-env.d.ts +5 -0
- package/dist/lib/config-env.d.ts.map +1 -0
- package/dist/lib/config-env.js +191 -0
- package/dist/lib/config-env.js.map +11 -0
- package/dist/lib/config-like.d.ts +40 -0
- package/dist/lib/config-like.d.ts.map +1 -0
- package/dist/lib/config-like.js +103 -0
- package/dist/lib/config-like.js.map +10 -0
- package/dist/lib/credential-type-map.d.ts +53 -0
- package/dist/lib/credential-type-map.d.ts.map +1 -0
- package/dist/lib/credential-type-map.js +88 -0
- package/dist/lib/credential-type-map.js.map +10 -0
- package/dist/lib/feature-flags.d.ts +2 -0
- package/dist/lib/feature-flags.d.ts.map +1 -0
- package/dist/lib/feature-flags.js +40 -0
- package/dist/lib/feature-flags.js.map +10 -0
- package/dist/lib/http.d.ts +22 -0
- package/dist/lib/http.d.ts.map +1 -0
- package/dist/lib/http.js +107 -0
- package/dist/lib/http.js.map +10 -0
- package/dist/lib/server-cloud-tts.d.ts +34 -0
- package/dist/lib/server-cloud-tts.d.ts.map +1 -0
- package/dist/lib/server-cloud-tts.js +549 -0
- package/dist/lib/server-cloud-tts.js.map +13 -0
- package/dist/lib/state-paths.d.ts +4 -0
- package/dist/lib/state-paths.d.ts.map +1 -0
- package/dist/lib/state-paths.js +52 -0
- package/dist/lib/state-paths.js.map +10 -0
- package/dist/lib/tts-debug.d.ts +4 -0
- package/dist/lib/tts-debug.d.ts.map +1 -0
- package/dist/lib/tts-debug.js +57 -0
- package/dist/lib/tts-debug.js.map +10 -0
- package/dist/models/embeddings.d.ts.map +1 -1
- package/dist/models/embeddings.js +319 -0
- package/dist/models/embeddings.js.map +13 -0
- package/dist/models/image.d.ts.map +1 -1
- package/dist/models/image.js +374 -0
- package/dist/models/image.js.map +14 -0
- package/dist/models/index.d.ts +1 -2
- package/dist/models/index.d.ts.map +1 -1
- package/dist/models/index.js +1386 -0
- package/dist/models/index.js.map +20 -0
- package/dist/models/research.d.ts.map +1 -1
- package/dist/models/research.js +324 -0
- package/dist/models/research.js.map +13 -0
- package/dist/models/speech.d.ts.map +1 -1
- package/dist/models/speech.js +273 -0
- package/dist/models/speech.js.map +13 -0
- package/dist/models/text.d.ts +5 -2
- package/dist/models/text.d.ts.map +1 -1
- package/dist/models/text.js +803 -0
- package/dist/models/text.js.map +15 -0
- package/dist/models/tokenization.d.ts.map +1 -1
- package/dist/models/tokenization.js +65 -0
- package/dist/models/tokenization.js.map +10 -0
- package/dist/models/transcription.d.ts.map +1 -1
- package/dist/models/transcription.js +283 -0
- package/dist/models/transcription.js.map +13 -0
- package/dist/node/index.d.ts +2 -2
- package/dist/node/index.node.js +12171 -2266
- package/dist/node/index.node.js.map +135 -27
- package/dist/onboarding.d.ts +35 -0
- package/dist/onboarding.d.ts.map +1 -0
- package/dist/onboarding.js +883 -0
- package/dist/onboarding.js.map +14 -0
- package/dist/plugin.d.ts +20 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +7611 -0
- package/dist/plugin.js.map +104 -0
- package/dist/providers/openai.d.ts.map +1 -1
- package/dist/providers/openai.js +127 -0
- package/dist/providers/openai.js.map +11 -0
- package/dist/register-routes.d.ts +2 -0
- package/dist/register-routes.d.ts.map +1 -0
- package/dist/register-routes.js +7612 -0
- package/dist/register-routes.js.map +105 -0
- package/dist/routes/cloud-billing-routes.d.ts +9 -0
- package/dist/routes/cloud-billing-routes.d.ts.map +1 -0
- package/dist/routes/cloud-billing-routes.js +807 -0
- package/dist/routes/cloud-billing-routes.js.map +14 -0
- package/dist/routes/cloud-compat-routes.d.ts +10 -0
- package/dist/routes/cloud-compat-routes.d.ts.map +1 -0
- package/dist/routes/cloud-compat-routes.js +538 -0
- package/dist/routes/cloud-compat-routes.js.map +14 -0
- package/dist/routes/cloud-features-routes.d.ts +9 -0
- package/dist/routes/cloud-features-routes.d.ts.map +1 -0
- package/dist/routes/cloud-features-routes.js +124 -0
- package/dist/routes/cloud-features-routes.js.map +11 -0
- package/dist/routes/cloud-provisioning.d.ts +14 -0
- package/dist/routes/cloud-provisioning.d.ts.map +1 -0
- package/dist/routes/cloud-provisioning.js +37 -0
- package/dist/routes/cloud-provisioning.js.map +10 -0
- package/dist/routes/cloud-relay-routes.d.ts +22 -0
- package/dist/routes/cloud-relay-routes.d.ts.map +1 -0
- package/dist/routes/cloud-relay-routes.js +60 -0
- package/dist/routes/cloud-relay-routes.js.map +10 -0
- package/dist/routes/cloud-routes-autonomous.d.ts +83 -0
- package/dist/routes/cloud-routes-autonomous.d.ts.map +1 -0
- package/dist/routes/cloud-routes-autonomous.js +6134 -0
- package/dist/routes/cloud-routes-autonomous.js.map +97 -0
- package/dist/routes/cloud-routes.d.ts +35 -0
- package/dist/routes/cloud-routes.d.ts.map +1 -0
- package/dist/routes/cloud-routes.js +6888 -0
- package/dist/routes/cloud-routes.js.map +100 -0
- package/dist/routes/cloud-status-routes-autonomous.d.ts +15 -0
- package/dist/routes/cloud-status-routes-autonomous.d.ts.map +1 -0
- package/dist/routes/cloud-status-routes-autonomous.js +396 -0
- package/dist/routes/cloud-status-routes-autonomous.js.map +13 -0
- package/dist/routes/cloud-status-routes.d.ts +4 -0
- package/dist/routes/cloud-status-routes.d.ts.map +1 -0
- package/dist/routes/cloud-status-routes.js +771 -0
- package/dist/routes/cloud-status-routes.js.map +15 -0
- package/dist/services/cloud-auth.d.ts +140 -5
- package/dist/services/cloud-auth.d.ts.map +1 -1
- package/dist/services/cloud-auth.js +363 -0
- package/dist/services/cloud-auth.js.map +12 -0
- package/dist/services/cloud-backup.d.ts.map +1 -1
- package/dist/services/cloud-backup.js +176 -0
- package/dist/services/cloud-backup.js.map +11 -0
- package/dist/services/cloud-bootstrap.d.ts +38 -0
- package/dist/services/cloud-bootstrap.d.ts.map +1 -0
- package/dist/services/cloud-bootstrap.js +84 -0
- package/dist/services/cloud-bootstrap.js.map +10 -0
- package/dist/services/cloud-bridge.d.ts +1 -1
- package/dist/services/cloud-bridge.d.ts.map +1 -1
- package/dist/services/cloud-bridge.js +308 -0
- package/dist/services/cloud-bridge.js.map +11 -0
- package/dist/services/cloud-container.d.ts.map +1 -1
- package/dist/services/cloud-container.js +241 -0
- package/dist/services/cloud-container.js.map +11 -0
- package/dist/services/cloud-credential-provider.d.ts +55 -0
- package/dist/services/cloud-credential-provider.d.ts.map +1 -0
- package/dist/services/cloud-credential-provider.js +190 -0
- package/dist/services/cloud-credential-provider.js.map +11 -0
- package/dist/services/cloud-managed-gateway-relay.d.ts +38 -0
- package/dist/services/cloud-managed-gateway-relay.d.ts.map +1 -0
- package/dist/services/cloud-managed-gateway-relay.js +479 -0
- package/dist/services/cloud-managed-gateway-relay.js.map +10 -0
- package/dist/services/cloud-model-registry.d.ts.map +1 -1
- package/dist/services/cloud-model-registry.js +175 -0
- package/dist/services/cloud-model-registry.js.map +10 -0
- package/dist/services/index.d.ts +3 -1
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +29 -0
- package/dist/services/index.js.map +9 -0
- package/dist/types/cloud.d.ts +41 -19
- package/dist/types/cloud.d.ts.map +1 -1
- package/dist/types/cloud.js +52 -0
- package/dist/types/cloud.js.map +10 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +24 -0
- package/dist/types/index.js.map +9 -0
- package/dist/utils/cloud-api.d.ts +2 -27
- package/dist/utils/cloud-api.d.ts.map +1 -1
- package/dist/utils/cloud-api.js +33 -0
- package/dist/utils/cloud-api.js.map +10 -0
- package/dist/utils/cloud-sdk/client.d.ts +133 -0
- package/dist/utils/cloud-sdk/client.d.ts.map +1 -0
- package/dist/utils/cloud-sdk/client.js +3561 -0
- package/dist/utils/cloud-sdk/client.js.map +13 -0
- package/dist/utils/cloud-sdk/http.d.ts +37 -0
- package/dist/utils/cloud-sdk/http.d.ts.map +1 -0
- package/dist/utils/cloud-sdk/http.js +237 -0
- package/dist/utils/cloud-sdk/http.js.map +11 -0
- package/dist/utils/cloud-sdk/index.d.ts +6 -0
- package/dist/utils/cloud-sdk/index.d.ts.map +1 -0
- package/dist/utils/cloud-sdk/index.js +29 -0
- package/dist/utils/cloud-sdk/index.js.map +9 -0
- package/dist/utils/cloud-sdk/public-routes.d.ts +5377 -0
- package/dist/utils/cloud-sdk/public-routes.d.ts.map +1 -0
- package/dist/utils/cloud-sdk/public-routes.js +2950 -0
- package/dist/utils/cloud-sdk/public-routes.js.map +10 -0
- package/dist/utils/cloud-sdk/types.cloud-api.d.ts +101 -0
- package/dist/utils/cloud-sdk/types.cloud-api.d.ts.map +1 -0
- package/dist/utils/cloud-sdk/types.cloud-api.js +2 -0
- package/dist/utils/cloud-sdk/types.cloud-api.js.map +9 -0
- package/dist/utils/cloud-sdk/types.d.ts +655 -0
- package/dist/utils/cloud-sdk/types.d.ts.map +1 -0
- package/dist/utils/cloud-sdk/types.js +29 -0
- package/dist/utils/cloud-sdk/types.js.map +10 -0
- package/dist/utils/config.d.ts +7 -3
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +137 -0
- package/dist/utils/config.js.map +10 -0
- package/dist/utils/events.d.ts.map +1 -1
- package/dist/utils/events.js +43 -0
- package/dist/utils/events.js.map +10 -0
- package/dist/utils/helpers.d.ts.map +1 -1
- package/dist/utils/helpers.js +103 -0
- package/dist/utils/helpers.js.map +10 -0
- package/dist/utils/responses-output.d.ts +13 -0
- package/dist/utils/responses-output.d.ts.map +1 -0
- package/dist/utils/responses-output.js +102 -0
- package/dist/utils/responses-output.js.map +10 -0
- package/dist/utils/sdk-client.d.ts +5 -0
- package/dist/utils/sdk-client.d.ts.map +1 -0
- package/dist/utils/sdk-client.js +144 -0
- package/dist/utils/sdk-client.js.map +11 -0
- package/package.json +108 -19
- package/src/cloud/auth.ts +175 -0
- package/src/cloud/backup.ts +46 -0
- package/src/cloud/base-url.ts +62 -0
- package/src/cloud/bridge-client.ts +602 -0
- package/src/cloud/cloud-api-key.ts +80 -0
- package/src/cloud/cloud-manager.ts +163 -0
- package/src/cloud/cloud-proxy.ts +52 -0
- package/src/cloud/cloud-wallet.ts +341 -0
- package/src/cloud/index.ts +28 -0
- package/src/cloud/reconnect.ts +111 -0
- package/src/cloud/validate-url.ts +181 -0
- package/src/cloud-providers/cloud-status.ts +75 -0
- package/src/cloud-providers/container-health.ts +68 -0
- package/src/cloud-providers/credit-balance.ts +70 -0
- package/src/cloud-providers/index.ts +3 -0
- package/src/cloud-providers/model-registry.ts +74 -0
- package/src/index.browser.ts +10 -0
- package/src/index.node.ts +39 -0
- package/src/index.ts +347 -0
- package/src/init.ts +39 -0
- package/src/lib/cloud-connection.ts +663 -0
- package/src/lib/cloud-secrets.ts +58 -0
- package/src/lib/config-env.ts +168 -0
- package/src/lib/config-like.ts +149 -0
- package/src/lib/credential-type-map.ts +130 -0
- package/src/lib/feature-flags.ts +26 -0
- package/src/lib/http.ts +139 -0
- package/src/lib/server-cloud-tts.ts +609 -0
- package/src/lib/state-paths.ts +28 -0
- package/src/lib/tts-debug.ts +34 -0
- package/src/models/embeddings.ts +234 -0
- package/src/models/image.ts +219 -0
- package/src/models/index.ts +16 -0
- package/src/models/research.ts +265 -0
- package/src/models/speech.ts +78 -0
- package/src/models/text.ts +899 -0
- package/src/models/tokenization.ts +67 -0
- package/src/models/transcription.ts +97 -0
- package/src/onboarding.ts +396 -0
- package/src/plugin.ts +243 -0
- package/src/providers/openai.ts +16 -0
- package/src/register-routes.ts +6 -0
- package/src/routes/cloud-billing-routes.ts +754 -0
- package/src/routes/cloud-compat-routes.ts +314 -0
- package/src/routes/cloud-features-routes.ts +57 -0
- package/src/routes/cloud-provisioning.ts +37 -0
- package/src/routes/cloud-relay-routes.ts +89 -0
- package/src/routes/cloud-routes-autonomous.ts +996 -0
- package/src/routes/cloud-routes.ts +576 -0
- package/src/routes/cloud-status-routes-autonomous.ts +234 -0
- package/src/routes/cloud-status-routes.ts +73 -0
- package/src/services/cloud-auth.ts +567 -0
- package/src/services/cloud-backup.ts +208 -0
- package/src/services/cloud-bootstrap.ts +108 -0
- package/src/services/cloud-bridge.ts +386 -0
- package/src/services/cloud-container.ts +297 -0
- package/src/services/cloud-credential-provider.ts +210 -0
- package/src/services/cloud-managed-gateway-relay.ts +663 -0
- package/src/services/cloud-model-registry.ts +202 -0
- package/src/services/index.ts +17 -0
- package/{types → src/types}/cloud.ts +52 -29
- package/{types → src/types}/index.ts +6 -0
- package/src/utils/cloud-api.ts +10 -0
- package/src/utils/cloud-sdk/client.ts +735 -0
- package/src/utils/cloud-sdk/http.ts +291 -0
- package/src/utils/cloud-sdk/index.ts +23 -0
- package/src/utils/cloud-sdk/public-routes.ts +5070 -0
- package/src/utils/cloud-sdk/types.cloud-api.ts +120 -0
- package/src/utils/cloud-sdk/types.ts +762 -0
- package/src/utils/config.ts +174 -0
- package/src/utils/events.ts +37 -0
- package/src/utils/helpers.ts +107 -0
- package/src/utils/responses-output.ts +115 -0
- package/src/utils/sdk-client.ts +37 -0
- package/dist/actions/check-credits.d.ts +0 -6
- package/dist/actions/check-credits.d.ts.map +0 -1
- package/dist/actions/freeze-agent.d.ts +0 -9
- package/dist/actions/freeze-agent.d.ts.map +0 -1
- package/dist/actions/index.d.ts +0 -5
- package/dist/actions/index.d.ts.map +0 -1
- package/dist/actions/provision-agent.d.ts +0 -8
- package/dist/actions/provision-agent.d.ts.map +0 -1
- package/dist/actions/resume-agent.d.ts +0 -9
- package/dist/actions/resume-agent.d.ts.map +0 -1
- package/dist/build.d.ts +0 -3
- package/dist/build.d.ts.map +0 -1
- package/dist/generated/specs/specs.d.ts +0 -55
- package/dist/generated/specs/specs.d.ts.map +0 -1
- package/dist/models/object.d.ts +0 -4
- package/dist/models/object.d.ts.map +0 -1
- package/dist/utils/forwarded-settings.d.ts +0 -8
- package/dist/utils/forwarded-settings.d.ts.map +0 -1
|
@@ -0,0 +1,754 @@
|
|
|
1
|
+
import type http from "node:http";
|
|
2
|
+
import type { AgentRuntime, Service } from "@elizaos/core";
|
|
3
|
+
import { normalizeCloudSiteUrl } from "../cloud/base-url.js";
|
|
4
|
+
import { resolveCloudApiKey } from "../cloud/cloud-api-key.js";
|
|
5
|
+
import { validateCloudBaseUrl } from "../cloud/validate-url.js";
|
|
6
|
+
import type { CloudProxyConfigLike } from "../lib/config-like";
|
|
7
|
+
import { sendJson, sendJsonError } from "../lib/http";
|
|
8
|
+
|
|
9
|
+
export interface CloudBillingRouteState {
|
|
10
|
+
config: CloudProxyConfigLike;
|
|
11
|
+
runtime?: AgentRuntime | null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const PROXY_TIMEOUT_MS = 15_000;
|
|
15
|
+
const MAX_BODY_BYTES = 1_048_576;
|
|
16
|
+
const MAX_REDIRECTS = 4;
|
|
17
|
+
/**
|
|
18
|
+
* crypto/status changes rarely (it advertises whether crypto top-ups are
|
|
19
|
+
* enabled and which networks). Caching it amortises the second upstream call
|
|
20
|
+
* `forwardSummary` makes per dashboard refresh against the per-key rate limit.
|
|
21
|
+
*
|
|
22
|
+
* Keyed by `(baseUrl, Authorization header)` so multi-account hosts (or hosts
|
|
23
|
+
* that switch keys via re-login) never serve one account's cached status to
|
|
24
|
+
* another. The Authorization header is already present in process memory at
|
|
25
|
+
* the time of caching, so this introduces no new exposure.
|
|
26
|
+
*/
|
|
27
|
+
const CRYPTO_STATUS_CACHE_MS = 60_000;
|
|
28
|
+
const cryptoStatusCache = new Map<
|
|
29
|
+
string,
|
|
30
|
+
{ value: unknown; expiresAt: number }
|
|
31
|
+
>();
|
|
32
|
+
|
|
33
|
+
function cryptoStatusCacheKey(
|
|
34
|
+
baseUrl: string,
|
|
35
|
+
headers: Record<string, string>,
|
|
36
|
+
): string {
|
|
37
|
+
return `${baseUrl}|${headers.Authorization ?? ""}`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function fetchCryptoStatusCached(
|
|
41
|
+
baseUrl: string,
|
|
42
|
+
headers: Record<string, string>,
|
|
43
|
+
): Promise<unknown> {
|
|
44
|
+
const now = Date.now();
|
|
45
|
+
const cacheKey = cryptoStatusCacheKey(baseUrl, headers);
|
|
46
|
+
const cached = cryptoStatusCache.get(cacheKey);
|
|
47
|
+
if (cached && cached.expiresAt > now) {
|
|
48
|
+
return cached.value;
|
|
49
|
+
}
|
|
50
|
+
const response = await fetchUpstream(
|
|
51
|
+
`${baseUrl}/api/crypto/status`,
|
|
52
|
+
"GET",
|
|
53
|
+
headers,
|
|
54
|
+
undefined,
|
|
55
|
+
).catch(() => null);
|
|
56
|
+
if (!response?.ok) {
|
|
57
|
+
return cached?.value ?? null;
|
|
58
|
+
}
|
|
59
|
+
const value = await readJsonResponse(response).catch(() => ({}));
|
|
60
|
+
cryptoStatusCache.set(cacheKey, {
|
|
61
|
+
value,
|
|
62
|
+
expiresAt: now + CRYPTO_STATUS_CACHE_MS,
|
|
63
|
+
});
|
|
64
|
+
return value;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
interface CloudAuthApiKeyService {
|
|
68
|
+
isAuthenticated: () => boolean;
|
|
69
|
+
getApiKey?: () => string | undefined;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function resolveCloudBaseUrl(config: CloudProxyConfigLike): string {
|
|
73
|
+
return normalizeCloudSiteUrl(config.cloud?.baseUrl);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function normalizeCloudApiKey(value: string | null | undefined): string | null {
|
|
77
|
+
if (typeof value !== "string") return null;
|
|
78
|
+
const trimmed = value.trim();
|
|
79
|
+
if (!trimmed || trimmed.toUpperCase() === "[REDACTED]") {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
return trimmed;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function resolveProxyApiKey(state: CloudBillingRouteState): string | null {
|
|
86
|
+
const cloudAuth = state.runtime
|
|
87
|
+
? state.runtime.getService<Service & CloudAuthApiKeyService>("CLOUD_AUTH")
|
|
88
|
+
: null;
|
|
89
|
+
const runtimeApiKey =
|
|
90
|
+
cloudAuth?.isAuthenticated() === true
|
|
91
|
+
? normalizeCloudApiKey(cloudAuth.getApiKey?.())
|
|
92
|
+
: null;
|
|
93
|
+
|
|
94
|
+
return runtimeApiKey ?? resolveCloudApiKey(state.config, state.runtime);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function buildAuthHeaders(
|
|
98
|
+
config: CloudProxyConfigLike,
|
|
99
|
+
apiKeyOverride?: string | null,
|
|
100
|
+
): Record<string, string> {
|
|
101
|
+
const serviceKey = config.cloud?.serviceKey?.trim();
|
|
102
|
+
const apiKey =
|
|
103
|
+
normalizeCloudApiKey(apiKeyOverride) ?? resolveCloudApiKey(config);
|
|
104
|
+
|
|
105
|
+
const headers: Record<string, string> = {
|
|
106
|
+
Accept: "application/json",
|
|
107
|
+
"Content-Type": "application/json",
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
if (serviceKey) {
|
|
111
|
+
headers["X-Service-Key"] = serviceKey;
|
|
112
|
+
}
|
|
113
|
+
if (apiKey) {
|
|
114
|
+
headers.Authorization = `Bearer ${apiKey}`;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return headers;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
121
|
+
return typeof value === "object" && value !== null;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function readString(value: unknown): string | undefined {
|
|
125
|
+
return typeof value === "string" && value.trim() ? value : undefined;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function readNumber(value: unknown): number | null {
|
|
129
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
130
|
+
if (typeof value === "string" && value.trim()) {
|
|
131
|
+
const parsed = Number(value);
|
|
132
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
133
|
+
}
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function readBoolean(value: unknown): boolean | undefined {
|
|
138
|
+
return typeof value === "boolean" ? value : undefined;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function readBody(req: http.IncomingMessage): Promise<string | undefined> {
|
|
142
|
+
const preParsedBody = (req as http.IncomingMessage & { body?: unknown }).body;
|
|
143
|
+
if (preParsedBody !== undefined) {
|
|
144
|
+
return Promise.resolve(
|
|
145
|
+
typeof preParsedBody === "string"
|
|
146
|
+
? preParsedBody
|
|
147
|
+
: JSON.stringify(preParsedBody),
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (req.readableEnded) {
|
|
152
|
+
return Promise.resolve(undefined);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return new Promise<string | undefined>((resolve, reject) => {
|
|
156
|
+
const chunks: Buffer[] = [];
|
|
157
|
+
let size = 0;
|
|
158
|
+
|
|
159
|
+
req.on("data", (chunk: Buffer) => {
|
|
160
|
+
size += chunk.length;
|
|
161
|
+
if (size > MAX_BODY_BYTES) {
|
|
162
|
+
reject(new Error("Request body too large"));
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
chunks.push(chunk);
|
|
166
|
+
});
|
|
167
|
+
req.on("end", () =>
|
|
168
|
+
resolve(
|
|
169
|
+
chunks.length > 0 ? Buffer.concat(chunks).toString("utf-8") : undefined,
|
|
170
|
+
),
|
|
171
|
+
);
|
|
172
|
+
req.on("error", reject);
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
async function fetchUpstream(
|
|
177
|
+
url: string,
|
|
178
|
+
method: string,
|
|
179
|
+
headers: Record<string, string>,
|
|
180
|
+
body: string | undefined,
|
|
181
|
+
): Promise<Response> {
|
|
182
|
+
let currentUrl = url;
|
|
183
|
+
let currentMethod = method;
|
|
184
|
+
let currentBody = body;
|
|
185
|
+
|
|
186
|
+
for (
|
|
187
|
+
let redirectCount = 0;
|
|
188
|
+
redirectCount <= MAX_REDIRECTS;
|
|
189
|
+
redirectCount += 1
|
|
190
|
+
) {
|
|
191
|
+
const response = await fetch(currentUrl, {
|
|
192
|
+
method: currentMethod,
|
|
193
|
+
headers,
|
|
194
|
+
body: currentBody,
|
|
195
|
+
redirect: "manual",
|
|
196
|
+
signal: AbortSignal.timeout(PROXY_TIMEOUT_MS),
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
if (response.status < 300 || response.status >= 400) {
|
|
200
|
+
return response;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const location = response.headers.get("location");
|
|
204
|
+
if (!location) {
|
|
205
|
+
throw Object.assign(new Error("redirect"), { code: "REDIRECT" });
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const nextUrl = new URL(location, currentUrl).toString();
|
|
209
|
+
const currentOrigin = normalizeCloudSiteUrl(new URL(currentUrl).origin);
|
|
210
|
+
const nextOrigin = normalizeCloudSiteUrl(new URL(nextUrl).origin);
|
|
211
|
+
if (
|
|
212
|
+
new URL(currentUrl).origin !== new URL(nextUrl).origin &&
|
|
213
|
+
currentOrigin !== nextOrigin
|
|
214
|
+
) {
|
|
215
|
+
throw Object.assign(new Error("redirect"), { code: "REDIRECT" });
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
currentUrl = nextUrl;
|
|
219
|
+
if (
|
|
220
|
+
currentMethod !== "GET" &&
|
|
221
|
+
currentMethod !== "HEAD" &&
|
|
222
|
+
(response.status === 301 ||
|
|
223
|
+
response.status === 302 ||
|
|
224
|
+
response.status === 303)
|
|
225
|
+
) {
|
|
226
|
+
currentMethod = "GET";
|
|
227
|
+
currentBody = undefined;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
throw Object.assign(new Error("redirect"), { code: "REDIRECT" });
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
async function readJsonResponse(response: Response): Promise<unknown> {
|
|
235
|
+
return response.json().catch(async () => ({
|
|
236
|
+
success: response.ok,
|
|
237
|
+
error: await response.text().catch(() => "Billing request failed"),
|
|
238
|
+
}));
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function buildRedirectUrl(
|
|
242
|
+
baseUrl: string,
|
|
243
|
+
pathname: string,
|
|
244
|
+
params: Record<string, string>,
|
|
245
|
+
): string {
|
|
246
|
+
const url = new URL(pathname, `${baseUrl}/`);
|
|
247
|
+
for (const [key, value] of Object.entries(params)) {
|
|
248
|
+
url.searchParams.set(key, value);
|
|
249
|
+
}
|
|
250
|
+
return url.toString();
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function normalizeCryptoNetwork(value: string | undefined): string | undefined {
|
|
254
|
+
if (!value) return undefined;
|
|
255
|
+
|
|
256
|
+
const normalized = value.trim().toUpperCase();
|
|
257
|
+
switch (normalized) {
|
|
258
|
+
case "BSC":
|
|
259
|
+
case "BEP20":
|
|
260
|
+
return "BEP20";
|
|
261
|
+
case "ETH":
|
|
262
|
+
case "ERC20":
|
|
263
|
+
return "ERC20";
|
|
264
|
+
case "MATIC":
|
|
265
|
+
case "POLYGON":
|
|
266
|
+
return "POLYGON";
|
|
267
|
+
case "SOL":
|
|
268
|
+
case "SOLANA":
|
|
269
|
+
return "SOL";
|
|
270
|
+
case "BASE":
|
|
271
|
+
return "BASE";
|
|
272
|
+
case "ARB":
|
|
273
|
+
case "ARBITRUM":
|
|
274
|
+
return "ARB";
|
|
275
|
+
case "OP":
|
|
276
|
+
case "OPTIMISM":
|
|
277
|
+
return "OP";
|
|
278
|
+
case "TRON":
|
|
279
|
+
case "TRC20":
|
|
280
|
+
return "TRC20";
|
|
281
|
+
default:
|
|
282
|
+
return undefined;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function mapBillingSummary(
|
|
287
|
+
payload: unknown,
|
|
288
|
+
baseUrl: string,
|
|
289
|
+
cryptoStatus: unknown,
|
|
290
|
+
): Record<string, unknown> {
|
|
291
|
+
const source = isRecord(payload) ? payload : {};
|
|
292
|
+
const organization = isRecord(source.organization) ? source.organization : {};
|
|
293
|
+
const pricing = isRecord(source.pricing) ? source.pricing : {};
|
|
294
|
+
const crypto = isRecord(cryptoStatus) ? cryptoStatus : {};
|
|
295
|
+
const balance = readNumber(organization.creditBalance) ?? 0;
|
|
296
|
+
|
|
297
|
+
return {
|
|
298
|
+
success: source.success ?? true,
|
|
299
|
+
balance,
|
|
300
|
+
currency: "USD",
|
|
301
|
+
topUpUrl: `${baseUrl}/dashboard/settings?tab=billing`,
|
|
302
|
+
embeddedCheckoutEnabled: false,
|
|
303
|
+
hostedCheckoutEnabled: true,
|
|
304
|
+
cryptoEnabled:
|
|
305
|
+
readBoolean(crypto.enabled) ?? readBoolean(pricing.x402Enabled) ?? false,
|
|
306
|
+
low: balance < 2,
|
|
307
|
+
critical: balance < 0.5,
|
|
308
|
+
hasPaymentMethod: readBoolean(organization.hasPaymentMethod) ?? false,
|
|
309
|
+
autoTopUpEnabled: readBoolean(organization.autoTopUpEnabled) ?? false,
|
|
310
|
+
autoTopUpAmount: readNumber(organization.autoTopUpAmount),
|
|
311
|
+
autoTopUpThreshold: readNumber(organization.autoTopUpThreshold),
|
|
312
|
+
minimumTopUp: readNumber(pricing.minimumTopUp),
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function mapPaymentMethods(payload: unknown): Record<string, unknown> {
|
|
317
|
+
const source = isRecord(payload) ? payload : {};
|
|
318
|
+
const organization = isRecord(source.organization) ? source.organization : {};
|
|
319
|
+
const hasPaymentMethod = readBoolean(organization.hasPaymentMethod) ?? false;
|
|
320
|
+
|
|
321
|
+
return {
|
|
322
|
+
success: true,
|
|
323
|
+
data: hasPaymentMethod
|
|
324
|
+
? [
|
|
325
|
+
{
|
|
326
|
+
id: "stripe-default",
|
|
327
|
+
type: "card",
|
|
328
|
+
label: "Saved payment method",
|
|
329
|
+
brand: "Card",
|
|
330
|
+
isDefault: true,
|
|
331
|
+
},
|
|
332
|
+
]
|
|
333
|
+
: [],
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function mapBillingHistory(payload: unknown): Record<string, unknown> {
|
|
338
|
+
const source = isRecord(payload) ? payload : {};
|
|
339
|
+
const rawTransactions = Array.isArray(source.transactions)
|
|
340
|
+
? source.transactions
|
|
341
|
+
: [];
|
|
342
|
+
|
|
343
|
+
return {
|
|
344
|
+
success: true,
|
|
345
|
+
data: rawTransactions.filter(isRecord).map((item, index) => ({
|
|
346
|
+
id: readString(item.id) ?? `txn-${index}`,
|
|
347
|
+
kind: readString(item.type),
|
|
348
|
+
provider: readString(item.stripe_payment_intent_id)
|
|
349
|
+
? "stripe"
|
|
350
|
+
: undefined,
|
|
351
|
+
status: (readNumber(item.amount) ?? 0) >= 0 ? "credited" : "usage",
|
|
352
|
+
amount: readNumber(item.amount) ?? 0,
|
|
353
|
+
currency: "USD",
|
|
354
|
+
description: readString(item.description),
|
|
355
|
+
createdAt: readString(item.created_at) ?? new Date().toISOString(),
|
|
356
|
+
})),
|
|
357
|
+
total: readNumber(source.total),
|
|
358
|
+
period: source.period,
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function mapCheckoutResponse(payload: unknown): Record<string, unknown> {
|
|
363
|
+
const source = isRecord(payload) ? payload : {};
|
|
364
|
+
return {
|
|
365
|
+
success: true,
|
|
366
|
+
provider: "stripe",
|
|
367
|
+
mode: "hosted",
|
|
368
|
+
checkoutUrl: readString(source.url) ?? readString(source.checkoutUrl),
|
|
369
|
+
sessionId: readString(source.sessionId),
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
function mapCryptoQuoteResponse(
|
|
374
|
+
payload: unknown,
|
|
375
|
+
amountUsd: number,
|
|
376
|
+
payCurrency: string,
|
|
377
|
+
network: string | undefined,
|
|
378
|
+
): Record<string, unknown> {
|
|
379
|
+
const source = isRecord(payload) ? payload : {};
|
|
380
|
+
return {
|
|
381
|
+
success: true,
|
|
382
|
+
provider: "oxapay",
|
|
383
|
+
invoiceId: readString(source.paymentId) ?? readString(source.trackId),
|
|
384
|
+
trackId: readString(source.trackId),
|
|
385
|
+
network,
|
|
386
|
+
currency: payCurrency,
|
|
387
|
+
amount: readString(source.creditsToAdd) ?? String(amountUsd),
|
|
388
|
+
amountUsd,
|
|
389
|
+
paymentLinkUrl: readString(source.payLink),
|
|
390
|
+
expiresAt: readString(source.expiresAt),
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function parseJsonBody(body: string | undefined): Record<string, unknown> {
|
|
395
|
+
if (!body) return {};
|
|
396
|
+
const parsed = JSON.parse(body);
|
|
397
|
+
return isRecord(parsed) ? parsed : {};
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
async function forwardSummary(
|
|
401
|
+
baseUrl: string,
|
|
402
|
+
headers: Record<string, string>,
|
|
403
|
+
): Promise<{ status: number; payload: Record<string, unknown> | unknown }> {
|
|
404
|
+
const summaryResponse = await fetchUpstream(
|
|
405
|
+
`${baseUrl}/api/v1/credits/summary`,
|
|
406
|
+
"GET",
|
|
407
|
+
headers,
|
|
408
|
+
undefined,
|
|
409
|
+
);
|
|
410
|
+
const summaryPayload = await readJsonResponse(summaryResponse);
|
|
411
|
+
if (!summaryResponse.ok) {
|
|
412
|
+
return { status: summaryResponse.status, payload: summaryPayload };
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// Fetch crypto/status only when summary succeeded; cache it across requests
|
|
416
|
+
// so we don't burn a per-key rate-limit slot every time the dashboard
|
|
417
|
+
// refreshes (it changes rarely).
|
|
418
|
+
const cryptoPayload = (await fetchCryptoStatusCached(baseUrl, headers)) ?? {};
|
|
419
|
+
|
|
420
|
+
return {
|
|
421
|
+
status: summaryResponse.status,
|
|
422
|
+
payload: mapBillingSummary(summaryPayload, baseUrl, cryptoPayload),
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Wraps `sendJson` to also set the HTTP `Retry-After` header when the upstream
|
|
428
|
+
* payload describes a rate-limit (status 429 + `retryAfter` in the body).
|
|
429
|
+
* The body's `retryAfter` field is preserved for clients that read JSON.
|
|
430
|
+
*/
|
|
431
|
+
function sendJsonWithRetry(
|
|
432
|
+
res: http.ServerResponse,
|
|
433
|
+
payload: unknown,
|
|
434
|
+
status: number,
|
|
435
|
+
): void {
|
|
436
|
+
if (status === 429 && isRecord(payload)) {
|
|
437
|
+
const retryAfter = readNumber(payload.retryAfter);
|
|
438
|
+
if (retryAfter && retryAfter > 0) {
|
|
439
|
+
res.setHeader("Retry-After", String(Math.ceil(retryAfter)));
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
sendJson(res, payload, status);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
function mirrorPaymentHeaders(
|
|
446
|
+
res: http.ServerResponse,
|
|
447
|
+
upstreamResponse: Response,
|
|
448
|
+
): void {
|
|
449
|
+
for (const header of [
|
|
450
|
+
"PAYMENT-REQUIRED",
|
|
451
|
+
"Payment-Required",
|
|
452
|
+
"PAYMENT-RESPONSE",
|
|
453
|
+
"Payment-Response",
|
|
454
|
+
"Access-Control-Expose-Headers",
|
|
455
|
+
]) {
|
|
456
|
+
const value = upstreamResponse.headers.get(header);
|
|
457
|
+
if (value) {
|
|
458
|
+
res.setHeader(header, value);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
function isAllowedAppMoneyPath(pathname: string): boolean {
|
|
464
|
+
const appMoneyPath =
|
|
465
|
+
/^\/api\/cloud\/billing\/apps\/[^/]+\/(charges|earnings|monetization)(?:\/|$)/;
|
|
466
|
+
return appMoneyPath.test(pathname);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
function mapBillingProxyPath(pathname: string): string | null {
|
|
470
|
+
if (pathname === "/api/cloud/billing/x402") return "/api/v1/x402";
|
|
471
|
+
if (pathname.startsWith("/api/cloud/billing/x402/")) {
|
|
472
|
+
return pathname.replace("/api/cloud/billing/x402", "/api/v1/x402");
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
if (pathname === "/api/cloud/billing/app-credits") {
|
|
476
|
+
return "/api/v1/app-credits";
|
|
477
|
+
}
|
|
478
|
+
if (pathname.startsWith("/api/cloud/billing/app-credits/")) {
|
|
479
|
+
return pathname.replace(
|
|
480
|
+
"/api/cloud/billing/app-credits",
|
|
481
|
+
"/api/v1/app-credits",
|
|
482
|
+
);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
if (pathname === "/api/cloud/billing/affiliates") {
|
|
486
|
+
return "/api/v1/affiliates";
|
|
487
|
+
}
|
|
488
|
+
if (pathname.startsWith("/api/cloud/billing/affiliates/")) {
|
|
489
|
+
return pathname.replace(
|
|
490
|
+
"/api/cloud/billing/affiliates",
|
|
491
|
+
"/api/v1/affiliates",
|
|
492
|
+
);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
if (pathname === "/api/cloud/billing/redemptions") {
|
|
496
|
+
return "/api/v1/redemptions";
|
|
497
|
+
}
|
|
498
|
+
if (pathname.startsWith("/api/cloud/billing/redemptions/")) {
|
|
499
|
+
return pathname.replace(
|
|
500
|
+
"/api/cloud/billing/redemptions",
|
|
501
|
+
"/api/v1/redemptions",
|
|
502
|
+
);
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
if (isAllowedAppMoneyPath(pathname)) {
|
|
506
|
+
return pathname.replace("/api/cloud/billing/apps", "/api/v1/apps");
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
return null;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
async function forwardBillingProxy(
|
|
513
|
+
req: http.IncomingMessage,
|
|
514
|
+
res: http.ServerResponse,
|
|
515
|
+
method: string,
|
|
516
|
+
baseUrl: string,
|
|
517
|
+
headers: Record<string, string>,
|
|
518
|
+
upstreamPath: string,
|
|
519
|
+
search: string,
|
|
520
|
+
): Promise<void> {
|
|
521
|
+
let body: string | undefined;
|
|
522
|
+
if (method !== "GET" && method !== "HEAD") {
|
|
523
|
+
body = await readBody(req);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
const upstreamResponse = await fetchUpstream(
|
|
527
|
+
`${baseUrl}${upstreamPath}${search}`,
|
|
528
|
+
method,
|
|
529
|
+
headers,
|
|
530
|
+
body,
|
|
531
|
+
);
|
|
532
|
+
const responseData = await readJsonResponse(upstreamResponse);
|
|
533
|
+
mirrorPaymentHeaders(res, upstreamResponse);
|
|
534
|
+
sendJsonWithRetry(res, responseData, upstreamResponse.status);
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
export async function handleCloudBillingRoute(
|
|
538
|
+
req: http.IncomingMessage,
|
|
539
|
+
res: http.ServerResponse,
|
|
540
|
+
pathname: string,
|
|
541
|
+
method: string,
|
|
542
|
+
state: CloudBillingRouteState,
|
|
543
|
+
): Promise<boolean> {
|
|
544
|
+
if (!pathname.startsWith("/api/cloud/billing")) return false;
|
|
545
|
+
|
|
546
|
+
const apiKey = resolveProxyApiKey(state);
|
|
547
|
+
if (!apiKey) {
|
|
548
|
+
sendJsonError(
|
|
549
|
+
res,
|
|
550
|
+
"Not connected to Eliza Cloud. Please log in first.",
|
|
551
|
+
401,
|
|
552
|
+
);
|
|
553
|
+
return true;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
const baseUrl = resolveCloudBaseUrl(state.config);
|
|
557
|
+
const urlError = await validateCloudBaseUrl(baseUrl);
|
|
558
|
+
if (urlError) {
|
|
559
|
+
sendJsonError(res, urlError, 502);
|
|
560
|
+
return true;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
const headers = buildAuthHeaders(state.config, apiKey);
|
|
564
|
+
|
|
565
|
+
const fullUrl = new URL(req.url ?? pathname, "http://localhost");
|
|
566
|
+
const proxiedMoneyPath = mapBillingProxyPath(pathname);
|
|
567
|
+
if (proxiedMoneyPath) {
|
|
568
|
+
await forwardBillingProxy(
|
|
569
|
+
req,
|
|
570
|
+
res,
|
|
571
|
+
method,
|
|
572
|
+
baseUrl,
|
|
573
|
+
headers,
|
|
574
|
+
proxiedMoneyPath,
|
|
575
|
+
fullUrl.search,
|
|
576
|
+
);
|
|
577
|
+
return true;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
if (pathname === "/api/cloud/billing/summary" && method === "GET") {
|
|
581
|
+
const { status, payload } = await forwardSummary(baseUrl, headers);
|
|
582
|
+
sendJsonWithRetry(res, payload, status);
|
|
583
|
+
return true;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
if (pathname === "/api/cloud/billing/payment-methods" && method === "GET") {
|
|
587
|
+
const summaryResponse = await fetchUpstream(
|
|
588
|
+
`${baseUrl}/api/v1/credits/summary`,
|
|
589
|
+
"GET",
|
|
590
|
+
headers,
|
|
591
|
+
undefined,
|
|
592
|
+
);
|
|
593
|
+
const summaryPayload = await readJsonResponse(summaryResponse);
|
|
594
|
+
sendJsonWithRetry(
|
|
595
|
+
res,
|
|
596
|
+
summaryResponse.ok ? mapPaymentMethods(summaryPayload) : summaryPayload,
|
|
597
|
+
summaryResponse.status,
|
|
598
|
+
);
|
|
599
|
+
return true;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
if (pathname === "/api/cloud/billing/history" && method === "GET") {
|
|
603
|
+
const upstreamUrl = `${baseUrl}/api/credits/transactions${fullUrl.search}`;
|
|
604
|
+
const historyResponse = await fetchUpstream(
|
|
605
|
+
upstreamUrl,
|
|
606
|
+
"GET",
|
|
607
|
+
headers,
|
|
608
|
+
undefined,
|
|
609
|
+
);
|
|
610
|
+
const historyPayload = await readJsonResponse(historyResponse);
|
|
611
|
+
sendJsonWithRetry(
|
|
612
|
+
res,
|
|
613
|
+
historyResponse.ok ? mapBillingHistory(historyPayload) : historyPayload,
|
|
614
|
+
historyResponse.status,
|
|
615
|
+
);
|
|
616
|
+
return true;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
if (pathname === "/api/cloud/billing/checkout" && method === "POST") {
|
|
620
|
+
const body = await readBody(req);
|
|
621
|
+
const requestBody = parseJsonBody(body);
|
|
622
|
+
const amountUsd = readNumber(requestBody.amountUsd);
|
|
623
|
+
|
|
624
|
+
if (!amountUsd || amountUsd <= 0) {
|
|
625
|
+
sendJsonError(res, "Invalid top-up amount", 400);
|
|
626
|
+
return true;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
const upstreamBody = JSON.stringify({
|
|
630
|
+
credits: amountUsd,
|
|
631
|
+
success_url: buildRedirectUrl(baseUrl, "/dashboard/billing/success", {
|
|
632
|
+
from: "eliza",
|
|
633
|
+
}),
|
|
634
|
+
cancel_url: buildRedirectUrl(baseUrl, "/dashboard/settings", {
|
|
635
|
+
from: "eliza",
|
|
636
|
+
tab: "billing",
|
|
637
|
+
canceled: "1",
|
|
638
|
+
}),
|
|
639
|
+
});
|
|
640
|
+
|
|
641
|
+
const checkoutResponse = await fetchUpstream(
|
|
642
|
+
`${baseUrl}/api/v1/credits/checkout`,
|
|
643
|
+
"POST",
|
|
644
|
+
headers,
|
|
645
|
+
upstreamBody,
|
|
646
|
+
);
|
|
647
|
+
const checkoutPayload = await readJsonResponse(checkoutResponse);
|
|
648
|
+
sendJsonWithRetry(
|
|
649
|
+
res,
|
|
650
|
+
checkoutResponse.ok
|
|
651
|
+
? mapCheckoutResponse(checkoutPayload)
|
|
652
|
+
: checkoutPayload,
|
|
653
|
+
checkoutResponse.status,
|
|
654
|
+
);
|
|
655
|
+
return true;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
if (pathname === "/api/cloud/billing/crypto/quote" && method === "POST") {
|
|
659
|
+
const body = await readBody(req);
|
|
660
|
+
const requestBody = parseJsonBody(body);
|
|
661
|
+
const amountUsd = readNumber(requestBody.amountUsd);
|
|
662
|
+
|
|
663
|
+
if (!amountUsd || amountUsd <= 0) {
|
|
664
|
+
sendJsonError(res, "Invalid top-up amount", 400);
|
|
665
|
+
return true;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
const payCurrency =
|
|
669
|
+
readString(requestBody.currency)?.trim().toUpperCase() ?? "USDC";
|
|
670
|
+
const network = normalizeCryptoNetwork(readString(requestBody.network));
|
|
671
|
+
const upstreamBody = JSON.stringify({
|
|
672
|
+
amount: amountUsd,
|
|
673
|
+
payCurrency,
|
|
674
|
+
network,
|
|
675
|
+
});
|
|
676
|
+
|
|
677
|
+
const cryptoResponse = await fetchUpstream(
|
|
678
|
+
`${baseUrl}/api/crypto/payments`,
|
|
679
|
+
"POST",
|
|
680
|
+
headers,
|
|
681
|
+
upstreamBody,
|
|
682
|
+
);
|
|
683
|
+
const cryptoPayload = await readJsonResponse(cryptoResponse);
|
|
684
|
+
sendJsonWithRetry(
|
|
685
|
+
res,
|
|
686
|
+
cryptoResponse.ok
|
|
687
|
+
? mapCryptoQuoteResponse(cryptoPayload, amountUsd, payCurrency, network)
|
|
688
|
+
: cryptoPayload,
|
|
689
|
+
cryptoResponse.status,
|
|
690
|
+
);
|
|
691
|
+
return true;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
if (pathname.startsWith("/api/cloud/billing/credits/")) {
|
|
695
|
+
let body: string | undefined;
|
|
696
|
+
if (method !== "GET" && method !== "HEAD") {
|
|
697
|
+
body = await readBody(req);
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
const upstreamPath = pathname.replace(
|
|
701
|
+
"/api/cloud/billing/credits",
|
|
702
|
+
"/api/v1/credits",
|
|
703
|
+
);
|
|
704
|
+
const upstreamResponse = await fetchUpstream(
|
|
705
|
+
`${baseUrl}${upstreamPath}${fullUrl.search}`,
|
|
706
|
+
method,
|
|
707
|
+
headers,
|
|
708
|
+
body,
|
|
709
|
+
);
|
|
710
|
+
const responseData = await readJsonResponse(upstreamResponse);
|
|
711
|
+
sendJsonWithRetry(res, responseData, upstreamResponse.status);
|
|
712
|
+
return true;
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
if (
|
|
716
|
+
pathname.startsWith("/api/cloud/billing/crypto/") &&
|
|
717
|
+
pathname !== "/api/cloud/billing/crypto/quote"
|
|
718
|
+
) {
|
|
719
|
+
let body: string | undefined;
|
|
720
|
+
if (method !== "GET" && method !== "HEAD") {
|
|
721
|
+
body = await readBody(req);
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
const upstreamPath = pathname.replace(
|
|
725
|
+
"/api/cloud/billing/crypto",
|
|
726
|
+
"/api/crypto",
|
|
727
|
+
);
|
|
728
|
+
const upstreamResponse = await fetchUpstream(
|
|
729
|
+
`${baseUrl}${upstreamPath}${fullUrl.search}`,
|
|
730
|
+
method,
|
|
731
|
+
headers,
|
|
732
|
+
body,
|
|
733
|
+
);
|
|
734
|
+
const responseData = await readJsonResponse(upstreamResponse);
|
|
735
|
+
sendJsonWithRetry(res, responseData, upstreamResponse.status);
|
|
736
|
+
return true;
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
let body: string | undefined;
|
|
740
|
+
if (method !== "GET" && method !== "HEAD") {
|
|
741
|
+
body = await readBody(req);
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
const billingPath = pathname.replace("/api/cloud/billing", "/api/v1/billing");
|
|
745
|
+
const upstreamResponse = await fetchUpstream(
|
|
746
|
+
`${baseUrl}${billingPath}${fullUrl.search}`,
|
|
747
|
+
method,
|
|
748
|
+
headers,
|
|
749
|
+
body,
|
|
750
|
+
);
|
|
751
|
+
const responseData = await readJsonResponse(upstreamResponse);
|
|
752
|
+
sendJsonWithRetry(res, responseData, upstreamResponse.status);
|
|
753
|
+
return true;
|
|
754
|
+
}
|