@elizaos/agent 0.25.8 → 2.0.0-alpha.83
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 +1 -1
- package/package.json +994 -34
- package/packages/agent/src/actions/emote.d.ts +14 -0
- package/packages/agent/src/actions/emote.d.ts.map +1 -0
- package/packages/agent/src/actions/emote.js +91 -0
- package/packages/agent/src/actions/restart.d.ts +19 -0
- package/packages/agent/src/actions/restart.d.ts.map +1 -0
- package/packages/agent/src/actions/restart.js +86 -0
- package/packages/agent/src/actions/send-message.d.ts +3 -0
- package/packages/agent/src/actions/send-message.d.ts.map +1 -0
- package/packages/agent/src/actions/send-message.js +144 -0
- package/packages/agent/src/actions/stream-control.d.ts +15 -0
- package/packages/agent/src/actions/stream-control.d.ts.map +1 -0
- package/packages/agent/src/actions/stream-control.js +357 -0
- package/packages/agent/src/actions/switch-stream-source.d.ts +16 -0
- package/packages/agent/src/actions/switch-stream-source.d.ts.map +1 -0
- package/packages/agent/src/actions/switch-stream-source.js +94 -0
- package/packages/agent/src/actions/terminal.d.ts +14 -0
- package/packages/agent/src/actions/terminal.d.ts.map +1 -0
- package/packages/agent/src/actions/terminal.js +154 -0
- package/packages/agent/src/api/agent-admin-routes.d.ts +38 -0
- package/packages/agent/src/api/agent-admin-routes.d.ts.map +1 -0
- package/packages/agent/src/api/agent-admin-routes.js +93 -0
- package/packages/agent/src/api/agent-lifecycle-routes.d.ts +16 -0
- package/packages/agent/src/api/agent-lifecycle-routes.d.ts.map +1 -0
- package/packages/agent/src/api/agent-lifecycle-routes.js +80 -0
- package/packages/agent/src/api/agent-model.d.ts +12 -0
- package/packages/agent/src/api/agent-model.d.ts.map +1 -0
- package/packages/agent/src/api/agent-model.js +123 -0
- package/packages/agent/src/api/agent-transfer-routes.d.ts +16 -0
- package/packages/agent/src/api/agent-transfer-routes.d.ts.map +1 -0
- package/packages/agent/src/api/agent-transfer-routes.js +124 -0
- package/packages/agent/src/api/apps-routes.d.ts +19 -0
- package/packages/agent/src/api/apps-routes.d.ts.map +1 -0
- package/packages/agent/src/api/apps-routes.js +128 -0
- package/packages/agent/src/api/auth-routes.d.ts +11 -0
- package/packages/agent/src/api/auth-routes.d.ts.map +1 -0
- package/packages/agent/src/api/auth-routes.js +54 -0
- package/packages/agent/src/api/bsc-trade.d.ts +34 -0
- package/packages/agent/src/api/bsc-trade.d.ts.map +1 -0
- package/packages/agent/src/api/bsc-trade.js +567 -0
- package/packages/agent/src/api/bug-report-routes.d.ts +7 -0
- package/packages/agent/src/api/bug-report-routes.d.ts.map +1 -0
- package/packages/agent/src/api/bug-report-routes.js +124 -0
- package/packages/agent/src/api/character-routes.d.ts +50 -0
- package/packages/agent/src/api/character-routes.d.ts.map +1 -0
- package/packages/agent/src/api/character-routes.js +302 -0
- package/packages/agent/src/api/cloud-billing-routes.d.ts +14 -0
- package/packages/agent/src/api/cloud-billing-routes.d.ts.map +1 -0
- package/packages/agent/src/api/cloud-billing-routes.js +400 -0
- package/packages/agent/src/api/cloud-compat-routes.d.ts +15 -0
- package/packages/agent/src/api/cloud-compat-routes.d.ts.map +1 -0
- package/packages/agent/src/api/cloud-compat-routes.js +131 -0
- package/packages/agent/src/api/cloud-routes.d.ts +62 -0
- package/packages/agent/src/api/cloud-routes.d.ts.map +1 -0
- package/packages/agent/src/api/cloud-routes.js +339 -0
- package/packages/agent/src/api/cloud-status-routes.d.ts +15 -0
- package/packages/agent/src/api/cloud-status-routes.d.ts.map +1 -0
- package/packages/agent/src/api/cloud-status-routes.js +165 -0
- package/packages/agent/src/api/compat-utils.d.ts +49 -0
- package/packages/agent/src/api/compat-utils.d.ts.map +1 -0
- package/packages/agent/src/api/compat-utils.js +126 -0
- package/packages/agent/src/api/connector-health.d.ts +34 -0
- package/packages/agent/src/api/connector-health.d.ts.map +1 -0
- package/packages/agent/src/api/connector-health.js +109 -0
- package/packages/agent/src/api/coordinator-wiring.d.ts +46 -0
- package/packages/agent/src/api/coordinator-wiring.d.ts.map +1 -0
- package/packages/agent/src/api/coordinator-wiring.js +101 -0
- package/packages/agent/src/api/credit-detection.d.ts +9 -0
- package/packages/agent/src/api/credit-detection.d.ts.map +1 -0
- package/packages/agent/src/api/credit-detection.js +41 -0
- package/packages/agent/src/api/database.d.ts +33 -0
- package/packages/agent/src/api/database.d.ts.map +1 -0
- package/packages/agent/src/api/database.js +1019 -0
- package/packages/agent/src/api/diagnostics-routes.d.ts +46 -0
- package/packages/agent/src/api/diagnostics-routes.d.ts.map +1 -0
- package/packages/agent/src/api/diagnostics-routes.js +241 -0
- package/packages/agent/src/api/drop-service.d.ts +26 -0
- package/packages/agent/src/api/drop-service.d.ts.map +1 -0
- package/packages/agent/src/api/drop-service.js +134 -0
- package/packages/agent/src/api/early-logs.d.ts +29 -0
- package/packages/agent/src/api/early-logs.d.ts.map +1 -0
- package/packages/agent/src/api/early-logs.js +96 -0
- package/packages/agent/src/api/http-helpers.d.ts +50 -0
- package/packages/agent/src/api/http-helpers.d.ts.map +1 -0
- package/packages/agent/src/api/http-helpers.js +145 -0
- package/packages/agent/src/api/index.d.ts +61 -0
- package/packages/agent/src/api/index.d.ts.map +1 -0
- package/packages/agent/src/api/index.js +59 -0
- package/packages/agent/src/api/knowledge-routes.d.ts +23 -0
- package/packages/agent/src/api/knowledge-routes.d.ts.map +1 -0
- package/packages/agent/src/api/knowledge-routes.js +931 -0
- package/packages/agent/src/api/knowledge-service-loader.d.ts +51 -0
- package/packages/agent/src/api/knowledge-service-loader.d.ts.map +1 -0
- package/packages/agent/src/api/knowledge-service-loader.js +34 -0
- package/packages/agent/src/api/memory-bounds.d.ts +51 -0
- package/packages/agent/src/api/memory-bounds.d.ts.map +1 -0
- package/packages/agent/src/api/memory-bounds.js +81 -0
- package/packages/agent/src/api/memory-routes.d.ts +9 -0
- package/packages/agent/src/api/memory-routes.d.ts.map +1 -0
- package/packages/agent/src/api/memory-routes.js +241 -0
- package/packages/agent/src/api/merkle-tree.d.ts +90 -0
- package/packages/agent/src/api/merkle-tree.d.ts.map +1 -0
- package/packages/agent/src/api/merkle-tree.js +174 -0
- package/packages/agent/src/api/models-routes.d.ts +14 -0
- package/packages/agent/src/api/models-routes.d.ts.map +1 -0
- package/packages/agent/src/api/models-routes.js +37 -0
- package/packages/agent/src/api/nfa-routes.d.ts +5 -0
- package/packages/agent/src/api/nfa-routes.d.ts.map +1 -0
- package/packages/agent/src/api/nfa-routes.js +125 -0
- package/packages/agent/src/api/og-tracker.d.ts +28 -0
- package/packages/agent/src/api/og-tracker.d.ts.map +1 -0
- package/packages/agent/src/api/og-tracker.js +60 -0
- package/packages/agent/src/api/parse-action-block.d.ts +36 -0
- package/packages/agent/src/api/parse-action-block.d.ts.map +1 -0
- package/packages/agent/src/api/parse-action-block.js +110 -0
- package/packages/agent/src/api/permissions-routes.d.ts +32 -0
- package/packages/agent/src/api/permissions-routes.d.ts.map +1 -0
- package/packages/agent/src/api/permissions-routes.js +149 -0
- package/packages/agent/src/api/plugin-validation.d.ts +86 -0
- package/packages/agent/src/api/plugin-validation.d.ts.map +1 -0
- package/packages/agent/src/api/plugin-validation.js +259 -0
- package/packages/agent/src/api/provider-switch-config.d.ts +37 -0
- package/packages/agent/src/api/provider-switch-config.d.ts.map +1 -0
- package/packages/agent/src/api/provider-switch-config.js +317 -0
- package/packages/agent/src/api/registry-routes.d.ts +26 -0
- package/packages/agent/src/api/registry-routes.d.ts.map +1 -0
- package/packages/agent/src/api/registry-routes.js +90 -0
- package/packages/agent/src/api/registry-service.d.ts +77 -0
- package/packages/agent/src/api/registry-service.d.ts.map +1 -0
- package/packages/agent/src/api/registry-service.js +190 -0
- package/packages/agent/src/api/route-helpers.d.ts +16 -0
- package/packages/agent/src/api/route-helpers.d.ts.map +1 -0
- package/packages/agent/src/api/route-helpers.js +1 -0
- package/packages/agent/src/api/sandbox-routes.d.ts +12 -0
- package/packages/agent/src/api/sandbox-routes.d.ts.map +1 -0
- package/packages/agent/src/api/sandbox-routes.js +1334 -0
- package/packages/agent/src/api/server.d.ts +418 -0
- package/packages/agent/src/api/server.d.ts.map +1 -0
- package/packages/agent/src/api/server.js +13614 -0
- package/packages/agent/src/api/signal-routes.d.ts +39 -0
- package/packages/agent/src/api/signal-routes.d.ts.map +1 -0
- package/packages/agent/src/api/signal-routes.js +168 -0
- package/packages/agent/src/api/stream-persistence.d.ts +64 -0
- package/packages/agent/src/api/stream-persistence.d.ts.map +1 -0
- package/packages/agent/src/api/stream-persistence.js +231 -0
- package/packages/agent/src/api/stream-route-state.d.ts +50 -0
- package/packages/agent/src/api/stream-route-state.d.ts.map +1 -0
- package/packages/agent/src/api/stream-route-state.js +1 -0
- package/packages/agent/src/api/stream-routes.d.ts +45 -0
- package/packages/agent/src/api/stream-routes.d.ts.map +1 -0
- package/packages/agent/src/api/stream-routes.js +809 -0
- package/packages/agent/src/api/stream-voice-routes.d.ts +36 -0
- package/packages/agent/src/api/stream-voice-routes.d.ts.map +1 -0
- package/packages/agent/src/api/stream-voice-routes.js +133 -0
- package/packages/agent/src/api/streaming-text.d.ts +9 -0
- package/packages/agent/src/api/streaming-text.d.ts.map +1 -0
- package/packages/agent/src/api/streaming-text.js +85 -0
- package/packages/agent/src/api/streaming-types.d.ts +30 -0
- package/packages/agent/src/api/streaming-types.d.ts.map +1 -0
- package/packages/agent/src/api/streaming-types.js +1 -0
- package/packages/agent/src/api/subscription-routes.d.ts +20 -0
- package/packages/agent/src/api/subscription-routes.d.ts.map +1 -0
- package/packages/agent/src/api/subscription-routes.js +191 -0
- package/packages/agent/src/api/terminal-run-limits.d.ts +5 -0
- package/packages/agent/src/api/terminal-run-limits.d.ts.map +1 -0
- package/packages/agent/src/api/terminal-run-limits.js +22 -0
- package/packages/agent/src/api/training-backend-check.d.ts +8 -0
- package/packages/agent/src/api/training-backend-check.d.ts.map +1 -0
- package/packages/agent/src/api/training-backend-check.js +28 -0
- package/packages/agent/src/api/training-routes.d.ts +44 -0
- package/packages/agent/src/api/training-routes.d.ts.map +1 -0
- package/packages/agent/src/api/training-routes.js +195 -0
- package/packages/agent/src/api/training-service-like.d.ts +38 -0
- package/packages/agent/src/api/training-service-like.d.ts.map +1 -0
- package/packages/agent/src/api/training-service-like.js +1 -0
- package/packages/agent/src/api/trajectory-routes.d.ts +17 -0
- package/packages/agent/src/api/trajectory-routes.d.ts.map +1 -0
- package/packages/agent/src/api/trajectory-routes.js +405 -0
- package/packages/agent/src/api/trigger-routes.d.ts +72 -0
- package/packages/agent/src/api/trigger-routes.d.ts.map +1 -0
- package/packages/agent/src/api/trigger-routes.js +268 -0
- package/packages/agent/src/api/twitter-verify.d.ts +25 -0
- package/packages/agent/src/api/twitter-verify.d.ts.map +1 -0
- package/packages/agent/src/api/twitter-verify.js +168 -0
- package/packages/agent/src/api/tx-service.d.ts +47 -0
- package/packages/agent/src/api/tx-service.d.ts.map +1 -0
- package/packages/agent/src/api/tx-service.js +156 -0
- package/packages/agent/src/api/wallet-dex-prices.d.ts +43 -0
- package/packages/agent/src/api/wallet-dex-prices.d.ts.map +1 -0
- package/packages/agent/src/api/wallet-dex-prices.js +149 -0
- package/packages/agent/src/api/wallet-evm-balance.d.ts +65 -0
- package/packages/agent/src/api/wallet-evm-balance.d.ts.map +1 -0
- package/packages/agent/src/api/wallet-evm-balance.js +663 -0
- package/packages/agent/src/api/wallet-routes.d.ts +33 -0
- package/packages/agent/src/api/wallet-routes.d.ts.map +1 -0
- package/packages/agent/src/api/wallet-routes.js +292 -0
- package/packages/agent/src/api/wallet-rpc.d.ts +61 -0
- package/packages/agent/src/api/wallet-rpc.d.ts.map +1 -0
- package/packages/agent/src/api/wallet-rpc.js +367 -0
- package/packages/agent/src/api/wallet-trading-profile.d.ts +51 -0
- package/packages/agent/src/api/wallet-trading-profile.d.ts.map +1 -0
- package/packages/agent/src/api/wallet-trading-profile.js +547 -0
- package/packages/agent/src/api/wallet.d.ts +31 -0
- package/packages/agent/src/api/wallet.d.ts.map +1 -0
- package/packages/agent/src/api/wallet.js +513 -0
- package/packages/agent/src/api/whatsapp-routes.d.ts +39 -0
- package/packages/agent/src/api/whatsapp-routes.d.ts.map +1 -0
- package/packages/agent/src/api/whatsapp-routes.js +182 -0
- package/packages/agent/src/api/zip-utils.d.ts +8 -0
- package/packages/agent/src/api/zip-utils.d.ts.map +1 -0
- package/packages/agent/src/api/zip-utils.js +115 -0
- package/packages/agent/src/auth/anthropic.d.ts +25 -0
- package/packages/agent/src/auth/anthropic.d.ts.map +1 -0
- package/packages/agent/src/auth/anthropic.js +40 -0
- package/packages/agent/src/auth/apply-stealth.d.ts +8 -0
- package/packages/agent/src/auth/apply-stealth.d.ts.map +1 -0
- package/packages/agent/src/auth/apply-stealth.js +35 -0
- package/packages/agent/src/auth/claude-code-stealth.d.ts +2 -0
- package/packages/agent/src/auth/claude-code-stealth.d.ts.map +1 -0
- package/packages/agent/src/auth/claude-code-stealth.js +104 -0
- package/packages/agent/src/auth/credentials.d.ts +55 -0
- package/packages/agent/src/auth/credentials.d.ts.map +1 -0
- package/packages/agent/src/auth/credentials.js +182 -0
- package/packages/agent/src/auth/index.d.ts +7 -0
- package/packages/agent/src/auth/index.d.ts.map +1 -0
- package/packages/agent/src/auth/index.js +3 -0
- package/packages/agent/src/auth/openai-codex.d.ts +27 -0
- package/packages/agent/src/auth/openai-codex.d.ts.map +1 -0
- package/packages/agent/src/auth/openai-codex.js +72 -0
- package/packages/agent/src/auth/types.d.ts +18 -0
- package/packages/agent/src/auth/types.d.ts.map +1 -0
- package/packages/agent/src/auth/types.js +8 -0
- package/packages/agent/src/awareness/registry.d.ts +27 -0
- package/packages/agent/src/awareness/registry.d.ts.map +1 -0
- package/packages/agent/src/awareness/registry.js +161 -0
- package/packages/agent/src/benchmark-server.d.ts +2 -0
- package/packages/agent/src/benchmark-server.d.ts.map +1 -0
- package/packages/agent/src/benchmark-server.js +773 -0
- package/packages/agent/src/bin.d.ts +3 -0
- package/packages/agent/src/bin.d.ts.map +1 -0
- package/packages/agent/src/bin.js +6 -0
- package/packages/agent/src/cli/index.d.ts +2 -0
- package/packages/agent/src/cli/index.d.ts.map +1 -0
- package/packages/agent/src/cli/index.js +40 -0
- package/packages/agent/src/cli/parse-duration.d.ts +5 -0
- package/packages/agent/src/cli/parse-duration.d.ts.map +1 -0
- package/packages/agent/src/cli/parse-duration.js +27 -0
- package/packages/agent/src/cloud/auth.d.ts +19 -0
- package/packages/agent/src/cloud/auth.d.ts.map +1 -0
- package/packages/agent/src/cloud/auth.js +107 -0
- package/packages/agent/src/cloud/backup.d.ts +18 -0
- package/packages/agent/src/cloud/backup.d.ts.map +1 -0
- package/packages/agent/src/cloud/backup.js +42 -0
- package/packages/agent/src/cloud/base-url.d.ts +3 -0
- package/packages/agent/src/cloud/base-url.d.ts.map +1 -0
- package/packages/agent/src/cloud/base-url.js +40 -0
- package/packages/agent/src/cloud/bridge-client.d.ts +56 -0
- package/packages/agent/src/cloud/bridge-client.d.ts.map +1 -0
- package/packages/agent/src/cloud/bridge-client.js +190 -0
- package/packages/agent/src/cloud/cloud-manager.d.ts +32 -0
- package/packages/agent/src/cloud/cloud-manager.d.ts.map +1 -0
- package/packages/agent/src/cloud/cloud-manager.js +119 -0
- package/packages/agent/src/cloud/cloud-proxy.d.ts +20 -0
- package/packages/agent/src/cloud/cloud-proxy.d.ts.map +1 -0
- package/packages/agent/src/cloud/cloud-proxy.js +34 -0
- package/packages/agent/src/cloud/index.d.ts +7 -0
- package/packages/agent/src/cloud/index.d.ts.map +1 -0
- package/packages/agent/src/cloud/index.js +6 -0
- package/packages/agent/src/cloud/reconnect.d.ts +26 -0
- package/packages/agent/src/cloud/reconnect.d.ts.map +1 -0
- package/packages/agent/src/cloud/reconnect.js +86 -0
- package/packages/agent/src/cloud/validate-url.d.ts +2 -0
- package/packages/agent/src/cloud/validate-url.d.ts.map +1 -0
- package/packages/agent/src/cloud/validate-url.js +162 -0
- package/packages/agent/src/config/character-schema.d.ts +25 -0
- package/packages/agent/src/config/character-schema.d.ts.map +1 -0
- package/packages/agent/src/config/character-schema.js +39 -0
- package/packages/agent/src/config/config.d.ts +6 -0
- package/packages/agent/src/config/config.d.ts.map +1 -0
- package/packages/agent/src/config/config.js +118 -0
- package/packages/agent/src/config/env-vars.d.ts +3 -0
- package/packages/agent/src/config/env-vars.d.ts.map +1 -0
- package/packages/agent/src/config/env-vars.js +76 -0
- package/packages/agent/src/config/includes.d.ts +26 -0
- package/packages/agent/src/config/includes.d.ts.map +1 -0
- package/packages/agent/src/config/includes.js +148 -0
- package/packages/agent/src/config/index.d.ts +16 -0
- package/packages/agent/src/config/index.d.ts.map +1 -0
- package/packages/agent/src/config/index.js +15 -0
- package/packages/agent/src/config/object-utils.d.ts +2 -0
- package/packages/agent/src/config/object-utils.d.ts.map +1 -0
- package/packages/agent/src/config/object-utils.js +6 -0
- package/packages/agent/src/config/paths.d.ts +13 -0
- package/packages/agent/src/config/paths.d.ts.map +1 -0
- package/packages/agent/src/config/paths.js +67 -0
- package/packages/agent/src/config/plugin-auto-enable.d.ts +16 -0
- package/packages/agent/src/config/plugin-auto-enable.d.ts.map +1 -0
- package/packages/agent/src/config/plugin-auto-enable.js +384 -0
- package/packages/agent/src/config/schema.d.ts +87 -0
- package/packages/agent/src/config/schema.d.ts.map +1 -0
- package/packages/agent/src/config/schema.js +928 -0
- package/packages/agent/src/config/telegram-custom-commands.d.ts +25 -0
- package/packages/agent/src/config/telegram-custom-commands.d.ts.map +1 -0
- package/packages/agent/src/config/telegram-custom-commands.js +71 -0
- package/packages/agent/src/config/types.agent-defaults.d.ts +331 -0
- package/packages/agent/src/config/types.agent-defaults.d.ts.map +1 -0
- package/packages/agent/src/config/types.agent-defaults.js +1 -0
- package/packages/agent/src/config/types.agents.d.ts +110 -0
- package/packages/agent/src/config/types.agents.d.ts.map +1 -0
- package/packages/agent/src/config/types.agents.js +1 -0
- package/packages/agent/src/config/types.d.ts +8 -0
- package/packages/agent/src/config/types.d.ts.map +1 -0
- package/packages/agent/src/config/types.eliza.d.ts +636 -0
- package/packages/agent/src/config/types.eliza.d.ts.map +1 -0
- package/packages/agent/src/config/types.eliza.js +1 -0
- package/packages/agent/src/config/types.gateway.d.ts +216 -0
- package/packages/agent/src/config/types.gateway.d.ts.map +1 -0
- package/packages/agent/src/config/types.gateway.js +1 -0
- package/packages/agent/src/config/types.hooks.d.ts +107 -0
- package/packages/agent/src/config/types.hooks.d.ts.map +1 -0
- package/packages/agent/src/config/types.hooks.js +1 -0
- package/packages/agent/src/config/types.js +7 -0
- package/packages/agent/src/config/types.messages.d.ts +176 -0
- package/packages/agent/src/config/types.messages.d.ts.map +1 -0
- package/packages/agent/src/config/types.messages.js +1 -0
- package/packages/agent/src/config/types.tools.d.ts +400 -0
- package/packages/agent/src/config/types.tools.d.ts.map +1 -0
- package/packages/agent/src/config/types.tools.js +1 -0
- package/packages/agent/src/config/zod-schema.agent-runtime.d.ts +1062 -0
- package/packages/agent/src/config/zod-schema.agent-runtime.d.ts.map +1 -0
- package/packages/agent/src/config/zod-schema.agent-runtime.js +721 -0
- package/packages/agent/src/config/zod-schema.core.d.ts +1021 -0
- package/packages/agent/src/config/zod-schema.core.d.ts.map +1 -0
- package/packages/agent/src/config/zod-schema.core.js +694 -0
- package/packages/agent/src/config/zod-schema.d.ts +4817 -0
- package/packages/agent/src/config/zod-schema.d.ts.map +1 -0
- package/packages/agent/src/config/zod-schema.hooks.d.ts +88 -0
- package/packages/agent/src/config/zod-schema.hooks.d.ts.map +1 -0
- package/packages/agent/src/config/zod-schema.hooks.js +133 -0
- package/packages/agent/src/config/zod-schema.js +778 -0
- package/packages/agent/src/config/zod-schema.providers-core.d.ts +2976 -0
- package/packages/agent/src/config/zod-schema.providers-core.d.ts.map +1 -0
- package/packages/agent/src/config/zod-schema.providers-core.js +1006 -0
- package/packages/agent/src/config/zod-schema.session.d.ts +183 -0
- package/packages/agent/src/config/zod-schema.session.d.ts.map +1 -0
- package/packages/agent/src/config/zod-schema.session.js +86 -0
- package/packages/agent/src/contracts/apps.d.ts +42 -0
- package/packages/agent/src/contracts/apps.d.ts.map +1 -0
- package/packages/agent/src/contracts/apps.js +4 -0
- package/packages/agent/src/contracts/awareness.d.ts +38 -0
- package/packages/agent/src/contracts/awareness.d.ts.map +1 -0
- package/packages/agent/src/contracts/awareness.js +7 -0
- package/packages/agent/src/contracts/config.d.ts +146 -0
- package/packages/agent/src/contracts/config.d.ts.map +1 -0
- package/packages/agent/src/contracts/config.js +4 -0
- package/packages/agent/src/contracts/drop.d.ts +20 -0
- package/packages/agent/src/contracts/drop.d.ts.map +1 -0
- package/packages/agent/src/contracts/drop.js +4 -0
- package/packages/agent/src/contracts/index.d.ts +9 -0
- package/packages/agent/src/contracts/index.d.ts.map +1 -0
- package/packages/agent/src/contracts/index.js +8 -0
- package/packages/agent/src/contracts/onboarding.d.ts +379 -0
- package/packages/agent/src/contracts/onboarding.d.ts.map +1 -0
- package/packages/agent/src/contracts/onboarding.js +290 -0
- package/packages/agent/src/contracts/permissions.d.ts +35 -0
- package/packages/agent/src/contracts/permissions.d.ts.map +1 -0
- package/packages/agent/src/contracts/permissions.js +4 -0
- package/packages/agent/src/contracts/verification.d.ts +9 -0
- package/packages/agent/src/contracts/verification.d.ts.map +1 -0
- package/packages/agent/src/contracts/verification.js +4 -0
- package/packages/agent/src/contracts/wallet.d.ts +409 -0
- package/packages/agent/src/contracts/wallet.d.ts.map +1 -0
- package/packages/agent/src/contracts/wallet.js +60 -0
- package/packages/agent/src/diagnostics/integration-observability.d.ts +40 -0
- package/packages/agent/src/diagnostics/integration-observability.d.ts.map +1 -0
- package/packages/agent/src/diagnostics/integration-observability.js +68 -0
- package/packages/agent/src/emotes/catalog.d.ts +31 -0
- package/packages/agent/src/emotes/catalog.d.ts.map +1 -0
- package/packages/agent/src/emotes/catalog.js +618 -0
- package/packages/agent/src/hooks/discovery.d.ts +13 -0
- package/packages/agent/src/hooks/discovery.d.ts.map +1 -0
- package/packages/agent/src/hooks/discovery.js +184 -0
- package/packages/agent/src/hooks/eligibility.d.ts +12 -0
- package/packages/agent/src/hooks/eligibility.d.ts.map +1 -0
- package/packages/agent/src/hooks/eligibility.js +100 -0
- package/packages/agent/src/hooks/index.d.ts +3 -0
- package/packages/agent/src/hooks/index.d.ts.map +1 -0
- package/packages/agent/src/hooks/index.js +2 -0
- package/packages/agent/src/hooks/loader.d.ts +34 -0
- package/packages/agent/src/hooks/loader.d.ts.map +1 -0
- package/packages/agent/src/hooks/loader.js +176 -0
- package/packages/agent/src/hooks/registry.d.ts +11 -0
- package/packages/agent/src/hooks/registry.d.ts.map +1 -0
- package/packages/agent/src/hooks/registry.js +58 -0
- package/packages/agent/src/hooks/types.d.ts +104 -0
- package/packages/agent/src/hooks/types.d.ts.map +1 -0
- package/packages/agent/src/hooks/types.js +8 -0
- package/packages/agent/src/index.d.ts +20 -0
- package/packages/agent/src/index.d.ts.map +1 -0
- package/packages/agent/src/index.js +19 -0
- package/packages/agent/src/onboarding-presets.d.ts +78 -0
- package/packages/agent/src/onboarding-presets.d.ts.map +1 -0
- package/packages/agent/src/onboarding-presets.js +1352 -0
- package/packages/agent/src/plugins/custom-rtmp/index.d.ts +12 -0
- package/packages/agent/src/plugins/custom-rtmp/index.d.ts.map +1 -0
- package/packages/agent/src/plugins/custom-rtmp/index.js +26 -0
- package/packages/agent/src/providers/admin-trust.d.ts +4 -0
- package/packages/agent/src/providers/admin-trust.d.ts.map +1 -0
- package/packages/agent/src/providers/admin-trust.js +53 -0
- package/packages/agent/src/providers/session-bridge.d.ts +24 -0
- package/packages/agent/src/providers/session-bridge.d.ts.map +1 -0
- package/packages/agent/src/providers/session-bridge.js +85 -0
- package/packages/agent/src/providers/session-utils.d.ts +20 -0
- package/packages/agent/src/providers/session-utils.d.ts.map +1 -0
- package/packages/agent/src/providers/session-utils.js +33 -0
- package/packages/agent/src/providers/simple-mode.d.ts +4 -0
- package/packages/agent/src/providers/simple-mode.d.ts.map +1 -0
- package/packages/agent/src/providers/simple-mode.js +85 -0
- package/packages/agent/src/providers/ui-catalog.d.ts +3 -0
- package/packages/agent/src/providers/ui-catalog.d.ts.map +1 -0
- package/packages/agent/src/providers/ui-catalog.js +123 -0
- package/packages/agent/src/providers/workspace-provider.d.ts +22 -0
- package/packages/agent/src/providers/workspace-provider.d.ts.map +1 -0
- package/packages/agent/src/providers/workspace-provider.js +167 -0
- package/packages/agent/src/providers/workspace.d.ts +54 -0
- package/packages/agent/src/providers/workspace.d.ts.map +1 -0
- package/packages/agent/src/providers/workspace.js +405 -0
- package/packages/agent/src/runtime/agent-event-service.d.ts +35 -0
- package/packages/agent/src/runtime/agent-event-service.d.ts.map +1 -0
- package/packages/agent/src/runtime/agent-event-service.js +16 -0
- package/packages/agent/src/runtime/cloud-onboarding.d.ts +55 -0
- package/packages/agent/src/runtime/cloud-onboarding.d.ts.map +1 -0
- package/packages/agent/src/runtime/cloud-onboarding.js +279 -0
- package/packages/agent/src/runtime/core-plugins.d.ts +14 -0
- package/packages/agent/src/runtime/core-plugins.d.ts.map +1 -0
- package/packages/agent/src/runtime/core-plugins.js +51 -0
- package/packages/agent/src/runtime/custom-actions.d.ts +40 -0
- package/packages/agent/src/runtime/custom-actions.d.ts.map +1 -0
- package/packages/agent/src/runtime/custom-actions.js +454 -0
- package/packages/agent/src/runtime/eliza-plugin.d.ts +16 -0
- package/packages/agent/src/runtime/eliza-plugin.d.ts.map +1 -0
- package/packages/agent/src/runtime/eliza-plugin.js +108 -0
- package/packages/agent/src/runtime/eliza.d.ts +205 -0
- package/packages/agent/src/runtime/eliza.d.ts.map +1 -0
- package/packages/agent/src/runtime/eliza.js +3935 -0
- package/packages/agent/src/runtime/embedding-presets.d.ts +19 -0
- package/packages/agent/src/runtime/embedding-presets.d.ts.map +1 -0
- package/packages/agent/src/runtime/embedding-presets.js +53 -0
- package/packages/agent/src/runtime/index.d.ts +9 -0
- package/packages/agent/src/runtime/index.d.ts.map +1 -0
- package/packages/agent/src/runtime/index.js +8 -0
- package/packages/agent/src/runtime/onboarding-names.d.ts +11 -0
- package/packages/agent/src/runtime/onboarding-names.d.ts.map +1 -0
- package/packages/agent/src/runtime/onboarding-names.js +74 -0
- package/packages/agent/src/runtime/release-plugin-policy.d.ts +20 -0
- package/packages/agent/src/runtime/release-plugin-policy.d.ts.map +1 -0
- package/packages/agent/src/runtime/release-plugin-policy.js +87 -0
- package/packages/agent/src/runtime/restart.d.ts +45 -0
- package/packages/agent/src/runtime/restart.d.ts.map +1 -0
- package/packages/agent/src/runtime/restart.js +45 -0
- package/packages/agent/src/runtime/trajectory-persistence.d.ts +214 -0
- package/packages/agent/src/runtime/trajectory-persistence.d.ts.map +1 -0
- package/packages/agent/src/runtime/trajectory-persistence.js +1957 -0
- package/packages/agent/src/runtime/version.d.ts +2 -0
- package/packages/agent/src/runtime/version.d.ts.map +1 -0
- package/packages/agent/src/runtime/version.js +5 -0
- package/packages/agent/src/security/audit-log.d.ts +49 -0
- package/packages/agent/src/security/audit-log.d.ts.map +1 -0
- package/packages/agent/src/security/audit-log.js +161 -0
- package/packages/agent/src/security/network-policy.d.ts +6 -0
- package/packages/agent/src/security/network-policy.d.ts.map +1 -0
- package/packages/agent/src/security/network-policy.js +85 -0
- package/packages/agent/src/server/index.d.ts +3 -0
- package/packages/agent/src/server/index.d.ts.map +1 -0
- package/packages/agent/src/server/index.js +1 -0
- package/packages/agent/src/services/agent-export.d.ts +100 -0
- package/packages/agent/src/services/agent-export.d.ts.map +1 -0
- package/packages/agent/src/services/agent-export.js +729 -0
- package/packages/agent/src/services/app-manager.d.ts +34 -0
- package/packages/agent/src/services/app-manager.d.ts.map +1 -0
- package/packages/agent/src/services/app-manager.js +425 -0
- package/packages/agent/src/services/browser-capture.d.ts +39 -0
- package/packages/agent/src/services/browser-capture.d.ts.map +1 -0
- package/packages/agent/src/services/browser-capture.js +162 -0
- package/packages/agent/src/services/coding-agent-context.d.ts +310 -0
- package/packages/agent/src/services/coding-agent-context.d.ts.map +1 -0
- package/packages/agent/src/services/coding-agent-context.js +281 -0
- package/packages/agent/src/services/fallback-training-service.d.ts +78 -0
- package/packages/agent/src/services/fallback-training-service.d.ts.map +1 -0
- package/packages/agent/src/services/fallback-training-service.js +126 -0
- package/packages/agent/src/services/index.d.ts +18 -0
- package/packages/agent/src/services/index.d.ts.map +1 -0
- package/packages/agent/src/services/index.js +17 -0
- package/packages/agent/src/services/mcp-marketplace.d.ts +89 -0
- package/packages/agent/src/services/mcp-marketplace.d.ts.map +1 -0
- package/packages/agent/src/services/mcp-marketplace.js +200 -0
- package/packages/agent/src/services/plugin-manager-types.d.ts +139 -0
- package/packages/agent/src/services/plugin-manager-types.d.ts.map +1 -0
- package/packages/agent/src/services/plugin-manager-types.js +18 -0
- package/packages/agent/src/services/privy-wallets.d.ts +18 -0
- package/packages/agent/src/services/privy-wallets.d.ts.map +1 -0
- package/packages/agent/src/services/privy-wallets.js +225 -0
- package/packages/agent/src/services/registry-client-app-meta.d.ts +6 -0
- package/packages/agent/src/services/registry-client-app-meta.d.ts.map +1 -0
- package/packages/agent/src/services/registry-client-app-meta.js +147 -0
- package/packages/agent/src/services/registry-client-endpoints.d.ts +7 -0
- package/packages/agent/src/services/registry-client-endpoints.d.ts.map +1 -0
- package/packages/agent/src/services/registry-client-endpoints.js +183 -0
- package/packages/agent/src/services/registry-client-local.d.ts +4 -0
- package/packages/agent/src/services/registry-client-local.d.ts.map +1 -0
- package/packages/agent/src/services/registry-client-local.js +377 -0
- package/packages/agent/src/services/registry-client-network.d.ts +9 -0
- package/packages/agent/src/services/registry-client-network.d.ts.map +1 -0
- package/packages/agent/src/services/registry-client-network.js +109 -0
- package/packages/agent/src/services/registry-client-queries.d.ts +15 -0
- package/packages/agent/src/services/registry-client-queries.d.ts.map +1 -0
- package/packages/agent/src/services/registry-client-queries.js +150 -0
- package/packages/agent/src/services/registry-client-types.d.ts +115 -0
- package/packages/agent/src/services/registry-client-types.d.ts.map +1 -0
- package/packages/agent/src/services/registry-client-types.js +1 -0
- package/packages/agent/src/services/registry-client.d.ts +39 -0
- package/packages/agent/src/services/registry-client.d.ts.map +1 -0
- package/packages/agent/src/services/registry-client.js +249 -0
- package/packages/agent/src/services/remote-signing-service.d.ts +58 -0
- package/packages/agent/src/services/remote-signing-service.d.ts.map +1 -0
- package/packages/agent/src/services/remote-signing-service.js +185 -0
- package/packages/agent/src/services/sandbox-engine.d.ts +96 -0
- package/packages/agent/src/services/sandbox-engine.d.ts.map +1 -0
- package/packages/agent/src/services/sandbox-engine.js +604 -0
- package/packages/agent/src/services/sandbox-manager.d.ts +104 -0
- package/packages/agent/src/services/sandbox-manager.d.ts.map +1 -0
- package/packages/agent/src/services/sandbox-manager.js +353 -0
- package/packages/agent/src/services/self-updater.d.ts +21 -0
- package/packages/agent/src/services/self-updater.d.ts.map +1 -0
- package/packages/agent/src/services/self-updater.js +162 -0
- package/packages/agent/src/services/signal-pairing.d.ts +37 -0
- package/packages/agent/src/services/signal-pairing.d.ts.map +1 -0
- package/packages/agent/src/services/signal-pairing.js +124 -0
- package/packages/agent/src/services/signing-policy.d.ts +44 -0
- package/packages/agent/src/services/signing-policy.d.ts.map +1 -0
- package/packages/agent/src/services/signing-policy.js +165 -0
- package/packages/agent/src/services/skill-catalog-client.d.ts +47 -0
- package/packages/agent/src/services/skill-catalog-client.d.ts.map +1 -0
- package/packages/agent/src/services/skill-catalog-client.js +130 -0
- package/packages/agent/src/services/skill-marketplace.d.ts +42 -0
- package/packages/agent/src/services/skill-marketplace.d.ts.map +1 -0
- package/packages/agent/src/services/skill-marketplace.js +680 -0
- package/packages/agent/src/services/stream-manager.d.ts +121 -0
- package/packages/agent/src/services/stream-manager.d.ts.map +1 -0
- package/packages/agent/src/services/stream-manager.js +604 -0
- package/packages/agent/src/services/tts-stream-bridge.d.ts +83 -0
- package/packages/agent/src/services/tts-stream-bridge.d.ts.map +1 -0
- package/packages/agent/src/services/tts-stream-bridge.js +349 -0
- package/packages/agent/src/services/update-checker.d.ts +29 -0
- package/packages/agent/src/services/update-checker.d.ts.map +1 -0
- package/packages/agent/src/services/update-checker.js +134 -0
- package/packages/agent/src/services/version-compat.d.ts +99 -0
- package/packages/agent/src/services/version-compat.d.ts.map +1 -0
- package/packages/agent/src/services/version-compat.js +195 -0
- package/packages/agent/src/services/whatsapp-pairing.d.ts +41 -0
- package/packages/agent/src/services/whatsapp-pairing.d.ts.map +1 -0
- package/packages/agent/src/services/whatsapp-pairing.js +209 -0
- package/packages/agent/src/shared/ui-catalog-prompt.d.ts +52 -0
- package/packages/agent/src/shared/ui-catalog-prompt.d.ts.map +1 -0
- package/packages/agent/src/shared/ui-catalog-prompt.js +1028 -0
- package/packages/agent/src/test-support/process-helpers.d.ts +13 -0
- package/packages/agent/src/test-support/process-helpers.d.ts.map +1 -0
- package/packages/agent/src/test-support/process-helpers.js +23 -0
- package/packages/agent/src/test-support/route-test-helpers.d.ts +37 -0
- package/packages/agent/src/test-support/route-test-helpers.d.ts.map +1 -0
- package/packages/agent/src/test-support/route-test-helpers.js +54 -0
- package/packages/agent/src/test-support/test-helpers.d.ts +77 -0
- package/packages/agent/src/test-support/test-helpers.d.ts.map +1 -0
- package/packages/agent/src/test-support/test-helpers.js +210 -0
- package/packages/agent/src/testing/index.d.ts +4 -0
- package/packages/agent/src/testing/index.d.ts.map +1 -0
- package/packages/agent/src/testing/index.js +3 -0
- package/packages/agent/src/triggers/action.d.ts +3 -0
- package/packages/agent/src/triggers/action.d.ts.map +1 -0
- package/packages/agent/src/triggers/action.js +267 -0
- package/packages/agent/src/triggers/runtime.d.ts +24 -0
- package/packages/agent/src/triggers/runtime.d.ts.map +1 -0
- package/packages/agent/src/triggers/runtime.js +322 -0
- package/packages/agent/src/triggers/scheduling.d.ts +70 -0
- package/packages/agent/src/triggers/scheduling.d.ts.map +1 -0
- package/packages/agent/src/triggers/scheduling.js +355 -0
- package/packages/agent/src/triggers/types.d.ts +115 -0
- package/packages/agent/src/triggers/types.d.ts.map +1 -0
- package/packages/agent/src/triggers/types.js +1 -0
- package/packages/agent/src/utils/exec-safety.d.ts +2 -0
- package/packages/agent/src/utils/exec-safety.d.ts.map +1 -0
- package/packages/agent/src/utils/exec-safety.js +21 -0
- package/packages/agent/src/utils/number-parsing.d.ts +26 -0
- package/packages/agent/src/utils/number-parsing.d.ts.map +1 -0
- package/packages/agent/src/utils/number-parsing.js +52 -0
- package/packages/agent/src/utils/spoken-text.d.ts +2 -0
- package/packages/agent/src/utils/spoken-text.d.ts.map +1 -0
- package/packages/agent/src/utils/spoken-text.js +56 -0
- package/packages/agent/src/version-resolver.d.ts +3 -0
- package/packages/agent/src/version-resolver.d.ts.map +1 -0
- package/packages/agent/src/version-resolver.js +51 -0
- package/jest.config.js +0 -17
- package/src/__tests__/client-type-identification.test.ts +0 -59
- package/src/defaultCharacter.ts +0 -530
- package/src/index.ts +0 -865
- package/tsconfig.json +0 -16
|
@@ -0,0 +1,1019 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Database management API handlers for the Eliza Control UI.
|
|
3
|
+
*
|
|
4
|
+
* Provides endpoints for:
|
|
5
|
+
* - Database provider configuration (PGLite vs Postgres)
|
|
6
|
+
* - Connection testing for remote Postgres
|
|
7
|
+
* - Table browsing and introspection
|
|
8
|
+
* - Row-level CRUD operations
|
|
9
|
+
* - Raw SQL query execution
|
|
10
|
+
* - Database status and health
|
|
11
|
+
*
|
|
12
|
+
* All data endpoints use the active runtime's database adapter (Drizzle ORM)
|
|
13
|
+
* so they work identically for both PGLite and Postgres.
|
|
14
|
+
*/
|
|
15
|
+
import dns from "node:dns";
|
|
16
|
+
import net from "node:net";
|
|
17
|
+
import { promisify } from "node:util";
|
|
18
|
+
import { logger } from "@elizaos/core";
|
|
19
|
+
import { loadElizaConfig, saveElizaConfig } from "../config/config";
|
|
20
|
+
import { isLoopbackHost, normalizeHostLike, normalizeIpForPolicy, } from "../security/network-policy";
|
|
21
|
+
import { readJsonBody as parseJsonBody, sendJson, sendJsonError, } from "./http-helpers";
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
// Helpers
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
async function readJsonBody(req, res) {
|
|
26
|
+
return parseJsonBody(req, res, {
|
|
27
|
+
maxBytes: 2 * 1024 * 1024,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Safely quote a SQL identifier (table or column name).
|
|
32
|
+
* Postgres uses double-quote escaping: embedded " becomes "".
|
|
33
|
+
*/
|
|
34
|
+
function quoteIdent(name) {
|
|
35
|
+
return `"${name.replace(/"/g, '""')}"`;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Build a Postgres connection string from individual credential fields.
|
|
39
|
+
*/
|
|
40
|
+
function buildConnectionString(creds) {
|
|
41
|
+
if (creds.connectionString)
|
|
42
|
+
return creds.connectionString;
|
|
43
|
+
const host = creds.host ?? "localhost";
|
|
44
|
+
const port = creds.port ?? 5432;
|
|
45
|
+
const user = encodeURIComponent(creds.user ?? "postgres");
|
|
46
|
+
const password = creds.password ? encodeURIComponent(creds.password) : "";
|
|
47
|
+
const database = creds.database ?? "postgres";
|
|
48
|
+
const auth = password ? `${user}:${password}` : user;
|
|
49
|
+
const sslParam = creds.ssl ? "?sslmode=require" : "";
|
|
50
|
+
return `postgresql://${auth}@${host}:${port}/${database}${sslParam}`;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Return a copy of credentials with host pinned to a validated IP address.
|
|
54
|
+
* For connection strings, rewrites URL hostname to avoid re-resolution later.
|
|
55
|
+
*/
|
|
56
|
+
function withPinnedHost(creds, pinnedHost) {
|
|
57
|
+
const normalizedPinned = pinnedHost.replace(/^::ffff:/i, "");
|
|
58
|
+
const next = { ...creds, host: normalizedPinned };
|
|
59
|
+
if (next.connectionString) {
|
|
60
|
+
try {
|
|
61
|
+
const parsed = new URL(next.connectionString);
|
|
62
|
+
parsed.hostname = normalizedPinned;
|
|
63
|
+
// Preserve DNS pinning even when libpq-style query params are present.
|
|
64
|
+
// `host` / `hostaddr` can override URI hostname; force both to pinned IP.
|
|
65
|
+
parsed.searchParams.set("host", normalizedPinned);
|
|
66
|
+
parsed.searchParams.set("hostaddr", normalizedPinned);
|
|
67
|
+
next.connectionString = parsed.toString();
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
// Validation has already parsed this once, but if URL rewriting fails,
|
|
71
|
+
// force builder path to use the pinned host.
|
|
72
|
+
delete next.connectionString;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return next;
|
|
76
|
+
}
|
|
77
|
+
// ---------------------------------------------------------------------------
|
|
78
|
+
// Host validation — prevent SSRF via database connection endpoints
|
|
79
|
+
// ---------------------------------------------------------------------------
|
|
80
|
+
const dnsLookupAll = promisify(dns.lookup);
|
|
81
|
+
/**
|
|
82
|
+
* IP ranges that are ALWAYS blocked regardless of bind address.
|
|
83
|
+
* Cloud metadata and "this" network are never legitimate Postgres targets.
|
|
84
|
+
*/
|
|
85
|
+
const ALWAYS_BLOCKED_IP_PATTERNS = [
|
|
86
|
+
/^169\.254\./, // Link-local / cloud metadata (AWS, GCP, Azure)
|
|
87
|
+
/^0\./, // "This" network
|
|
88
|
+
/^fe[89ab][0-9a-f]:/i, // IPv6 link-local fe80::/10
|
|
89
|
+
];
|
|
90
|
+
/**
|
|
91
|
+
* Private/internal IP ranges — blocked only when the API is bound to a
|
|
92
|
+
* non-loopback address (i.e. remotely reachable). When bound to 127.0.0.1
|
|
93
|
+
* (the default), these are allowed since local Postgres is the most common
|
|
94
|
+
* setup and an attacker who can reach the loopback API already has local
|
|
95
|
+
* network access.
|
|
96
|
+
*/
|
|
97
|
+
const PRIVATE_IP_PATTERNS = [
|
|
98
|
+
/^127\./, // IPv4 loopback
|
|
99
|
+
/^10\./, // RFC 1918 Class A
|
|
100
|
+
/^172\.(1[6-9]|2\d|3[01])\./, // RFC 1918 Class B
|
|
101
|
+
/^192\.168\./, // RFC 1918 Class C
|
|
102
|
+
/^::1$/, // IPv6 loopback
|
|
103
|
+
/^f[cd][0-9a-f]{2}:/i, // IPv6 ULA (fc00::/7 includes fc00::–fdff::)
|
|
104
|
+
];
|
|
105
|
+
/**
|
|
106
|
+
* Returns true when the API server is bound to a loopback-only address.
|
|
107
|
+
* In that case, private/internal IP ranges are allowed for DB connections
|
|
108
|
+
* since only local processes can reach the API.
|
|
109
|
+
*/
|
|
110
|
+
function isApiLoopbackOnly() {
|
|
111
|
+
let bind = (process.env.ELIZA_API_BIND ?? "127.0.0.1").trim().toLowerCase();
|
|
112
|
+
if (!bind)
|
|
113
|
+
bind = "127.0.0.1";
|
|
114
|
+
// Accept accidental URL-shaped bind values.
|
|
115
|
+
if (bind.startsWith("http://") || bind.startsWith("https://")) {
|
|
116
|
+
try {
|
|
117
|
+
const parsed = new URL(bind);
|
|
118
|
+
bind = parsed.hostname.toLowerCase();
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
// Fall through and treat as raw host value.
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
// [::1]:2138 -> ::1
|
|
125
|
+
const bracketedIpv6 = /^\[([^\]]+)\](?::\d+)?$/.exec(bind);
|
|
126
|
+
if (bracketedIpv6?.[1]) {
|
|
127
|
+
bind = bracketedIpv6[1];
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
// localhost:2138 -> localhost, 127.0.0.1:2138 -> 127.0.0.1
|
|
131
|
+
const singleColonHostPort = /^([^:]+):(\d+)$/.exec(bind);
|
|
132
|
+
if (singleColonHostPort?.[1]) {
|
|
133
|
+
bind = singleColonHostPort[1];
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
bind = bind.replace(/^\[|\]$/g, "");
|
|
137
|
+
// Reuse the strict loopback classifier to avoid hostname prefix bypasses
|
|
138
|
+
// such as "127.evil.com" that are not literal 127.0.0.0/8 IPs.
|
|
139
|
+
return isLoopbackHost(bind);
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Extract all potential hosts from a Postgres connection string or credentials object.
|
|
143
|
+
* Includes query params like ?host= and ?hostaddr= which Postgres clients honor.
|
|
144
|
+
* Returns empty array if no host can be determined.
|
|
145
|
+
*/
|
|
146
|
+
function extractHosts(creds) {
|
|
147
|
+
if (creds.connectionString) {
|
|
148
|
+
try {
|
|
149
|
+
const url = new URL(creds.connectionString);
|
|
150
|
+
const hosts = [];
|
|
151
|
+
// PostgreSQL connection strings can have ?host= param that overrides URI hostname
|
|
152
|
+
const hostParam = url.searchParams.get("host");
|
|
153
|
+
if (hostParam) {
|
|
154
|
+
hosts.push(...hostParam
|
|
155
|
+
.split(",")
|
|
156
|
+
.map((h) => normalizeHostLike(h))
|
|
157
|
+
.filter(Boolean));
|
|
158
|
+
}
|
|
159
|
+
// Also check hostaddr param
|
|
160
|
+
const hostAddrParam = url.searchParams.get("hostaddr");
|
|
161
|
+
if (hostAddrParam) {
|
|
162
|
+
hosts.push(...hostAddrParam
|
|
163
|
+
.split(",")
|
|
164
|
+
.map((h) => normalizeHostLike(h))
|
|
165
|
+
.filter(Boolean));
|
|
166
|
+
}
|
|
167
|
+
// Include URI hostname
|
|
168
|
+
if (url.hostname) {
|
|
169
|
+
hosts.push(normalizeHostLike(url.hostname));
|
|
170
|
+
}
|
|
171
|
+
return [...new Set(hosts)];
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
return []; // Unparseable — will be rejected
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
if (creds.host) {
|
|
178
|
+
const host = normalizeHostLike(creds.host);
|
|
179
|
+
return host ? [host] : [];
|
|
180
|
+
}
|
|
181
|
+
return [];
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Check whether an IP address falls in a blocked range.
|
|
185
|
+
* When the API is remotely reachable, private ranges are also blocked.
|
|
186
|
+
*/
|
|
187
|
+
function isBlockedIp(ip) {
|
|
188
|
+
const normalized = normalizeIpForPolicy(ip);
|
|
189
|
+
if (ALWAYS_BLOCKED_IP_PATTERNS.some((p) => p.test(normalized)))
|
|
190
|
+
return true;
|
|
191
|
+
if (!isApiLoopbackOnly() &&
|
|
192
|
+
PRIVATE_IP_PATTERNS.some((p) => p.test(normalized)))
|
|
193
|
+
return true;
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Validate that all target hosts do not resolve to blocked addresses.
|
|
198
|
+
*
|
|
199
|
+
* Performs DNS resolution to catch hostnames like `metadata.google.internal`
|
|
200
|
+
* or `169.254.169.254.nip.io` that resolve to link-local / cloud metadata
|
|
201
|
+
* IPs. Also handles IPv6-mapped IPv4 addresses (e.g. `::ffff:169.254.x.y`).
|
|
202
|
+
*
|
|
203
|
+
* Returns a validation result including a pinned host IP when successful.
|
|
204
|
+
*/
|
|
205
|
+
async function validateDbHost(creds, opts = {}) {
|
|
206
|
+
const hosts = extractHosts(creds);
|
|
207
|
+
if (hosts.length === 0) {
|
|
208
|
+
return {
|
|
209
|
+
error: "Could not determine target host from the provided credentials.",
|
|
210
|
+
pinnedHost: null,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
let pinnedHost = null;
|
|
214
|
+
for (const host of hosts) {
|
|
215
|
+
const literalNormalized = normalizeIpForPolicy(host);
|
|
216
|
+
// First check the literal host string (catches raw IPs without DNS lookup)
|
|
217
|
+
if (isBlockedIp(literalNormalized)) {
|
|
218
|
+
return {
|
|
219
|
+
error: `Connection to "${host}" is blocked: link-local and metadata addresses are not allowed.`,
|
|
220
|
+
pinnedHost: null,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
// Literal IPs are already pinned and do not require DNS.
|
|
224
|
+
if (net.isIP(literalNormalized)) {
|
|
225
|
+
if (!pinnedHost)
|
|
226
|
+
pinnedHost = literalNormalized;
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
// Resolve DNS and check all resulting IPs
|
|
230
|
+
try {
|
|
231
|
+
const results = await dnsLookupAll(host, { all: true });
|
|
232
|
+
const addresses = Array.isArray(results) ? results : [results];
|
|
233
|
+
for (const entry of addresses) {
|
|
234
|
+
const ip = typeof entry === "string"
|
|
235
|
+
? entry
|
|
236
|
+
: entry.address;
|
|
237
|
+
const normalized = normalizeIpForPolicy(ip);
|
|
238
|
+
if (isBlockedIp(normalized)) {
|
|
239
|
+
return {
|
|
240
|
+
error: `Connection to "${host}" is blocked: it resolves to ${ip} ` +
|
|
241
|
+
`which is a link-local or metadata address.`,
|
|
242
|
+
pinnedHost: null,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
if (!pinnedHost)
|
|
246
|
+
pinnedHost = normalized;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
catch {
|
|
250
|
+
// For "save config" flows we allow unresolved hostnames so users can
|
|
251
|
+
// persist remote endpoints that are only resolvable from their runtime
|
|
252
|
+
// network. For "test connection" flows we keep strict DNS requirements.
|
|
253
|
+
if (!opts.allowUnresolvedHostnames) {
|
|
254
|
+
return {
|
|
255
|
+
error: `Connection to "${host}" failed DNS resolution during validation. ` +
|
|
256
|
+
"Use a resolvable hostname or a literal IP address.",
|
|
257
|
+
pinnedHost: null,
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
if (!pinnedHost) {
|
|
263
|
+
if (opts.allowUnresolvedHostnames) {
|
|
264
|
+
return { error: null, pinnedHost: null };
|
|
265
|
+
}
|
|
266
|
+
return {
|
|
267
|
+
error: "Could not validate any host to a concrete IP address.",
|
|
268
|
+
pinnedHost: null,
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
return { error: null, pinnedHost };
|
|
272
|
+
}
|
|
273
|
+
/** Convert a JS value to a SQL literal for use in raw queries. */
|
|
274
|
+
function sqlLiteral(v) {
|
|
275
|
+
if (v === null || v === undefined)
|
|
276
|
+
return "NULL";
|
|
277
|
+
if (typeof v === "number")
|
|
278
|
+
return String(v);
|
|
279
|
+
if (typeof v === "boolean")
|
|
280
|
+
return v ? "TRUE" : "FALSE";
|
|
281
|
+
if (typeof v === "object")
|
|
282
|
+
return `'${JSON.stringify(v).replace(/'/g, "''")}'::jsonb`;
|
|
283
|
+
return `'${String(v).replace(/'/g, "''")}'`;
|
|
284
|
+
}
|
|
285
|
+
/** Build a "col = val" SQL assignment clause. */
|
|
286
|
+
function sqlAssign(col, val) {
|
|
287
|
+
if (val === null || val === undefined)
|
|
288
|
+
return `${quoteIdent(col)} = NULL`;
|
|
289
|
+
return `${quoteIdent(col)} = ${sqlLiteral(val)}`;
|
|
290
|
+
}
|
|
291
|
+
/** Build a "col = val" or "col IS NULL" SQL WHERE predicate. */
|
|
292
|
+
function sqlPredicate(col, val) {
|
|
293
|
+
if (val === null || val === undefined)
|
|
294
|
+
return `${quoteIdent(col)} IS NULL`;
|
|
295
|
+
return `${quoteIdent(col)} = ${sqlLiteral(val)}`;
|
|
296
|
+
}
|
|
297
|
+
// Cached drizzle-orm sql helper; resolved once on first call.
|
|
298
|
+
let _sqlHelper = null;
|
|
299
|
+
async function getDrizzleSql() {
|
|
300
|
+
if (!_sqlHelper) {
|
|
301
|
+
const drizzle = await import("drizzle-orm");
|
|
302
|
+
_sqlHelper = drizzle.sql;
|
|
303
|
+
}
|
|
304
|
+
return _sqlHelper;
|
|
305
|
+
}
|
|
306
|
+
/** Execute raw SQL via the runtime's Drizzle adapter. */
|
|
307
|
+
async function executeRawSql(runtime, sqlText) {
|
|
308
|
+
const drizzleSql = await getDrizzleSql();
|
|
309
|
+
const db = runtime.adapter.db;
|
|
310
|
+
const rawQuery = drizzleSql?.raw(sqlText);
|
|
311
|
+
if (!rawQuery)
|
|
312
|
+
throw new Error("SQL module not available");
|
|
313
|
+
const result = await db.execute(rawQuery);
|
|
314
|
+
const rows = Array.isArray(result.rows)
|
|
315
|
+
? result.rows
|
|
316
|
+
: result;
|
|
317
|
+
let columns = [];
|
|
318
|
+
if (result.fields && Array.isArray(result.fields)) {
|
|
319
|
+
columns = result.fields.map((f) => f.name);
|
|
320
|
+
}
|
|
321
|
+
else if (rows.length > 0) {
|
|
322
|
+
columns = Object.keys(rows[0]);
|
|
323
|
+
}
|
|
324
|
+
return { rows, columns };
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Detect the current database provider from environment / runtime state.
|
|
328
|
+
*/
|
|
329
|
+
function detectCurrentProvider() {
|
|
330
|
+
return process.env.POSTGRES_URL ? "postgres" : "pglite";
|
|
331
|
+
}
|
|
332
|
+
/** Verify a table name refers to a real user table. */
|
|
333
|
+
async function assertTableExists(runtime, tableName) {
|
|
334
|
+
const safe = tableName.replace(/'/g, "''");
|
|
335
|
+
const { rows } = await executeRawSql(runtime, `SELECT 1 FROM information_schema.tables
|
|
336
|
+
WHERE table_name = '${safe}'
|
|
337
|
+
AND table_schema NOT IN ('pg_catalog', 'information_schema')
|
|
338
|
+
AND table_type = 'BASE TABLE'
|
|
339
|
+
LIMIT 1`);
|
|
340
|
+
return rows.length > 0;
|
|
341
|
+
}
|
|
342
|
+
// ---------------------------------------------------------------------------
|
|
343
|
+
// Route handlers
|
|
344
|
+
// ---------------------------------------------------------------------------
|
|
345
|
+
/**
|
|
346
|
+
* GET /api/database/status
|
|
347
|
+
* Returns current connection status, provider, table count, version.
|
|
348
|
+
*/
|
|
349
|
+
async function handleGetStatus(_req, res, runtime) {
|
|
350
|
+
const provider = detectCurrentProvider();
|
|
351
|
+
if (!runtime?.adapter) {
|
|
352
|
+
sendJson(res, {
|
|
353
|
+
provider,
|
|
354
|
+
connected: false,
|
|
355
|
+
serverVersion: null,
|
|
356
|
+
tableCount: 0,
|
|
357
|
+
pgliteDataDir: process.env.PGLITE_DATA_DIR ?? null,
|
|
358
|
+
postgresHost: null,
|
|
359
|
+
});
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
const { rows } = await executeRawSql(runtime, "SELECT version()");
|
|
363
|
+
const serverVersion = rows.length > 0
|
|
364
|
+
? String(rows[0].version ?? "")
|
|
365
|
+
: null;
|
|
366
|
+
const tableResult = await executeRawSql(runtime, `SELECT count(*) AS cnt
|
|
367
|
+
FROM information_schema.tables
|
|
368
|
+
WHERE table_schema NOT IN ('pg_catalog', 'information_schema')
|
|
369
|
+
AND table_type = 'BASE TABLE'`);
|
|
370
|
+
const tableCount = tableResult.rows.length > 0
|
|
371
|
+
? Number(tableResult.rows[0].cnt ?? 0)
|
|
372
|
+
: 0;
|
|
373
|
+
const status = {
|
|
374
|
+
provider,
|
|
375
|
+
connected: true,
|
|
376
|
+
serverVersion,
|
|
377
|
+
tableCount,
|
|
378
|
+
pgliteDataDir: provider === "pglite" ? (process.env.PGLITE_DATA_DIR ?? null) : null,
|
|
379
|
+
postgresHost: provider === "postgres"
|
|
380
|
+
? (process.env.POSTGRES_URL?.replace(/^postgresql:\/\/[^@]*@/, "").replace(/\/.*$/, "") ?? null)
|
|
381
|
+
: null,
|
|
382
|
+
};
|
|
383
|
+
sendJson(res, status);
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* GET /api/database/config
|
|
387
|
+
* Returns the persisted database configuration from eliza.json.
|
|
388
|
+
*/
|
|
389
|
+
function handleGetConfig(_req, res) {
|
|
390
|
+
const config = loadElizaConfig();
|
|
391
|
+
const dbConfig = config.database ?? { provider: "pglite" };
|
|
392
|
+
// Mask the password in the response
|
|
393
|
+
const sanitized = { ...dbConfig };
|
|
394
|
+
if (sanitized.postgres?.password) {
|
|
395
|
+
sanitized.postgres = {
|
|
396
|
+
...sanitized.postgres,
|
|
397
|
+
password: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022",
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
if (sanitized.postgres?.connectionString) {
|
|
401
|
+
// Mask password in connection string
|
|
402
|
+
sanitized.postgres = {
|
|
403
|
+
...sanitized.postgres,
|
|
404
|
+
connectionString: sanitized.postgres.connectionString.replace(/:([^@]+)@/, ":\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022@"),
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
sendJson(res, {
|
|
408
|
+
config: sanitized,
|
|
409
|
+
activeProvider: detectCurrentProvider(),
|
|
410
|
+
needsRestart: (dbConfig.provider ?? "pglite") !== detectCurrentProvider(),
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* PUT /api/database/config
|
|
415
|
+
* Saves new database configuration. Does NOT restart the agent automatically;
|
|
416
|
+
* the UI prompts the user to restart.
|
|
417
|
+
*/
|
|
418
|
+
async function handlePutConfig(req, res) {
|
|
419
|
+
const body = await readJsonBody(req, res);
|
|
420
|
+
if (!body)
|
|
421
|
+
return;
|
|
422
|
+
// Validate
|
|
423
|
+
if (body.provider &&
|
|
424
|
+
body.provider !== "pglite" &&
|
|
425
|
+
body.provider !== "postgres") {
|
|
426
|
+
sendJsonError(res, `Invalid provider: ${String(body.provider)}. Must be "pglite" or "postgres".`);
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
// Load current config so validation can account for unchanged provider.
|
|
430
|
+
const config = loadElizaConfig();
|
|
431
|
+
const existingDb = config.database ?? {};
|
|
432
|
+
const effectiveProvider = body.provider ?? existingDb.provider ?? "pglite";
|
|
433
|
+
let validatedPostgres = null;
|
|
434
|
+
if (body.postgres) {
|
|
435
|
+
const pg = body.postgres;
|
|
436
|
+
if (effectiveProvider === "postgres" && !pg.connectionString && !pg.host) {
|
|
437
|
+
sendJsonError(res, "Postgres configuration requires either a connectionString or at least a host.");
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
const validation = await validateDbHost(pg, {
|
|
441
|
+
allowUnresolvedHostnames: Boolean(pg.connectionString),
|
|
442
|
+
});
|
|
443
|
+
if (validation.error) {
|
|
444
|
+
sendJsonError(res, validation.error);
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
validatedPostgres = validation.pinnedHost
|
|
448
|
+
? withPinnedHost(pg, validation.pinnedHost)
|
|
449
|
+
: pg;
|
|
450
|
+
}
|
|
451
|
+
// Merge: keep existing postgres/pglite sub-configs unless explicitly provided
|
|
452
|
+
const merged = {
|
|
453
|
+
...existingDb,
|
|
454
|
+
...body,
|
|
455
|
+
};
|
|
456
|
+
// If switching to postgres, ensure postgres config is present
|
|
457
|
+
if (merged.provider === "postgres" && body.postgres) {
|
|
458
|
+
merged.postgres = {
|
|
459
|
+
...existingDb.postgres,
|
|
460
|
+
...(validatedPostgres ?? body.postgres),
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
// If switching to pglite, ensure pglite config is present
|
|
464
|
+
if (merged.provider === "pglite" && body.pglite) {
|
|
465
|
+
merged.pglite = { ...existingDb.pglite, ...body.pglite };
|
|
466
|
+
}
|
|
467
|
+
config.database = merged;
|
|
468
|
+
saveElizaConfig(config);
|
|
469
|
+
logger.info({ src: "database-api", provider: merged.provider }, "Database configuration saved");
|
|
470
|
+
sendJson(res, {
|
|
471
|
+
saved: true,
|
|
472
|
+
config: merged,
|
|
473
|
+
needsRestart: (merged.provider ?? "pglite") !== detectCurrentProvider(),
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* POST /api/database/test
|
|
478
|
+
* Tests a Postgres connection without persisting anything.
|
|
479
|
+
* Body: { connectionString?, host?, port?, user?, password?, database?, ssl? }
|
|
480
|
+
*/
|
|
481
|
+
async function handleTestConnection(req, res) {
|
|
482
|
+
const body = await readJsonBody(req, res);
|
|
483
|
+
if (!body)
|
|
484
|
+
return;
|
|
485
|
+
const validation = await validateDbHost(body);
|
|
486
|
+
if (validation.error) {
|
|
487
|
+
sendJsonError(res, validation.error);
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
const pinnedCreds = validation.pinnedHost
|
|
491
|
+
? withPinnedHost(body, validation.pinnedHost)
|
|
492
|
+
: body;
|
|
493
|
+
const connectionString = buildConnectionString(pinnedCreds);
|
|
494
|
+
const start = Date.now();
|
|
495
|
+
// Dynamically import pg to avoid hard-coupling (it is a peer dep via plugin-sql)
|
|
496
|
+
let Pool;
|
|
497
|
+
try {
|
|
498
|
+
const pgModule = await import("pg");
|
|
499
|
+
Pool = pgModule.default?.Pool ?? pgModule.Pool;
|
|
500
|
+
}
|
|
501
|
+
catch {
|
|
502
|
+
sendJson(res, {
|
|
503
|
+
success: false,
|
|
504
|
+
serverVersion: null,
|
|
505
|
+
error: "PostgreSQL client library (pg) is not available. Ensure @elizaos/plugin-sql is installed.",
|
|
506
|
+
durationMs: Date.now() - start,
|
|
507
|
+
});
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
const pool = new Pool({
|
|
511
|
+
connectionString,
|
|
512
|
+
max: 1,
|
|
513
|
+
connectionTimeoutMillis: 10000,
|
|
514
|
+
idleTimeoutMillis: 5000,
|
|
515
|
+
});
|
|
516
|
+
let client = null;
|
|
517
|
+
try {
|
|
518
|
+
client = await pool.connect();
|
|
519
|
+
const versionResult = await client.query("SELECT version()");
|
|
520
|
+
const serverVersion = String(versionResult.rows[0]?.version ?? "");
|
|
521
|
+
const durationMs = Date.now() - start;
|
|
522
|
+
sendJson(res, {
|
|
523
|
+
success: true,
|
|
524
|
+
serverVersion,
|
|
525
|
+
error: null,
|
|
526
|
+
durationMs,
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
catch (err) {
|
|
530
|
+
const durationMs = Date.now() - start;
|
|
531
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
532
|
+
sendJson(res, {
|
|
533
|
+
success: false,
|
|
534
|
+
serverVersion: null,
|
|
535
|
+
error: message,
|
|
536
|
+
durationMs,
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
finally {
|
|
540
|
+
if (client)
|
|
541
|
+
client.release();
|
|
542
|
+
await pool.end();
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
/**
|
|
546
|
+
* GET /api/database/tables
|
|
547
|
+
* Lists all user tables with column metadata and approximate row counts.
|
|
548
|
+
*/
|
|
549
|
+
async function handleGetTables(_req, res, runtime) {
|
|
550
|
+
// Get all user tables
|
|
551
|
+
const tablesResult = await executeRawSql(runtime, `SELECT
|
|
552
|
+
t.table_schema AS schema,
|
|
553
|
+
t.table_name AS name,
|
|
554
|
+
COALESCE(s.n_live_tup, 0) AS row_count
|
|
555
|
+
FROM information_schema.tables t
|
|
556
|
+
LEFT JOIN pg_stat_user_tables s
|
|
557
|
+
ON s.schemaname = t.table_schema
|
|
558
|
+
AND s.relname = t.table_name
|
|
559
|
+
WHERE t.table_schema NOT IN ('pg_catalog', 'information_schema')
|
|
560
|
+
AND t.table_type = 'BASE TABLE'
|
|
561
|
+
ORDER BY t.table_schema, t.table_name`);
|
|
562
|
+
// Get columns for all tables in one query
|
|
563
|
+
const columnsResult = await executeRawSql(runtime, `SELECT
|
|
564
|
+
c.table_schema AS schema,
|
|
565
|
+
c.table_name AS table_name,
|
|
566
|
+
c.column_name AS name,
|
|
567
|
+
c.data_type AS type,
|
|
568
|
+
(c.is_nullable = 'YES') AS nullable,
|
|
569
|
+
c.column_default AS default_value,
|
|
570
|
+
COALESCE(
|
|
571
|
+
(SELECT true
|
|
572
|
+
FROM information_schema.table_constraints tc
|
|
573
|
+
JOIN information_schema.key_column_usage kcu
|
|
574
|
+
ON tc.constraint_name = kcu.constraint_name
|
|
575
|
+
AND tc.table_schema = kcu.table_schema
|
|
576
|
+
WHERE tc.constraint_type = 'PRIMARY KEY'
|
|
577
|
+
AND tc.table_schema = c.table_schema
|
|
578
|
+
AND tc.table_name = c.table_name
|
|
579
|
+
AND kcu.column_name = c.column_name),
|
|
580
|
+
false
|
|
581
|
+
) AS is_primary_key
|
|
582
|
+
FROM information_schema.columns c
|
|
583
|
+
WHERE c.table_schema NOT IN ('pg_catalog', 'information_schema')
|
|
584
|
+
ORDER BY c.table_schema, c.table_name, c.ordinal_position`);
|
|
585
|
+
// Group columns by table
|
|
586
|
+
const columnsByTable = new Map();
|
|
587
|
+
for (const row of columnsResult.rows) {
|
|
588
|
+
const key = `${String(row.schema)}.${String(row.table_name)}`;
|
|
589
|
+
const cols = columnsByTable.get(key) ?? [];
|
|
590
|
+
cols.push({
|
|
591
|
+
name: String(row.name),
|
|
592
|
+
type: String(row.type),
|
|
593
|
+
nullable: Boolean(row.nullable),
|
|
594
|
+
defaultValue: row.default_value != null ? String(row.default_value) : null,
|
|
595
|
+
isPrimaryKey: Boolean(row.is_primary_key),
|
|
596
|
+
});
|
|
597
|
+
columnsByTable.set(key, cols);
|
|
598
|
+
}
|
|
599
|
+
const tables = tablesResult.rows.map((row) => {
|
|
600
|
+
const key = `${String(row.schema)}.${String(row.name)}`;
|
|
601
|
+
return {
|
|
602
|
+
name: String(row.name),
|
|
603
|
+
schema: String(row.schema),
|
|
604
|
+
rowCount: Number(row.row_count ?? 0),
|
|
605
|
+
columns: columnsByTable.get(key) ?? [],
|
|
606
|
+
};
|
|
607
|
+
});
|
|
608
|
+
sendJson(res, { tables });
|
|
609
|
+
}
|
|
610
|
+
/**
|
|
611
|
+
* GET /api/database/tables/:table/rows?offset=0&limit=50&sort=col&order=asc&search=term
|
|
612
|
+
* Paginated row retrieval for a specific table.
|
|
613
|
+
*/
|
|
614
|
+
async function handleGetRows(req, res, runtime, tableName) {
|
|
615
|
+
const url = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
|
|
616
|
+
const offset = Math.max(0, Number(url.searchParams.get("offset") ?? "0"));
|
|
617
|
+
const limit = Math.min(500, Math.max(1, Number(url.searchParams.get("limit") ?? "50")));
|
|
618
|
+
const sortCol = url.searchParams.get("sort") ?? "";
|
|
619
|
+
const sortOrder = url.searchParams.get("order") === "desc" ? "DESC" : "ASC";
|
|
620
|
+
const search = url.searchParams.get("search") ?? "";
|
|
621
|
+
if (!(await assertTableExists(runtime, tableName))) {
|
|
622
|
+
sendJsonError(res, `Table "${tableName}" not found`, 404);
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
// Get column names for this table (for search and sort validation)
|
|
626
|
+
const safeTableName = tableName.replace(/'/g, "''");
|
|
627
|
+
const colResult = await executeRawSql(runtime, `SELECT column_name, data_type
|
|
628
|
+
FROM information_schema.columns
|
|
629
|
+
WHERE table_name = '${safeTableName}'
|
|
630
|
+
AND table_schema NOT IN ('pg_catalog', 'information_schema')
|
|
631
|
+
ORDER BY ordinal_position`);
|
|
632
|
+
const columnNames = colResult.rows.map((r) => String(r.column_name));
|
|
633
|
+
const columnTypes = new Map(colResult.rows.map((r) => [String(r.column_name), String(r.data_type)]));
|
|
634
|
+
// Validate sort column
|
|
635
|
+
const validSort = sortCol && columnNames.includes(sortCol) ? sortCol : "";
|
|
636
|
+
// Build search clause: search across all text-castable columns
|
|
637
|
+
let whereClause = "";
|
|
638
|
+
if (search.trim()) {
|
|
639
|
+
// Escape ILIKE special characters: backslash first (since it becomes
|
|
640
|
+
// the escape character), then the ILIKE wildcards % and _.
|
|
641
|
+
const escapedSearch = search
|
|
642
|
+
.replace(/'/g, "''")
|
|
643
|
+
.replace(/\\/g, "\\\\")
|
|
644
|
+
.replace(/%/g, "\\%")
|
|
645
|
+
.replace(/_/g, "\\_");
|
|
646
|
+
const textColumns = columnNames.filter((col) => {
|
|
647
|
+
const t = columnTypes.get(col) ?? "";
|
|
648
|
+
return (t.includes("char") ||
|
|
649
|
+
t.includes("text") ||
|
|
650
|
+
t === "uuid" ||
|
|
651
|
+
t === "jsonb" ||
|
|
652
|
+
t === "json" ||
|
|
653
|
+
t === "integer" ||
|
|
654
|
+
t === "bigint" ||
|
|
655
|
+
t === "numeric" ||
|
|
656
|
+
t === "timestamp" ||
|
|
657
|
+
t.includes("timestamp"));
|
|
658
|
+
});
|
|
659
|
+
if (textColumns.length > 0) {
|
|
660
|
+
const conditions = textColumns.map((col) => `${quoteIdent(col)}::text ILIKE '%${escapedSearch}%' ESCAPE '\\'`);
|
|
661
|
+
whereClause = `WHERE (${conditions.join(" OR ")})`;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
// Count total (with search filter)
|
|
665
|
+
const countResult = await executeRawSql(runtime, `SELECT count(*) AS total FROM ${quoteIdent(tableName)} ${whereClause}`);
|
|
666
|
+
const total = Number(countResult.rows[0]?.total ?? 0);
|
|
667
|
+
// Fetch rows
|
|
668
|
+
const orderClause = validSort
|
|
669
|
+
? `ORDER BY ${quoteIdent(validSort)} ${sortOrder}`
|
|
670
|
+
: "";
|
|
671
|
+
const query = `SELECT * FROM ${quoteIdent(tableName)} ${whereClause} ${orderClause} LIMIT ${limit} OFFSET ${offset}`;
|
|
672
|
+
const result = await executeRawSql(runtime, query);
|
|
673
|
+
sendJson(res, {
|
|
674
|
+
table: tableName,
|
|
675
|
+
rows: result.rows,
|
|
676
|
+
columns: result.columns,
|
|
677
|
+
total,
|
|
678
|
+
offset,
|
|
679
|
+
limit,
|
|
680
|
+
});
|
|
681
|
+
}
|
|
682
|
+
/**
|
|
683
|
+
* POST /api/database/tables/:table/rows
|
|
684
|
+
* Insert a new row. Body: { data: Record<string, unknown> }
|
|
685
|
+
*/
|
|
686
|
+
async function handleInsertRow(req, res, runtime, tableName) {
|
|
687
|
+
const body = await readJsonBody(req, res);
|
|
688
|
+
if (!body)
|
|
689
|
+
return;
|
|
690
|
+
if (!body.data ||
|
|
691
|
+
typeof body.data !== "object" ||
|
|
692
|
+
Object.keys(body.data).length === 0) {
|
|
693
|
+
sendJsonError(res, "Request body must include a non-empty 'data' object.");
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
696
|
+
if (!(await assertTableExists(runtime, tableName))) {
|
|
697
|
+
sendJsonError(res, `Table "${tableName}" not found`, 404);
|
|
698
|
+
return;
|
|
699
|
+
}
|
|
700
|
+
const columns = Object.keys(body.data);
|
|
701
|
+
const values = Object.values(body.data);
|
|
702
|
+
const colList = columns.map((c) => quoteIdent(c)).join(", ");
|
|
703
|
+
const valList = values.map(sqlLiteral).join(", ");
|
|
704
|
+
const result = await executeRawSql(runtime, `INSERT INTO ${quoteIdent(tableName)} (${colList}) VALUES (${valList}) RETURNING *`);
|
|
705
|
+
sendJson(res, { inserted: true, row: result.rows[0] ?? null }, 201);
|
|
706
|
+
}
|
|
707
|
+
/**
|
|
708
|
+
* PUT /api/database/tables/:table/rows
|
|
709
|
+
* Update a row. Body: { where: Record<string, unknown>, data: Record<string, unknown> }
|
|
710
|
+
*/
|
|
711
|
+
async function handleUpdateRow(req, res, runtime, tableName) {
|
|
712
|
+
const body = await readJsonBody(req, res);
|
|
713
|
+
if (!body)
|
|
714
|
+
return;
|
|
715
|
+
if (!body.where || Object.keys(body.where).length === 0) {
|
|
716
|
+
sendJsonError(res, "Request body must include a non-empty 'where' object for row identification.");
|
|
717
|
+
return;
|
|
718
|
+
}
|
|
719
|
+
if (!body.data || Object.keys(body.data).length === 0) {
|
|
720
|
+
sendJsonError(res, "Request body must include a non-empty 'data' object with fields to update.");
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
723
|
+
const setClauses = Object.entries(body.data).map(([col, val]) => sqlAssign(col, val));
|
|
724
|
+
const whereClauses = Object.entries(body.where).map(([col, val]) => sqlPredicate(col, val));
|
|
725
|
+
const result = await executeRawSql(runtime, `UPDATE ${quoteIdent(tableName)}
|
|
726
|
+
SET ${setClauses.join(", ")}
|
|
727
|
+
WHERE ${whereClauses.join(" AND ")}
|
|
728
|
+
RETURNING *`);
|
|
729
|
+
if (result.rows.length === 0) {
|
|
730
|
+
sendJsonError(res, "No matching row found to update.", 404);
|
|
731
|
+
return;
|
|
732
|
+
}
|
|
733
|
+
sendJson(res, { updated: true, row: result.rows[0] });
|
|
734
|
+
}
|
|
735
|
+
/**
|
|
736
|
+
* DELETE /api/database/tables/:table/rows
|
|
737
|
+
* Delete a row. Body: { where: Record<string, unknown> }
|
|
738
|
+
*/
|
|
739
|
+
async function handleDeleteRow(req, res, runtime, tableName) {
|
|
740
|
+
const body = await readJsonBody(req, res);
|
|
741
|
+
if (!body)
|
|
742
|
+
return;
|
|
743
|
+
if (!body.where || Object.keys(body.where).length === 0) {
|
|
744
|
+
sendJsonError(res, "Request body must include a non-empty 'where' object for row identification.");
|
|
745
|
+
return;
|
|
746
|
+
}
|
|
747
|
+
const whereClauses = Object.entries(body.where).map(([col, val]) => sqlPredicate(col, val));
|
|
748
|
+
const result = await executeRawSql(runtime, `DELETE FROM ${quoteIdent(tableName)}
|
|
749
|
+
WHERE ${whereClauses.join(" AND ")}
|
|
750
|
+
RETURNING *`);
|
|
751
|
+
if (result.rows.length === 0) {
|
|
752
|
+
sendJsonError(res, "No matching row found to delete.", 404);
|
|
753
|
+
return;
|
|
754
|
+
}
|
|
755
|
+
sendJson(res, { deleted: true, row: result.rows[0] });
|
|
756
|
+
}
|
|
757
|
+
/**
|
|
758
|
+
* POST /api/database/query
|
|
759
|
+
* Execute a raw SQL query. Body: { sql: string, readOnly?: boolean }
|
|
760
|
+
*/
|
|
761
|
+
async function handleQuery(req, res, runtime) {
|
|
762
|
+
const body = await readJsonBody(req, res);
|
|
763
|
+
if (!body)
|
|
764
|
+
return;
|
|
765
|
+
if (!body.sql ||
|
|
766
|
+
typeof body.sql !== "string" ||
|
|
767
|
+
body.sql.trim().length === 0) {
|
|
768
|
+
sendJsonError(res, "Request body must include a non-empty 'sql' string.");
|
|
769
|
+
return;
|
|
770
|
+
}
|
|
771
|
+
const sqlText = body.sql.trim();
|
|
772
|
+
// If readOnly mode, reject mutation statements.
|
|
773
|
+
// Strip SQL comments, then scan for mutation keywords *anywhere* in the
|
|
774
|
+
// query — not just the leading keyword. This prevents bypass via CTEs
|
|
775
|
+
// (WITH ... AS (DELETE ...)) and other SQL constructs that nest mutations.
|
|
776
|
+
if (body.readOnly !== false) {
|
|
777
|
+
// Strip block comments (/* ... */) and line comments (-- ...).
|
|
778
|
+
// Use empty-string replacement (not space) to mirror how PostgreSQL
|
|
779
|
+
// concatenates tokens across comments — e.g. DE/* */LETE → DELETE.
|
|
780
|
+
// A space replacement would turn it into "DE LETE", hiding the keyword.
|
|
781
|
+
const stripped = sqlText
|
|
782
|
+
.replace(/\/\*[\s\S]*?\*\//g, "")
|
|
783
|
+
.replace(/--.*$/gm, "")
|
|
784
|
+
.trim();
|
|
785
|
+
// Strip string literals so that mutation keywords/functions inside quoted
|
|
786
|
+
// strings are ignored. Handles single-quoted ('...'), dollar-quoted
|
|
787
|
+
// ($$...$$), and tagged dollar-quoted ($tag$...$tag$) strings.
|
|
788
|
+
const noLiterals = stripped
|
|
789
|
+
.replace(/\$([A-Za-z0-9_]*)\$[\s\S]*?\$\1\$/g, " ")
|
|
790
|
+
.replace(/'(?:[^']|'')*'/g, " ");
|
|
791
|
+
// For keyword checks, also strip double-quoted identifiers to avoid
|
|
792
|
+
// matching words inside quoted table/column names.
|
|
793
|
+
const noStrings = noLiterals.replace(/"(?:[^"]|"")*"/g, " ");
|
|
794
|
+
const mutationKeywords = [
|
|
795
|
+
// ── DML ────────────────────────────────────────────────────────────
|
|
796
|
+
"INSERT",
|
|
797
|
+
"UPDATE",
|
|
798
|
+
"DELETE",
|
|
799
|
+
"INTO",
|
|
800
|
+
"COPY",
|
|
801
|
+
"MERGE",
|
|
802
|
+
// ── DDL ────────────────────────────────────────────────────────────
|
|
803
|
+
"DROP",
|
|
804
|
+
"ALTER",
|
|
805
|
+
"TRUNCATE",
|
|
806
|
+
"CREATE",
|
|
807
|
+
"COMMENT",
|
|
808
|
+
// ── Admin / privilege ──────────────────────────────────────────────
|
|
809
|
+
"GRANT",
|
|
810
|
+
"REVOKE",
|
|
811
|
+
"SET",
|
|
812
|
+
"RESET",
|
|
813
|
+
"LOAD",
|
|
814
|
+
// ── Maintenance ────────────────────────────────────────────────────
|
|
815
|
+
"VACUUM",
|
|
816
|
+
"REINDEX",
|
|
817
|
+
"CLUSTER",
|
|
818
|
+
"REFRESH",
|
|
819
|
+
"DISCARD",
|
|
820
|
+
// ── Procedural ─────────────────────────────────────────────────────
|
|
821
|
+
"CALL",
|
|
822
|
+
"DO",
|
|
823
|
+
// ── Async notifications (side-effects) ─────────────────────────────
|
|
824
|
+
"LISTEN",
|
|
825
|
+
"UNLISTEN",
|
|
826
|
+
"NOTIFY",
|
|
827
|
+
// ── Prepared statements (can wrap mutations) ───────────────────────
|
|
828
|
+
"PREPARE",
|
|
829
|
+
"EXECUTE",
|
|
830
|
+
"DEALLOCATE",
|
|
831
|
+
// ── Locking ────────────────────────────────────────────────────────
|
|
832
|
+
"LOCK",
|
|
833
|
+
];
|
|
834
|
+
// Match mutation keywords as whole words (word boundary) anywhere in the
|
|
835
|
+
// query, catching them inside CTEs, subqueries, etc.
|
|
836
|
+
const mutationPattern = new RegExp(`\\b(${mutationKeywords.join("|")})\\b`, "i");
|
|
837
|
+
const match = mutationPattern.exec(noStrings);
|
|
838
|
+
if (match) {
|
|
839
|
+
sendJsonError(res, `Query rejected: "${match[1].toUpperCase()}" is a mutation keyword. Set readOnly: false to execute mutations.`);
|
|
840
|
+
return;
|
|
841
|
+
}
|
|
842
|
+
// PostgreSQL built-in functions that can read/write server files, mutate
|
|
843
|
+
// server state, or cause denial of service. These appear inside otherwise
|
|
844
|
+
// valid SELECT expressions, so keyword checks alone won't catch them.
|
|
845
|
+
//
|
|
846
|
+
// ── File I/O (arbitrary file read/write on the DB server) ─────────
|
|
847
|
+
// lo_import('/etc/passwd') — load file into large object
|
|
848
|
+
// lo_export(oid, '/tmp/evil') — write large object to file
|
|
849
|
+
// lo_unlink(oid) — delete large object
|
|
850
|
+
// pg_read_file('/etc/passwd') — read server file (superuser)
|
|
851
|
+
// pg_read_binary_file(...) — same, binary
|
|
852
|
+
// pg_write_file(...) — write to server files (ext. module)
|
|
853
|
+
// pg_stat_file(...) — stat a server file
|
|
854
|
+
// pg_ls_dir(...) — list server directory
|
|
855
|
+
//
|
|
856
|
+
// ── Sequence / state mutation ────────────────────────────────────
|
|
857
|
+
// nextval('seq'), setval('seq', n)
|
|
858
|
+
//
|
|
859
|
+
// ── Denial of service ────────────────────────────────────────────
|
|
860
|
+
// pg_sleep(n) — block connection for n seconds
|
|
861
|
+
// pg_sleep_for(interval) — same, interval version
|
|
862
|
+
// pg_sleep_until(timestamp) — same, deadline version
|
|
863
|
+
//
|
|
864
|
+
// ── Session / backend control ────────────────────────────────────
|
|
865
|
+
// pg_terminate_backend(pid) — kill another connection
|
|
866
|
+
// pg_cancel_backend(pid) — cancel a running query
|
|
867
|
+
// pg_reload_conf() — reload server configuration
|
|
868
|
+
// pg_rotate_logfile() — rotate the server log
|
|
869
|
+
// set_config(name, value, local) — SET equivalent as function
|
|
870
|
+
//
|
|
871
|
+
// ── Advisory locks (can deadlock other connections) ───────────────
|
|
872
|
+
// pg_advisory_lock(key) — session-level advisory lock
|
|
873
|
+
// pg_advisory_lock_shared(key)
|
|
874
|
+
// pg_try_advisory_lock(key)
|
|
875
|
+
const dangerousFunctions = [
|
|
876
|
+
// File I/O
|
|
877
|
+
"lo_import",
|
|
878
|
+
"lo_export",
|
|
879
|
+
"lo_unlink",
|
|
880
|
+
"lo_put",
|
|
881
|
+
"lo_from_bytea",
|
|
882
|
+
"pg_read_file",
|
|
883
|
+
"pg_read_binary_file",
|
|
884
|
+
"pg_write_file",
|
|
885
|
+
"pg_stat_file",
|
|
886
|
+
"pg_ls_dir",
|
|
887
|
+
"pg_ls_logdir",
|
|
888
|
+
"pg_ls_waldir",
|
|
889
|
+
"pg_ls_tmpdir",
|
|
890
|
+
"pg_ls_archive_statusdir",
|
|
891
|
+
// Sequence / state mutation
|
|
892
|
+
"nextval",
|
|
893
|
+
"setval",
|
|
894
|
+
// Denial of service
|
|
895
|
+
"pg_sleep",
|
|
896
|
+
"pg_sleep_for",
|
|
897
|
+
"pg_sleep_until",
|
|
898
|
+
// Session / backend control
|
|
899
|
+
"pg_terminate_backend",
|
|
900
|
+
"pg_cancel_backend",
|
|
901
|
+
"pg_reload_conf",
|
|
902
|
+
"pg_rotate_logfile",
|
|
903
|
+
"set_config",
|
|
904
|
+
// Advisory locks
|
|
905
|
+
"pg_advisory_lock",
|
|
906
|
+
"pg_advisory_lock_shared",
|
|
907
|
+
"pg_try_advisory_lock",
|
|
908
|
+
"pg_try_advisory_lock_shared",
|
|
909
|
+
"pg_advisory_xact_lock",
|
|
910
|
+
"pg_advisory_xact_lock_shared",
|
|
911
|
+
"pg_advisory_unlock",
|
|
912
|
+
"pg_advisory_unlock_shared",
|
|
913
|
+
"pg_advisory_unlock_all",
|
|
914
|
+
];
|
|
915
|
+
const dangerousFnPattern = new RegExp(`(?:^|[^\\w$])"?(?:${dangerousFunctions.join("|")})"?\\s*\\(`, "i");
|
|
916
|
+
const fnMatch = dangerousFnPattern.exec(noLiterals);
|
|
917
|
+
if (fnMatch) {
|
|
918
|
+
// Extract the function name from the match for the error message.
|
|
919
|
+
const fnNameMatch = fnMatch[0].match(new RegExp(`(${dangerousFunctions.join("|")})`, "i"));
|
|
920
|
+
const fnName = fnNameMatch ? fnNameMatch[1].toUpperCase() : "UNKNOWN";
|
|
921
|
+
sendJsonError(res, `Query rejected: "${fnName}" is a dangerous function that can modify server state. Set readOnly: false to execute this query.`);
|
|
922
|
+
return;
|
|
923
|
+
}
|
|
924
|
+
// Reject multi-statement queries (naive: any semicolon not at the very end)
|
|
925
|
+
const trimmedForSemicolon = stripped.replace(/;\s*$/, "");
|
|
926
|
+
if (trimmedForSemicolon.includes(";")) {
|
|
927
|
+
sendJsonError(res, "Query rejected: multi-statement queries are not allowed in read-only mode.");
|
|
928
|
+
return;
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
const start = Date.now();
|
|
932
|
+
const result = await executeRawSql(runtime, sqlText);
|
|
933
|
+
const durationMs = Date.now() - start;
|
|
934
|
+
const queryResult = {
|
|
935
|
+
columns: result.columns,
|
|
936
|
+
rows: result.rows,
|
|
937
|
+
rowCount: result.rows.length,
|
|
938
|
+
durationMs,
|
|
939
|
+
};
|
|
940
|
+
sendJson(res, queryResult);
|
|
941
|
+
}
|
|
942
|
+
// ---------------------------------------------------------------------------
|
|
943
|
+
// Router
|
|
944
|
+
// ---------------------------------------------------------------------------
|
|
945
|
+
/**
|
|
946
|
+
* Route a database API request. Returns true if handled, false if not matched.
|
|
947
|
+
*
|
|
948
|
+
* Expected URL patterns:
|
|
949
|
+
* GET /api/database/status
|
|
950
|
+
* GET /api/database/config
|
|
951
|
+
* PUT /api/database/config
|
|
952
|
+
* POST /api/database/test
|
|
953
|
+
* GET /api/database/tables
|
|
954
|
+
* GET /api/database/tables/:table/rows
|
|
955
|
+
* POST /api/database/tables/:table/rows
|
|
956
|
+
* PUT /api/database/tables/:table/rows
|
|
957
|
+
* DELETE /api/database/tables/:table/rows
|
|
958
|
+
* POST /api/database/query
|
|
959
|
+
*/
|
|
960
|
+
export async function handleDatabaseRoute(req, res, runtime, pathname) {
|
|
961
|
+
const method = req.method ?? "GET";
|
|
962
|
+
// ── GET /api/database/status ──────────────────────────────────────────
|
|
963
|
+
if (method === "GET" && pathname === "/api/database/status") {
|
|
964
|
+
await handleGetStatus(req, res, runtime);
|
|
965
|
+
return true;
|
|
966
|
+
}
|
|
967
|
+
// ── GET /api/database/config ──────────────────────────────────────────
|
|
968
|
+
if (method === "GET" && pathname === "/api/database/config") {
|
|
969
|
+
handleGetConfig(req, res);
|
|
970
|
+
return true;
|
|
971
|
+
}
|
|
972
|
+
// ── PUT /api/database/config ──────────────────────────────────────────
|
|
973
|
+
if (method === "PUT" && pathname === "/api/database/config") {
|
|
974
|
+
await handlePutConfig(req, res);
|
|
975
|
+
return true;
|
|
976
|
+
}
|
|
977
|
+
// ── POST /api/database/test ───────────────────────────────────────────
|
|
978
|
+
if (method === "POST" && pathname === "/api/database/test") {
|
|
979
|
+
await handleTestConnection(req, res);
|
|
980
|
+
return true;
|
|
981
|
+
}
|
|
982
|
+
// Routes below require a live runtime with a database adapter
|
|
983
|
+
if (!runtime?.adapter) {
|
|
984
|
+
sendJsonError(res, "Database not available. The agent may not be running or the database adapter is not initialized.", 503);
|
|
985
|
+
return true;
|
|
986
|
+
}
|
|
987
|
+
// ── GET /api/database/tables ──────────────────────────────────────────
|
|
988
|
+
if (method === "GET" && pathname === "/api/database/tables") {
|
|
989
|
+
await handleGetTables(req, res, runtime);
|
|
990
|
+
return true;
|
|
991
|
+
}
|
|
992
|
+
// ── POST /api/database/query ──────────────────────────────────────────
|
|
993
|
+
if (method === "POST" && pathname === "/api/database/query") {
|
|
994
|
+
await handleQuery(req, res, runtime);
|
|
995
|
+
return true;
|
|
996
|
+
}
|
|
997
|
+
// ── Table row operations: /api/database/tables/:table/rows ────────────
|
|
998
|
+
const rowsMatch = pathname.match(/^\/api\/database\/tables\/([^/]+)\/rows$/);
|
|
999
|
+
if (rowsMatch) {
|
|
1000
|
+
const tableNameDecoded = decodeURIComponent(rowsMatch[1]);
|
|
1001
|
+
if (method === "GET") {
|
|
1002
|
+
await handleGetRows(req, res, runtime, tableNameDecoded);
|
|
1003
|
+
return true;
|
|
1004
|
+
}
|
|
1005
|
+
if (method === "POST") {
|
|
1006
|
+
await handleInsertRow(req, res, runtime, tableNameDecoded);
|
|
1007
|
+
return true;
|
|
1008
|
+
}
|
|
1009
|
+
if (method === "PUT") {
|
|
1010
|
+
await handleUpdateRow(req, res, runtime, tableNameDecoded);
|
|
1011
|
+
return true;
|
|
1012
|
+
}
|
|
1013
|
+
if (method === "DELETE") {
|
|
1014
|
+
await handleDeleteRow(req, res, runtime, tableNameDecoded);
|
|
1015
|
+
return true;
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
return false;
|
|
1019
|
+
}
|