@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,567 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CloudAuthService — Eliza Cloud authentication entry points.
|
|
3
|
+
*
|
|
4
|
+
* Two distinct auth flows live here:
|
|
5
|
+
*
|
|
6
|
+
* 1. **Device auto-signup** (`authenticateWithDevice`) — convenience-only.
|
|
7
|
+
* Derives a hardware fingerprint and exchanges it for a free-tier API key
|
|
8
|
+
* against the cloud signup endpoint. The result is treated as opaque and
|
|
9
|
+
* is **never** trusted as inbound auth for the local Eliza dashboard.
|
|
10
|
+
* See `docs/security/remote-auth-hardening-plan.md` §7 for the explicit
|
|
11
|
+
* demotion rationale.
|
|
12
|
+
*
|
|
13
|
+
* 2. **Eliza Cloud SSO** (`getSsoRedirectUrl` / `exchangeCodeForSession`) —
|
|
14
|
+
* OAuth-style authorization-code flow against the cloud issuer. The
|
|
15
|
+
* callback handler in `app-core` (`api/auth/cloud-sso.ts`) consumes these
|
|
16
|
+
* methods to bind a verified cloud user to a local Identity. All error
|
|
17
|
+
* paths fail closed: the methods throw and the caller MUST refuse the
|
|
18
|
+
* request. There is no partial-claims fallback.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import {
|
|
22
|
+
type RuntimeEnvRecord,
|
|
23
|
+
type IAgentRuntime,
|
|
24
|
+
logger,
|
|
25
|
+
Service,
|
|
26
|
+
resolveApiSecurityConfig,
|
|
27
|
+
resolveDesktopApiPort,
|
|
28
|
+
} from "@elizaos/core";
|
|
29
|
+
import { createRemoteJWKSet, jwtVerify } from "jose";
|
|
30
|
+
import type { CloudCredentials, DeviceAuthResponse, DevicePlatform } from "../types/cloud";
|
|
31
|
+
import { DEFAULT_CLOUD_CONFIG } from "../types/cloud";
|
|
32
|
+
import { CloudApiClient } from "../utils/cloud-api";
|
|
33
|
+
import type { CloudBootstrapService } from "./cloud-bootstrap";
|
|
34
|
+
|
|
35
|
+
/** SHA-256 hash of hostname + platform + arch + cpu + memory. */
|
|
36
|
+
async function deriveDeviceId(): Promise<string> {
|
|
37
|
+
const os = await import("node:os");
|
|
38
|
+
const crypto = await import("node:crypto");
|
|
39
|
+
const cpus = os.cpus();
|
|
40
|
+
const raw = [
|
|
41
|
+
os.hostname(),
|
|
42
|
+
os.platform(),
|
|
43
|
+
os.arch(),
|
|
44
|
+
cpus[0]?.model ?? "?",
|
|
45
|
+
cpus.length,
|
|
46
|
+
os.totalmem(),
|
|
47
|
+
].join(":");
|
|
48
|
+
return crypto.createHash("sha256").update(raw).digest("hex");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function detectPlatform(): DevicePlatform {
|
|
52
|
+
if (typeof process === "undefined") return "web";
|
|
53
|
+
const map: Record<string, DevicePlatform> = {
|
|
54
|
+
darwin: "macos",
|
|
55
|
+
win32: "windows",
|
|
56
|
+
linux: "linux",
|
|
57
|
+
};
|
|
58
|
+
return map[process.platform] ?? "linux";
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ─── Eliza Cloud SSO ───────────────────────────────────────────────────────
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Required ID-token claims for an Eliza Cloud SSO exchange.
|
|
65
|
+
*
|
|
66
|
+
* `sub` is the cloud user id (canonical identity key). `email` and `name`
|
|
67
|
+
* are surfaced for UI display and identity provisioning. Anything else the
|
|
68
|
+
* cloud issuer adds is preserved on `extra` for callers that need it but
|
|
69
|
+
* is never required for auth decisions.
|
|
70
|
+
*/
|
|
71
|
+
export interface CloudSsoIdTokenClaims {
|
|
72
|
+
iss: string;
|
|
73
|
+
sub: string;
|
|
74
|
+
aud: string | string[];
|
|
75
|
+
exp: number;
|
|
76
|
+
iat: number;
|
|
77
|
+
email: string;
|
|
78
|
+
email_verified?: boolean;
|
|
79
|
+
name: string;
|
|
80
|
+
picture?: string;
|
|
81
|
+
extra: Record<string, unknown>;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export interface CloudSsoSession {
|
|
85
|
+
cloudUserId: string;
|
|
86
|
+
email: string;
|
|
87
|
+
displayName: string;
|
|
88
|
+
claims: CloudSsoIdTokenClaims;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface SsoRedirectArgs {
|
|
92
|
+
/**
|
|
93
|
+
* Local URL the user should land on after the SSO round-trip
|
|
94
|
+
* (e.g. `/onboarding/setup`). The dashboard's callback route forwards
|
|
95
|
+
* to this once the session cookie is set; it is NOT sent to the cloud
|
|
96
|
+
* issuer.
|
|
97
|
+
*/
|
|
98
|
+
returnTo?: string;
|
|
99
|
+
/**
|
|
100
|
+
* State nonce. The caller is responsible for generating this with
|
|
101
|
+
* `crypto.randomBytes(32)` and storing it server-side keyed by the
|
|
102
|
+
* issued cookie / pending exchange.
|
|
103
|
+
*/
|
|
104
|
+
state: string;
|
|
105
|
+
/**
|
|
106
|
+
* Override for `ELIZA_CLOUD_CLIENT_ID`. Falls through to the env when
|
|
107
|
+
* unset; explicitly throws when neither is provided.
|
|
108
|
+
*/
|
|
109
|
+
clientId?: string;
|
|
110
|
+
/** Allows tests to inject a synthetic env record. */
|
|
111
|
+
env?: RuntimeEnvRecord;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export interface ExchangeCodeArgs {
|
|
115
|
+
/** Authorization code returned on the SSO callback. */
|
|
116
|
+
code: string;
|
|
117
|
+
/** State value the cloud issuer echoed back on the callback. */
|
|
118
|
+
state: string;
|
|
119
|
+
/**
|
|
120
|
+
* State value the caller originally issued. Compared with `state` and
|
|
121
|
+
* mismatch causes a fail-closed throw before any network call is made.
|
|
122
|
+
*/
|
|
123
|
+
expectedState: string;
|
|
124
|
+
/**
|
|
125
|
+
* Source for `getJwksUrl()`. The caller resolves this from the runtime
|
|
126
|
+
* service registry (`runtime.getService("CLOUD_BOOTSTRAP")`) so this
|
|
127
|
+
* file does not import from `app-core` directly.
|
|
128
|
+
*/
|
|
129
|
+
bootstrap: CloudBootstrapService;
|
|
130
|
+
/** Allows tests to inject a fake fetch for the token endpoint. */
|
|
131
|
+
fetchImpl?: typeof fetch;
|
|
132
|
+
/** Allows tests to inject a synthetic env record. */
|
|
133
|
+
env?: RuntimeEnvRecord;
|
|
134
|
+
/** Optional override for the redirect URI; defaults to the local callback. */
|
|
135
|
+
redirectUri?: string;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
interface RawTokenResponse {
|
|
139
|
+
id_token?: unknown;
|
|
140
|
+
access_token?: unknown;
|
|
141
|
+
token_type?: unknown;
|
|
142
|
+
expires_in?: unknown;
|
|
143
|
+
scope?: unknown;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
interface ApiKeyAuthInput {
|
|
147
|
+
apiKey: string;
|
|
148
|
+
organizationId?: string;
|
|
149
|
+
userId?: string;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
interface RawIdTokenPayload {
|
|
153
|
+
iss?: unknown;
|
|
154
|
+
sub?: unknown;
|
|
155
|
+
aud?: unknown;
|
|
156
|
+
exp?: unknown;
|
|
157
|
+
iat?: unknown;
|
|
158
|
+
email?: unknown;
|
|
159
|
+
email_verified?: unknown;
|
|
160
|
+
name?: unknown;
|
|
161
|
+
picture?: unknown;
|
|
162
|
+
[otherProperty: string]: unknown;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function readEnvKey(env: RuntimeEnvRecord, key: string): string | null {
|
|
166
|
+
const value = env[key];
|
|
167
|
+
if (typeof value !== "string") return null;
|
|
168
|
+
const trimmed = value.trim();
|
|
169
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function processEnv(): RuntimeEnvRecord {
|
|
173
|
+
if (typeof process === "undefined") return {};
|
|
174
|
+
return process.env as RuntimeEnvRecord;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Build the local SSO callback URL the cloud issuer will redirect back to.
|
|
179
|
+
*
|
|
180
|
+
* Reads `ELIZA_API_BIND` and the desktop API port from `@elizaos/core`'s
|
|
181
|
+
* runtime-env helper so the redirect_uri matches whatever the dashboard
|
|
182
|
+
* is actually serving on. Loopback binds default to `http://127.0.0.1:<port>`;
|
|
183
|
+
* non-loopback binds (cloud-provisioned containers) use `https://`.
|
|
184
|
+
*/
|
|
185
|
+
function defaultRedirectUri(env: RuntimeEnvRecord): string {
|
|
186
|
+
const security = resolveApiSecurityConfig(env);
|
|
187
|
+
const port = resolveDesktopApiPort(env);
|
|
188
|
+
const scheme = security.isLoopbackBind ? "http" : "https";
|
|
189
|
+
// Strip any IPv6 brackets the bind host might already include.
|
|
190
|
+
const host = security.bindHost.startsWith("[")
|
|
191
|
+
? security.bindHost
|
|
192
|
+
: security.bindHost.includes(":") && !security.bindHost.startsWith("[")
|
|
193
|
+
? `[${security.bindHost}]`
|
|
194
|
+
: security.bindHost;
|
|
195
|
+
return `${scheme}://${host}:${port}/api/auth/login/sso/callback`;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Returns the absolute URL the dashboard should redirect the user to in
|
|
200
|
+
* order to start an Eliza Cloud SSO authorization-code flow.
|
|
201
|
+
*
|
|
202
|
+
* Throws when `ELIZA_CLOUD_CLIENT_ID` is unset and no `clientId` override
|
|
203
|
+
* is provided — there is no built-in default. `ELIZA_CLOUD_ISSUER` is read
|
|
204
|
+
* via the `CloudBootstrapService`'s service-port and must already be set;
|
|
205
|
+
* if not, this method throws via the bootstrap's existing fail-closed
|
|
206
|
+
* behaviour.
|
|
207
|
+
*
|
|
208
|
+
* The `state` argument MUST be generated by the caller with a cryptographic
|
|
209
|
+
* RNG and stored server-side bound to the issued cookie. This method does
|
|
210
|
+
* NOT generate or persist state.
|
|
211
|
+
*
|
|
212
|
+
* @param bootstrap - Service-port that exposes `getExpectedIssuer()`.
|
|
213
|
+
* @param args - Required `state`, optional `clientId` / `returnTo` / `env`.
|
|
214
|
+
*/
|
|
215
|
+
export function getSsoRedirectUrl(bootstrap: CloudBootstrapService, args: SsoRedirectArgs): string {
|
|
216
|
+
const env = args.env ?? processEnv();
|
|
217
|
+
const clientId = args.clientId ?? readEnvKey(env, "ELIZA_CLOUD_CLIENT_ID");
|
|
218
|
+
if (!clientId) {
|
|
219
|
+
throw new Error("ELIZA_CLOUD_CLIENT_ID is not configured — cannot start Eliza Cloud SSO");
|
|
220
|
+
}
|
|
221
|
+
if (args.state.length === 0) {
|
|
222
|
+
throw new Error("getSsoRedirectUrl requires a non-empty state nonce");
|
|
223
|
+
}
|
|
224
|
+
const issuer = bootstrap.getExpectedIssuer();
|
|
225
|
+
const redirectUri = defaultRedirectUri(env);
|
|
226
|
+
const params = new URLSearchParams();
|
|
227
|
+
params.set("response_type", "code");
|
|
228
|
+
params.set("client_id", clientId);
|
|
229
|
+
params.set("redirect_uri", redirectUri);
|
|
230
|
+
params.set("scope", "openid profile");
|
|
231
|
+
params.set("state", args.state);
|
|
232
|
+
if (args.returnTo) {
|
|
233
|
+
// Forwarded through state on the cloud side is not safe (issuer-controlled);
|
|
234
|
+
// we surface it as a separate hint that the local callback honours.
|
|
235
|
+
params.set("eliza_return_to", args.returnTo);
|
|
236
|
+
}
|
|
237
|
+
return `${issuer}/oauth/authorize?${params.toString()}`;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function shapeIdTokenClaims(payload: RawIdTokenPayload): CloudSsoIdTokenClaims {
|
|
241
|
+
if (typeof payload.iss !== "string" || payload.iss.length === 0) {
|
|
242
|
+
throw new Error("id_token missing issuer claim");
|
|
243
|
+
}
|
|
244
|
+
if (typeof payload.sub !== "string" || payload.sub.length === 0) {
|
|
245
|
+
throw new Error("id_token missing sub claim");
|
|
246
|
+
}
|
|
247
|
+
if (
|
|
248
|
+
typeof payload.aud !== "string" &&
|
|
249
|
+
!(Array.isArray(payload.aud) && payload.aud.every((value) => typeof value === "string"))
|
|
250
|
+
) {
|
|
251
|
+
throw new Error("id_token missing or malformed aud claim");
|
|
252
|
+
}
|
|
253
|
+
if (typeof payload.exp !== "number" || !Number.isFinite(payload.exp)) {
|
|
254
|
+
throw new Error("id_token missing exp claim");
|
|
255
|
+
}
|
|
256
|
+
if (typeof payload.iat !== "number" || !Number.isFinite(payload.iat)) {
|
|
257
|
+
throw new Error("id_token missing iat claim");
|
|
258
|
+
}
|
|
259
|
+
if (typeof payload.email !== "string" || payload.email.length === 0) {
|
|
260
|
+
throw new Error("id_token missing email claim — Eliza Cloud SSO requires it");
|
|
261
|
+
}
|
|
262
|
+
if (typeof payload.name !== "string" || payload.name.length === 0) {
|
|
263
|
+
throw new Error("id_token missing name claim — Eliza Cloud SSO requires it");
|
|
264
|
+
}
|
|
265
|
+
const extra: Record<string, unknown> = {};
|
|
266
|
+
for (const [key, value] of Object.entries(payload)) {
|
|
267
|
+
if (
|
|
268
|
+
key !== "iss" &&
|
|
269
|
+
key !== "sub" &&
|
|
270
|
+
key !== "aud" &&
|
|
271
|
+
key !== "exp" &&
|
|
272
|
+
key !== "iat" &&
|
|
273
|
+
key !== "email" &&
|
|
274
|
+
key !== "email_verified" &&
|
|
275
|
+
key !== "name" &&
|
|
276
|
+
key !== "picture"
|
|
277
|
+
) {
|
|
278
|
+
extra[key] = value;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
return {
|
|
282
|
+
iss: payload.iss,
|
|
283
|
+
sub: payload.sub,
|
|
284
|
+
aud: payload.aud as string | string[],
|
|
285
|
+
exp: payload.exp,
|
|
286
|
+
iat: payload.iat,
|
|
287
|
+
email: payload.email,
|
|
288
|
+
email_verified:
|
|
289
|
+
typeof payload.email_verified === "boolean" ? payload.email_verified : undefined,
|
|
290
|
+
name: payload.name,
|
|
291
|
+
picture: typeof payload.picture === "string" ? payload.picture : undefined,
|
|
292
|
+
extra,
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function shapeTokenResponse(payload: unknown): { idToken: string } {
|
|
297
|
+
if (!payload || typeof payload !== "object") {
|
|
298
|
+
throw new Error("Eliza Cloud token endpoint returned a non-object body");
|
|
299
|
+
}
|
|
300
|
+
const raw = payload as RawTokenResponse;
|
|
301
|
+
if (typeof raw.id_token !== "string" || raw.id_token.length === 0) {
|
|
302
|
+
throw new Error("Eliza Cloud token endpoint did not return an id_token");
|
|
303
|
+
}
|
|
304
|
+
return { idToken: raw.id_token };
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Exchange an authorization code for a verified Eliza Cloud session.
|
|
309
|
+
*
|
|
310
|
+
* Steps:
|
|
311
|
+
* 1. Compare `state === expectedState`. Mismatch throws.
|
|
312
|
+
* 2. POST to `${ELIZA_CLOUD_ISSUER}/oauth/token` with the code,
|
|
313
|
+
* `client_id`, and `client_secret` (the latter from
|
|
314
|
+
* `ELIZA_CLOUD_CLIENT_SECRET`).
|
|
315
|
+
* 3. Verify the returned `id_token` against the JWKS exposed by
|
|
316
|
+
* `CloudBootstrapService.getJwksUrl()`. RS256 only.
|
|
317
|
+
* 4. Project the claims onto a `CloudSsoSession`.
|
|
318
|
+
*
|
|
319
|
+
* Any error in fetch / signature verify / claim shape throws — this method
|
|
320
|
+
* NEVER returns a partial or fallback session.
|
|
321
|
+
*/
|
|
322
|
+
export async function exchangeCodeForSession(args: ExchangeCodeArgs): Promise<CloudSsoSession> {
|
|
323
|
+
if (args.code.length === 0) {
|
|
324
|
+
throw new Error("exchangeCodeForSession requires a non-empty code");
|
|
325
|
+
}
|
|
326
|
+
if (!args.state || !args.expectedState || args.state !== args.expectedState) {
|
|
327
|
+
throw new Error("Eliza Cloud SSO state mismatch — refusing to exchange code (possible CSRF)");
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const env = args.env ?? processEnv();
|
|
331
|
+
const clientId = readEnvKey(env, "ELIZA_CLOUD_CLIENT_ID");
|
|
332
|
+
if (!clientId) {
|
|
333
|
+
throw new Error("ELIZA_CLOUD_CLIENT_ID is not configured — cannot complete Eliza Cloud SSO");
|
|
334
|
+
}
|
|
335
|
+
const clientSecret = readEnvKey(env, "ELIZA_CLOUD_CLIENT_SECRET");
|
|
336
|
+
if (!clientSecret) {
|
|
337
|
+
throw new Error(
|
|
338
|
+
"ELIZA_CLOUD_CLIENT_SECRET is not configured — cannot complete Eliza Cloud SSO"
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const issuer = args.bootstrap.getExpectedIssuer();
|
|
343
|
+
const redirectUri = args.redirectUri ?? defaultRedirectUri(env);
|
|
344
|
+
const tokenUrl = `${issuer}/oauth/token`;
|
|
345
|
+
|
|
346
|
+
const fetchImpl = args.fetchImpl ?? fetch;
|
|
347
|
+
const body = new URLSearchParams();
|
|
348
|
+
body.set("grant_type", "authorization_code");
|
|
349
|
+
body.set("code", args.code);
|
|
350
|
+
body.set("redirect_uri", redirectUri);
|
|
351
|
+
body.set("client_id", clientId);
|
|
352
|
+
body.set("client_secret", clientSecret);
|
|
353
|
+
|
|
354
|
+
const response = await fetchImpl(tokenUrl, {
|
|
355
|
+
method: "POST",
|
|
356
|
+
headers: {
|
|
357
|
+
"content-type": "application/x-www-form-urlencoded",
|
|
358
|
+
accept: "application/json",
|
|
359
|
+
},
|
|
360
|
+
body: body.toString(),
|
|
361
|
+
});
|
|
362
|
+
if (!response.ok) {
|
|
363
|
+
throw new Error(
|
|
364
|
+
`Eliza Cloud token endpoint returned HTTP ${response.status} for code exchange`
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
const payload: unknown = await response.json();
|
|
368
|
+
const { idToken } = shapeTokenResponse(payload);
|
|
369
|
+
|
|
370
|
+
const jwksUrl = args.bootstrap.getJwksUrl();
|
|
371
|
+
const remoteJwks = createRemoteJWKSet(new URL(jwksUrl));
|
|
372
|
+
|
|
373
|
+
const verified = await jwtVerify(idToken, remoteJwks, {
|
|
374
|
+
algorithms: ["RS256"],
|
|
375
|
+
issuer,
|
|
376
|
+
audience: clientId,
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
const claims = shapeIdTokenClaims(verified.payload as RawIdTokenPayload);
|
|
380
|
+
|
|
381
|
+
return {
|
|
382
|
+
cloudUserId: claims.sub,
|
|
383
|
+
email: claims.email,
|
|
384
|
+
displayName: claims.name,
|
|
385
|
+
claims,
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// ─── Service ───────────────────────────────────────────────────────────────
|
|
390
|
+
|
|
391
|
+
export class CloudAuthService extends Service {
|
|
392
|
+
static serviceType = "CLOUD_AUTH";
|
|
393
|
+
capabilityDescription = "Eliza Cloud device authentication and SSO session helpers";
|
|
394
|
+
|
|
395
|
+
private client: CloudApiClient;
|
|
396
|
+
private credentials: CloudCredentials | null = null;
|
|
397
|
+
|
|
398
|
+
constructor(runtime?: IAgentRuntime) {
|
|
399
|
+
super(runtime);
|
|
400
|
+
this.client = new CloudApiClient(DEFAULT_CLOUD_CONFIG.baseUrl);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
static async start(runtime: IAgentRuntime): Promise<Service> {
|
|
404
|
+
const service = new CloudAuthService(runtime);
|
|
405
|
+
await service.initialize();
|
|
406
|
+
return service;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
async stop(): Promise<void> {
|
|
410
|
+
this.credentials = null;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
private async initialize(): Promise<void> {
|
|
414
|
+
const baseUrl = String(
|
|
415
|
+
this.runtime.getSetting("ELIZAOS_CLOUD_BASE_URL") ?? DEFAULT_CLOUD_CONFIG.baseUrl
|
|
416
|
+
);
|
|
417
|
+
this.client.setBaseUrl(baseUrl);
|
|
418
|
+
|
|
419
|
+
// Try existing API key first. If the key is present in settings
|
|
420
|
+
// (persisted via config file or character secrets in the DB), trust it
|
|
421
|
+
// immediately so the agent is functional even when the cloud API is
|
|
422
|
+
// temporarily unreachable. A background validation fires to confirm
|
|
423
|
+
// the key — if it turns out to be revoked the next model call will
|
|
424
|
+
// surface the error, but the agent won't stall on startup.
|
|
425
|
+
const existingKey = this.runtime.getSetting("ELIZAOS_CLOUD_API_KEY");
|
|
426
|
+
if (existingKey) {
|
|
427
|
+
const key = String(existingKey);
|
|
428
|
+
this.client.setApiKey(key);
|
|
429
|
+
|
|
430
|
+
// Accept the key optimistically — no blocking network call.
|
|
431
|
+
this.credentials = {
|
|
432
|
+
apiKey: key,
|
|
433
|
+
userId: String(this.runtime.getSetting("ELIZAOS_CLOUD_USER_ID") ?? ""),
|
|
434
|
+
organizationId: String(
|
|
435
|
+
this.runtime.getSetting("ELIZAOS_CLOUD_ORG_ID") ??
|
|
436
|
+
this.runtime.getSetting("ELIZA_CLOUD_ORGANIZATION_ID") ??
|
|
437
|
+
""
|
|
438
|
+
),
|
|
439
|
+
authenticatedAt: Date.now(),
|
|
440
|
+
};
|
|
441
|
+
logger.info("[CloudAuth] Authenticated with saved API key");
|
|
442
|
+
|
|
443
|
+
// Non-blocking validation — if the key is invalid the next model
|
|
444
|
+
// call will surface the error; we just log a warning here.
|
|
445
|
+
this.validateApiKey(key)
|
|
446
|
+
.then((valid) => {
|
|
447
|
+
if (!valid) {
|
|
448
|
+
logger.warn(
|
|
449
|
+
"[CloudAuth] Saved API key could not be validated (cloud may be unreachable or key revoked) — model calls will use the key anyway"
|
|
450
|
+
);
|
|
451
|
+
}
|
|
452
|
+
})
|
|
453
|
+
.catch(() => {
|
|
454
|
+
// Swallow — already logged inside validateApiKey
|
|
455
|
+
});
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// Device-based auto-signup when explicitly enabled
|
|
460
|
+
const enabled = this.runtime.getSetting("ELIZAOS_CLOUD_ENABLED");
|
|
461
|
+
if (enabled === "true" || enabled === "1") {
|
|
462
|
+
try {
|
|
463
|
+
await this.authenticateWithDevice();
|
|
464
|
+
} catch (err) {
|
|
465
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
466
|
+
logger.warn(`[CloudAuth] Device auth failed (cloud may be unreachable): ${msg}`);
|
|
467
|
+
logger.info(
|
|
468
|
+
"[CloudAuth] Service will start unauthenticated — cloud features disabled until connectivity is restored"
|
|
469
|
+
);
|
|
470
|
+
}
|
|
471
|
+
} else {
|
|
472
|
+
logger.info("[CloudAuth] Cloud not enabled (set ELIZAOS_CLOUD_ENABLED=true)");
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
private async validateApiKey(key: string): Promise<boolean> {
|
|
477
|
+
try {
|
|
478
|
+
const validationClient = new CloudApiClient(this.client.getBaseUrl(), key);
|
|
479
|
+
await validationClient.get("/models", { timeoutMs: 10_000 });
|
|
480
|
+
return true;
|
|
481
|
+
} catch (err) {
|
|
482
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
483
|
+
logger.warn(`[CloudAuth] Could not reach cloud API to validate key: ${msg}`);
|
|
484
|
+
return false;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* Free-tier device auto-signup. **Convenience only — not a security
|
|
490
|
+
* primitive.** The hardware fingerprint is treated as opaque material the
|
|
491
|
+
* cloud signup endpoint can use to mint a fresh API key + $5 free credit
|
|
492
|
+
* for new installs. The result is usable for outbound LLM calls; it never
|
|
493
|
+
* authorizes inbound dashboard access.
|
|
494
|
+
*
|
|
495
|
+
* See `docs/security/remote-auth-hardening-plan.md` §7.
|
|
496
|
+
*/
|
|
497
|
+
async authenticateWithDevice(): Promise<CloudCredentials> {
|
|
498
|
+
const deviceId = await deriveDeviceId();
|
|
499
|
+
const platform = detectPlatform();
|
|
500
|
+
const appVersion = process.env.ELIZAOS_CLOUD_APP_VERSION ?? "2.0.0-beta.0";
|
|
501
|
+
const os = await import("node:os");
|
|
502
|
+
|
|
503
|
+
logger.info(`[CloudAuth] Authenticating device (platform=${platform})`);
|
|
504
|
+
|
|
505
|
+
const response = await this.client.postUnauthenticated<DeviceAuthResponse>("/device-auth", {
|
|
506
|
+
deviceId,
|
|
507
|
+
platform,
|
|
508
|
+
appVersion,
|
|
509
|
+
deviceName: os.hostname(),
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
this.credentials = {
|
|
513
|
+
apiKey: response.data.apiKey,
|
|
514
|
+
userId: response.data.userId,
|
|
515
|
+
organizationId: response.data.organizationId,
|
|
516
|
+
authenticatedAt: Date.now(),
|
|
517
|
+
};
|
|
518
|
+
this.client.setApiKey(response.data.apiKey);
|
|
519
|
+
|
|
520
|
+
const action = response.data.isNew ? "New account created" : "Authenticated";
|
|
521
|
+
logger.info(`[CloudAuth] ${action} (credits: $${response.data.credits.toFixed(2)})`);
|
|
522
|
+
|
|
523
|
+
return this.credentials;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
authenticateWithApiKey(input: ApiKeyAuthInput): CloudCredentials {
|
|
527
|
+
const apiKey = input.apiKey.trim();
|
|
528
|
+
if (!apiKey) {
|
|
529
|
+
throw new Error("Eliza Cloud API key is required");
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
this.client.setApiKey(apiKey);
|
|
533
|
+
this.credentials = {
|
|
534
|
+
apiKey,
|
|
535
|
+
userId: input.userId ?? "",
|
|
536
|
+
organizationId: input.organizationId ?? "",
|
|
537
|
+
authenticatedAt: Date.now(),
|
|
538
|
+
};
|
|
539
|
+
|
|
540
|
+
logger.info("[CloudAuth] Authenticated with API key");
|
|
541
|
+
return this.credentials;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
clearAuth(): void {
|
|
545
|
+
this.credentials = null;
|
|
546
|
+
this.client.setApiKey(undefined);
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
isAuthenticated(): boolean {
|
|
550
|
+
return this.credentials !== null;
|
|
551
|
+
}
|
|
552
|
+
getCredentials(): CloudCredentials | null {
|
|
553
|
+
return this.credentials;
|
|
554
|
+
}
|
|
555
|
+
getApiKey(): string | undefined {
|
|
556
|
+
return this.credentials?.apiKey ?? this.client.getApiKey();
|
|
557
|
+
}
|
|
558
|
+
getClient(): CloudApiClient {
|
|
559
|
+
return this.client;
|
|
560
|
+
}
|
|
561
|
+
getUserId(): string | undefined {
|
|
562
|
+
return this.credentials?.userId;
|
|
563
|
+
}
|
|
564
|
+
getOrganizationId(): string | undefined {
|
|
565
|
+
return this.credentials?.organizationId;
|
|
566
|
+
}
|
|
567
|
+
}
|