@ixo/oracle-runtime 0.0.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/dist/bootstrap/ambient-factory.d.ts +32 -0
- package/dist/bootstrap/ambient-factory.d.ts.map +1 -0
- package/dist/bootstrap/ambient-factory.js +128 -0
- package/dist/bootstrap/create-oracle-app.d.ts +112 -0
- package/dist/bootstrap/create-oracle-app.d.ts.map +1 -0
- package/dist/bootstrap/create-oracle-app.js +530 -0
- package/dist/bootstrap/graceful-shutdown.d.ts +20 -0
- package/dist/bootstrap/graceful-shutdown.d.ts.map +1 -0
- package/dist/bootstrap/graceful-shutdown.js +61 -0
- package/dist/bootstrap/index.d.ts +13 -0
- package/dist/bootstrap/index.d.ts.map +1 -0
- package/dist/bootstrap/index.js +6 -0
- package/dist/bootstrap/inspect.d.ts +74 -0
- package/dist/bootstrap/inspect.d.ts.map +1 -0
- package/dist/bootstrap/inspect.js +111 -0
- package/dist/bootstrap/plugin-loader.d.ts +50 -0
- package/dist/bootstrap/plugin-loader.d.ts.map +1 -0
- package/dist/bootstrap/plugin-loader.js +119 -0
- package/dist/bootstrap/runtime-app-module.d.ts +38 -0
- package/dist/bootstrap/runtime-app-module.d.ts.map +1 -0
- package/dist/bootstrap/runtime-app-module.js +114 -0
- package/dist/bootstrap/schema-composer.d.ts +46 -0
- package/dist/bootstrap/schema-composer.d.ts.map +1 -0
- package/dist/bootstrap/schema-composer.js +65 -0
- package/dist/config/base-env-config.d.ts +31 -0
- package/dist/config/base-env-config.d.ts.map +1 -0
- package/dist/config/base-env-config.js +70 -0
- package/dist/config/base-env-schema.d.ts +77 -0
- package/dist/config/base-env-schema.d.ts.map +1 -0
- package/dist/config/base-env-schema.js +102 -0
- package/dist/events/scoped-emitter.d.ts +33 -0
- package/dist/events/scoped-emitter.d.ts.map +1 -0
- package/dist/events/scoped-emitter.js +32 -0
- package/dist/graph/index.d.ts +8 -0
- package/dist/graph/index.d.ts.map +1 -0
- package/dist/graph/index.js +6 -0
- package/dist/graph/main-agent-types.d.ts +88 -0
- package/dist/graph/main-agent-types.d.ts.map +1 -0
- package/dist/graph/main-agent-types.js +20 -0
- package/dist/graph/main-agent.d.ts +16 -0
- package/dist/graph/main-agent.d.ts.map +1 -0
- package/dist/graph/main-agent.js +251 -0
- package/dist/graph/middlewares/capability-gate-middleware.d.ts +35 -0
- package/dist/graph/middlewares/capability-gate-middleware.d.ts.map +1 -0
- package/dist/graph/middlewares/capability-gate-middleware.js +54 -0
- package/dist/graph/middlewares/index.d.ts +7 -0
- package/dist/graph/middlewares/index.d.ts.map +1 -0
- package/dist/graph/middlewares/index.js +6 -0
- package/dist/graph/middlewares/page-context-middleware.d.ts +23 -0
- package/dist/graph/middlewares/page-context-middleware.d.ts.map +1 -0
- package/dist/graph/middlewares/page-context-middleware.js +68 -0
- package/dist/graph/middlewares/safety-guardrail-middleware.d.ts +26 -0
- package/dist/graph/middlewares/safety-guardrail-middleware.d.ts.map +1 -0
- package/dist/graph/middlewares/safety-guardrail-middleware.js +88 -0
- package/dist/graph/middlewares/summarization-middleware.d.ts +17 -0
- package/dist/graph/middlewares/summarization-middleware.d.ts.map +1 -0
- package/dist/graph/middlewares/summarization-middleware.js +62 -0
- package/dist/graph/middlewares/tool-repetition-guard-middleware.d.ts +24 -0
- package/dist/graph/middlewares/tool-repetition-guard-middleware.d.ts.map +1 -0
- package/dist/graph/middlewares/tool-repetition-guard-middleware.js +112 -0
- package/dist/graph/middlewares/tool-validation-middleware.d.ts +24 -0
- package/dist/graph/middlewares/tool-validation-middleware.d.ts.map +1 -0
- package/dist/graph/middlewares/tool-validation-middleware.js +61 -0
- package/dist/graph/prompt-composer.d.ts +69 -0
- package/dist/graph/prompt-composer.d.ts.map +1 -0
- package/dist/graph/prompt-composer.js +315 -0
- package/dist/graph/state.d.ts +65 -0
- package/dist/graph/state.d.ts.map +1 -0
- package/dist/graph/state.js +60 -0
- package/dist/graph/sub-agent-fallback.d.ts +56 -0
- package/dist/graph/sub-agent-fallback.d.ts.map +1 -0
- package/dist/graph/sub-agent-fallback.js +79 -0
- package/dist/graph/subagent-as-tool.d.ts +77 -0
- package/dist/graph/subagent-as-tool.d.ts.map +1 -0
- package/dist/graph/subagent-as-tool.js +197 -0
- package/dist/graph/wrap-plugin-tool.d.ts +28 -0
- package/dist/graph/wrap-plugin-tool.d.ts.map +1 -0
- package/dist/graph/wrap-plugin-tool.js +30 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/llm/index.d.ts +2 -0
- package/dist/llm/index.d.ts.map +1 -0
- package/dist/llm/index.js +1 -0
- package/dist/llm/llm-provider.d.ts +41 -0
- package/dist/llm/llm-provider.d.ts.map +1 -0
- package/dist/llm/llm-provider.js +130 -0
- package/dist/manifest/index.d.ts +6 -0
- package/dist/manifest/index.d.ts.map +1 -0
- package/dist/manifest/index.js +3 -0
- package/dist/manifest/schema.d.ts +31 -0
- package/dist/manifest/schema.d.ts.map +1 -0
- package/dist/manifest/schema.js +44 -0
- package/dist/manifest/tier1-renderer.d.ts +31 -0
- package/dist/manifest/tier1-renderer.d.ts.map +1 -0
- package/dist/manifest/tier1-renderer.js +68 -0
- package/dist/manifest/validator.d.ts +34 -0
- package/dist/manifest/validator.d.ts.map +1 -0
- package/dist/manifest/validator.js +111 -0
- package/dist/matrix/checkpointer/matrix-upload-utils.d.ts +66 -0
- package/dist/matrix/checkpointer/matrix-upload-utils.d.ts.map +1 -0
- package/dist/matrix/checkpointer/matrix-upload-utils.js +228 -0
- package/dist/matrix/checkpointer/type.d.ts +4 -0
- package/dist/matrix/checkpointer/type.d.ts.map +1 -0
- package/dist/matrix/checkpointer/type.js +1 -0
- package/dist/matrix/checkpointer/user-matrix-sqlite-sync-service.module.d.ts +3 -0
- package/dist/matrix/checkpointer/user-matrix-sqlite-sync-service.module.d.ts.map +1 -0
- package/dist/matrix/checkpointer/user-matrix-sqlite-sync-service.module.js +22 -0
- package/dist/matrix/checkpointer/user-matrix-sqlite-sync-service.service.d.ts +93 -0
- package/dist/matrix/checkpointer/user-matrix-sqlite-sync-service.service.d.ts.map +1 -0
- package/dist/matrix/checkpointer/user-matrix-sqlite-sync-service.service.js +856 -0
- package/dist/matrix/room-membership.d.ts +11 -0
- package/dist/matrix/room-membership.d.ts.map +1 -0
- package/dist/matrix/room-membership.js +33 -0
- package/dist/meta-tools/index.d.ts +28 -0
- package/dist/meta-tools/index.d.ts.map +1 -0
- package/dist/meta-tools/index.js +24 -0
- package/dist/meta-tools/list-capabilities.d.ts +12 -0
- package/dist/meta-tools/list-capabilities.d.ts.map +1 -0
- package/dist/meta-tools/list-capabilities.js +55 -0
- package/dist/meta-tools/load-capability.d.ts +24 -0
- package/dist/meta-tools/load-capability.d.ts.map +1 -0
- package/dist/meta-tools/load-capability.js +82 -0
- package/dist/modules/auth/auth-header.middleware.d.ts +37 -0
- package/dist/modules/auth/auth-header.middleware.d.ts.map +1 -0
- package/dist/modules/auth/auth-header.middleware.js +115 -0
- package/dist/modules/auth/auth.module.d.ts +9 -0
- package/dist/modules/auth/auth.module.d.ts.map +1 -0
- package/dist/modules/auth/auth.module.js +27 -0
- package/dist/modules/auth/index.d.ts +3 -0
- package/dist/modules/auth/index.d.ts.map +1 -0
- package/dist/modules/auth/index.js +2 -0
- package/dist/modules/auth/validate-ucan-delegation.d.ts +31 -0
- package/dist/modules/auth/validate-ucan-delegation.d.ts.map +1 -0
- package/dist/modules/auth/validate-ucan-delegation.js +36 -0
- package/dist/modules/blob-store/blob-store.module.d.ts +10 -0
- package/dist/modules/blob-store/blob-store.module.d.ts.map +1 -0
- package/dist/modules/blob-store/blob-store.module.js +25 -0
- package/dist/modules/blob-store/blob-store.service.d.ts +66 -0
- package/dist/modules/blob-store/blob-store.service.d.ts.map +1 -0
- package/dist/modules/blob-store/blob-store.service.js +108 -0
- package/dist/modules/blob-store/index.d.ts +3 -0
- package/dist/modules/blob-store/index.d.ts.map +1 -0
- package/dist/modules/blob-store/index.js +2 -0
- package/dist/modules/health/health.controller.d.ts +18 -0
- package/dist/modules/health/health.controller.d.ts.map +1 -0
- package/dist/modules/health/health.controller.js +48 -0
- package/dist/modules/health/health.module.d.ts +8 -0
- package/dist/modules/health/health.module.d.ts.map +1 -0
- package/dist/modules/health/health.module.js +21 -0
- package/dist/modules/index.d.ts +8 -0
- package/dist/modules/index.d.ts.map +1 -0
- package/dist/modules/index.js +6 -0
- package/dist/modules/messages/__test-fixtures__/deps.d.ts +41 -0
- package/dist/modules/messages/__test-fixtures__/deps.d.ts.map +1 -0
- package/dist/modules/messages/__test-fixtures__/deps.js +73 -0
- package/dist/modules/messages/__test-fixtures__/fake-agent.d.ts +27 -0
- package/dist/modules/messages/__test-fixtures__/fake-agent.d.ts.map +1 -0
- package/dist/modules/messages/__test-fixtures__/fake-agent.js +41 -0
- package/dist/modules/messages/__test-fixtures__/fake-response.d.ts +29 -0
- package/dist/modules/messages/__test-fixtures__/fake-response.d.ts.map +1 -0
- package/dist/modules/messages/__test-fixtures__/fake-response.js +55 -0
- package/dist/modules/messages/agent-builder.d.ts +64 -0
- package/dist/modules/messages/agent-builder.d.ts.map +1 -0
- package/dist/modules/messages/agent-builder.js +219 -0
- package/dist/modules/messages/batch-invoker.d.ts +36 -0
- package/dist/modules/messages/batch-invoker.d.ts.map +1 -0
- package/dist/modules/messages/batch-invoker.js +58 -0
- package/dist/modules/messages/dto/list-messages.dto.d.ts +4 -0
- package/dist/modules/messages/dto/list-messages.dto.d.ts.map +1 -0
- package/dist/modules/messages/dto/list-messages.dto.js +17 -0
- package/dist/modules/messages/dto/send-message.dto.d.ts +73 -0
- package/dist/modules/messages/dto/send-message.dto.d.ts.map +1 -0
- package/dist/modules/messages/dto/send-message.dto.js +318 -0
- package/dist/modules/messages/file-processing-credit-sink.port.d.ts +20 -0
- package/dist/modules/messages/file-processing-credit-sink.port.d.ts.map +1 -0
- package/dist/modules/messages/file-processing-credit-sink.port.js +1 -0
- package/dist/modules/messages/file-processing.service.d.ts +195 -0
- package/dist/modules/messages/file-processing.service.d.ts.map +1 -0
- package/dist/modules/messages/file-processing.service.js +1278 -0
- package/dist/modules/messages/homeserver-cache.d.ts +11 -0
- package/dist/modules/messages/homeserver-cache.d.ts.map +1 -0
- package/dist/modules/messages/homeserver-cache.js +31 -0
- package/dist/modules/messages/matrix-listener-bridge.d.ts +63 -0
- package/dist/modules/messages/matrix-listener-bridge.d.ts.map +1 -0
- package/dist/modules/messages/matrix-listener-bridge.js +280 -0
- package/dist/modules/messages/messages.controller.d.ts +13 -0
- package/dist/modules/messages/messages.controller.d.ts.map +1 -0
- package/dist/modules/messages/messages.controller.js +95 -0
- package/dist/modules/messages/messages.module.d.ts +3 -0
- package/dist/modules/messages/messages.module.d.ts.map +1 -0
- package/dist/modules/messages/messages.module.js +75 -0
- package/dist/modules/messages/messages.service.d.ts +112 -0
- package/dist/modules/messages/messages.service.d.ts.map +1 -0
- package/dist/modules/messages/messages.service.js +279 -0
- package/dist/modules/messages/oracle-runtime-bundle.d.ts +38 -0
- package/dist/modules/messages/oracle-runtime-bundle.d.ts.map +1 -0
- package/dist/modules/messages/oracle-runtime-bundle.js +40 -0
- package/dist/modules/messages/post-message-syncer.d.ts +30 -0
- package/dist/modules/messages/post-message-syncer.d.ts.map +1 -0
- package/dist/modules/messages/post-message-syncer.js +73 -0
- package/dist/modules/messages/request-preparer.d.ts +53 -0
- package/dist/modules/messages/request-preparer.d.ts.map +1 -0
- package/dist/modules/messages/request-preparer.js +139 -0
- package/dist/modules/messages/sse-stream-runner.d.ts +73 -0
- package/dist/modules/messages/sse-stream-runner.d.ts.map +1 -0
- package/dist/modules/messages/sse-stream-runner.js +352 -0
- package/dist/modules/messages/sse.utils.d.ts +29 -0
- package/dist/modules/messages/sse.utils.d.ts.map +1 -0
- package/dist/modules/messages/sse.utils.js +77 -0
- package/dist/modules/messages/user-context-fetcher.d.ts +41 -0
- package/dist/modules/messages/user-context-fetcher.d.ts.map +1 -0
- package/dist/modules/messages/user-context-fetcher.js +117 -0
- package/dist/modules/secrets/index.d.ts +2 -0
- package/dist/modules/secrets/index.d.ts.map +1 -0
- package/dist/modules/secrets/index.js +1 -0
- package/dist/modules/secrets/secrets.service.d.ts +29 -0
- package/dist/modules/secrets/secrets.service.d.ts.map +1 -0
- package/dist/modules/secrets/secrets.service.js +107 -0
- package/dist/modules/sessions/dto/create-session.dto.d.ts +6 -0
- package/dist/modules/sessions/dto/create-session.dto.d.ts.map +1 -0
- package/dist/modules/sessions/dto/create-session.dto.js +5 -0
- package/dist/modules/sessions/dto/delete-session.dto.d.ts +6 -0
- package/dist/modules/sessions/dto/delete-session.dto.d.ts.map +1 -0
- package/dist/modules/sessions/dto/delete-session.dto.js +5 -0
- package/dist/modules/sessions/dto/list-sessions.dto.d.ts +7 -0
- package/dist/modules/sessions/dto/list-sessions.dto.d.ts.map +1 -0
- package/dist/modules/sessions/dto/list-sessions.dto.js +6 -0
- package/dist/modules/sessions/session-history-processor.service.d.ts +48 -0
- package/dist/modules/sessions/session-history-processor.service.d.ts.map +1 -0
- package/dist/modules/sessions/session-history-processor.service.js +254 -0
- package/dist/modules/sessions/sessions.controller.d.ts +13 -0
- package/dist/modules/sessions/sessions.controller.d.ts.map +1 -0
- package/dist/modules/sessions/sessions.controller.js +113 -0
- package/dist/modules/sessions/sessions.module.d.ts +3 -0
- package/dist/modules/sessions/sessions.module.d.ts.map +1 -0
- package/dist/modules/sessions/sessions.module.js +49 -0
- package/dist/modules/sessions/sessions.service.d.ts +23 -0
- package/dist/modules/sessions/sessions.service.d.ts.map +1 -0
- package/dist/modules/sessions/sessions.service.js +168 -0
- package/dist/modules/subscription/subscription.middleware.d.ts +37 -0
- package/dist/modules/subscription/subscription.middleware.d.ts.map +1 -0
- package/dist/modules/subscription/subscription.middleware.js +141 -0
- package/dist/modules/subscription/subscription.module.d.ts +12 -0
- package/dist/modules/subscription/subscription.module.d.ts.map +1 -0
- package/dist/modules/subscription/subscription.module.js +26 -0
- package/dist/modules/throttler/throttler.module.d.ts +11 -0
- package/dist/modules/throttler/throttler.module.d.ts.map +1 -0
- package/dist/modules/throttler/throttler.module.js +32 -0
- package/dist/modules/ucan/index.d.ts +7 -0
- package/dist/modules/ucan/index.d.ts.map +1 -0
- package/dist/modules/ucan/index.js +6 -0
- package/dist/modules/ucan/ucan.config.d.ts +87 -0
- package/dist/modules/ucan/ucan.config.d.ts.map +1 -0
- package/dist/modules/ucan/ucan.config.js +114 -0
- package/dist/modules/ucan/ucan.module.d.ts +29 -0
- package/dist/modules/ucan/ucan.module.d.ts.map +1 -0
- package/dist/modules/ucan/ucan.module.js +46 -0
- package/dist/modules/ucan/ucan.service.d.ts +129 -0
- package/dist/modules/ucan/ucan.service.d.ts.map +1 -0
- package/dist/modules/ucan/ucan.service.js +582 -0
- package/dist/modules/ws/emitter.d.ts +12 -0
- package/dist/modules/ws/emitter.d.ts.map +1 -0
- package/dist/modules/ws/emitter.js +12 -0
- package/dist/modules/ws/ws.gateway.d.ts +34 -0
- package/dist/modules/ws/ws.gateway.d.ts.map +1 -0
- package/dist/modules/ws/ws.gateway.js +241 -0
- package/dist/modules/ws/ws.module.d.ts +3 -0
- package/dist/modules/ws/ws.module.d.ts.map +1 -0
- package/dist/modules/ws/ws.module.js +21 -0
- package/dist/modules/ws/ws.service.d.ts +25 -0
- package/dist/modules/ws/ws.service.d.ts.map +1 -0
- package/dist/modules/ws/ws.service.js +113 -0
- package/dist/plugin-api/define-plugin.d.ts +19 -0
- package/dist/plugin-api/define-plugin.d.ts.map +1 -0
- package/dist/plugin-api/define-plugin.js +25 -0
- package/dist/plugin-api/oracle-plugin.d.ts +92 -0
- package/dist/plugin-api/oracle-plugin.d.ts.map +1 -0
- package/dist/plugin-api/oracle-plugin.js +15 -0
- package/dist/plugin-api/tool-helper.d.ts +33 -0
- package/dist/plugin-api/tool-helper.d.ts.map +1 -0
- package/dist/plugin-api/tool-helper.js +40 -0
- package/dist/plugin-api/types.d.ts +378 -0
- package/dist/plugin-api/types.d.ts.map +1 -0
- package/dist/plugin-api/types.js +1 -0
- package/dist/plugins/agui/agui-agent.d.ts +10 -0
- package/dist/plugins/agui/agui-agent.d.ts.map +1 -0
- package/dist/plugins/agui/agui-agent.js +110 -0
- package/dist/plugins/agui/agui.plugin.d.ts +16 -0
- package/dist/plugins/agui/agui.plugin.d.ts.map +1 -0
- package/dist/plugins/agui/agui.plugin.js +117 -0
- package/dist/plugins/agui/index.d.ts +3 -0
- package/dist/plugins/agui/index.d.ts.map +1 -0
- package/dist/plugins/agui/index.js +2 -0
- package/dist/plugins/composio/composio-tools.d.ts +59 -0
- package/dist/plugins/composio/composio-tools.d.ts.map +1 -0
- package/dist/plugins/composio/composio-tools.js +88 -0
- package/dist/plugins/composio/composio-ucan.d.ts +14 -0
- package/dist/plugins/composio/composio-ucan.d.ts.map +1 -0
- package/dist/plugins/composio/composio-ucan.js +32 -0
- package/dist/plugins/composio/composio.plugin.d.ts +45 -0
- package/dist/plugins/composio/composio.plugin.d.ts.map +1 -0
- package/dist/plugins/composio/composio.plugin.js +116 -0
- package/dist/plugins/composio/index.d.ts +4 -0
- package/dist/plugins/composio/index.d.ts.map +1 -0
- package/dist/plugins/composio/index.js +3 -0
- package/dist/plugins/credits/claim-processing.module.d.ts +28 -0
- package/dist/plugins/credits/claim-processing.module.d.ts.map +1 -0
- package/dist/plugins/credits/claim-processing.module.js +37 -0
- package/dist/plugins/credits/claim-processing.service.d.ts +92 -0
- package/dist/plugins/credits/claim-processing.service.d.ts.map +1 -0
- package/dist/plugins/credits/claim-processing.service.js +406 -0
- package/dist/plugins/credits/credits-middleware.d.ts +23 -0
- package/dist/plugins/credits/credits-middleware.d.ts.map +1 -0
- package/dist/plugins/credits/credits-middleware.js +154 -0
- package/dist/plugins/credits/credits.plugin.d.ts +55 -0
- package/dist/plugins/credits/credits.plugin.d.ts.map +1 -0
- package/dist/plugins/credits/credits.plugin.js +95 -0
- package/dist/plugins/credits/file-processing-sink.module.d.ts +18 -0
- package/dist/plugins/credits/file-processing-sink.module.d.ts.map +1 -0
- package/dist/plugins/credits/file-processing-sink.module.js +56 -0
- package/dist/plugins/credits/index.d.ts +8 -0
- package/dist/plugins/credits/index.d.ts.map +1 -0
- package/dist/plugins/credits/index.js +7 -0
- package/dist/plugins/credits/subscription-sink.module.d.ts +23 -0
- package/dist/plugins/credits/subscription-sink.module.d.ts.map +1 -0
- package/dist/plugins/credits/subscription-sink.module.js +54 -0
- package/dist/plugins/credits/token-limiter.d.ts +139 -0
- package/dist/plugins/credits/token-limiter.d.ts.map +1 -0
- package/dist/plugins/credits/token-limiter.js +302 -0
- package/dist/plugins/domain-indexer/domain-indexer-agent.d.ts +7 -0
- package/dist/plugins/domain-indexer/domain-indexer-agent.d.ts.map +1 -0
- package/dist/plugins/domain-indexer/domain-indexer-agent.js +48 -0
- package/dist/plugins/domain-indexer/domain-indexer-tools.d.ts +8 -0
- package/dist/plugins/domain-indexer/domain-indexer-tools.d.ts.map +1 -0
- package/dist/plugins/domain-indexer/domain-indexer-tools.js +194 -0
- package/dist/plugins/domain-indexer/domain-indexer.plugin.d.ts +20 -0
- package/dist/plugins/domain-indexer/domain-indexer.plugin.d.ts.map +1 -0
- package/dist/plugins/domain-indexer/domain-indexer.plugin.js +78 -0
- package/dist/plugins/domain-indexer/index.d.ts +2 -0
- package/dist/plugins/domain-indexer/index.d.ts.map +1 -0
- package/dist/plugins/domain-indexer/index.js +1 -0
- package/dist/plugins/editor/apply-sandbox-output.d.ts +33 -0
- package/dist/plugins/editor/apply-sandbox-output.d.ts.map +1 -0
- package/dist/plugins/editor/apply-sandbox-output.js +302 -0
- package/dist/plugins/editor/block-actions.d.ts +84 -0
- package/dist/plugins/editor/block-actions.d.ts.map +1 -0
- package/dist/plugins/editor/block-actions.js +471 -0
- package/dist/plugins/editor/blocknote-helper.d.ts +147 -0
- package/dist/plugins/editor/blocknote-helper.d.ts.map +1 -0
- package/dist/plugins/editor/blocknote-helper.js +542 -0
- package/dist/plugins/editor/blocknote-tools.d.ts +364 -0
- package/dist/plugins/editor/blocknote-tools.d.ts.map +1 -0
- package/dist/plugins/editor/blocknote-tools.js +2123 -0
- package/dist/plugins/editor/editor-agent.d.ts +47 -0
- package/dist/plugins/editor/editor-agent.d.ts.map +1 -0
- package/dist/plugins/editor/editor-agent.js +158 -0
- package/dist/plugins/editor/editor-mx.d.ts +56 -0
- package/dist/plugins/editor/editor-mx.d.ts.map +1 -0
- package/dist/plugins/editor/editor-mx.js +100 -0
- package/dist/plugins/editor/editor.plugin.d.ts +32 -0
- package/dist/plugins/editor/editor.plugin.d.ts.map +1 -0
- package/dist/plugins/editor/editor.plugin.js +189 -0
- package/dist/plugins/editor/index.d.ts +6 -0
- package/dist/plugins/editor/index.d.ts.map +1 -0
- package/dist/plugins/editor/index.js +5 -0
- package/dist/plugins/editor/mint-invocation-tool.d.ts +72 -0
- package/dist/plugins/editor/mint-invocation-tool.d.ts.map +1 -0
- package/dist/plugins/editor/mint-invocation-tool.js +173 -0
- package/dist/plugins/editor/page-functions.d.ts +100 -0
- package/dist/plugins/editor/page-functions.d.ts.map +1 -0
- package/dist/plugins/editor/page-functions.js +317 -0
- package/dist/plugins/editor/page-tools.d.ts +75 -0
- package/dist/plugins/editor/page-tools.d.ts.map +1 -0
- package/dist/plugins/editor/page-tools.js +238 -0
- package/dist/plugins/editor/prompts.d.ts +22 -0
- package/dist/plugins/editor/prompts.d.ts.map +1 -0
- package/dist/plugins/editor/prompts.js +451 -0
- package/dist/plugins/editor/provider.d.ts +101 -0
- package/dist/plugins/editor/provider.d.ts.map +1 -0
- package/dist/plugins/editor/provider.js +249 -0
- package/dist/plugins/editor/standalone-editor-tool.d.ts +17 -0
- package/dist/plugins/editor/standalone-editor-tool.d.ts.map +1 -0
- package/dist/plugins/editor/standalone-editor-tool.js +136 -0
- package/dist/plugins/editor/survey-helpers.d.ts +112 -0
- package/dist/plugins/editor/survey-helpers.d.ts.map +1 -0
- package/dist/plugins/editor/survey-helpers.js +358 -0
- package/dist/plugins/firecrawl/firecrawl-agent.d.ts +7 -0
- package/dist/plugins/firecrawl/firecrawl-agent.d.ts.map +1 -0
- package/dist/plugins/firecrawl/firecrawl-agent.js +84 -0
- package/dist/plugins/firecrawl/firecrawl-tools.d.ts +37 -0
- package/dist/plugins/firecrawl/firecrawl-tools.d.ts.map +1 -0
- package/dist/plugins/firecrawl/firecrawl-tools.js +129 -0
- package/dist/plugins/firecrawl/firecrawl.plugin.d.ts +32 -0
- package/dist/plugins/firecrawl/firecrawl.plugin.d.ts.map +1 -0
- package/dist/plugins/firecrawl/firecrawl.plugin.js +69 -0
- package/dist/plugins/firecrawl/index.d.ts +3 -0
- package/dist/plugins/firecrawl/index.d.ts.map +1 -0
- package/dist/plugins/firecrawl/index.js +2 -0
- package/dist/plugins/index.d.ts +47 -0
- package/dist/plugins/index.d.ts.map +1 -0
- package/dist/plugins/index.js +69 -0
- package/dist/plugins/matrix-group-chats/channel-memory.module.d.ts +7 -0
- package/dist/plugins/matrix-group-chats/channel-memory.module.d.ts.map +1 -0
- package/dist/plugins/matrix-group-chats/channel-memory.module.js +22 -0
- package/dist/plugins/matrix-group-chats/channel-memory.repo.d.ts +62 -0
- package/dist/plugins/matrix-group-chats/channel-memory.repo.d.ts.map +1 -0
- package/dist/plugins/matrix-group-chats/channel-memory.repo.js +311 -0
- package/dist/plugins/matrix-group-chats/channel-memory.service.d.ts +89 -0
- package/dist/plugins/matrix-group-chats/channel-memory.service.d.ts.map +1 -0
- package/dist/plugins/matrix-group-chats/channel-memory.service.js +565 -0
- package/dist/plugins/matrix-group-chats/channel-memory.summarizer.d.ts +18 -0
- package/dist/plugins/matrix-group-chats/channel-memory.summarizer.d.ts.map +1 -0
- package/dist/plugins/matrix-group-chats/channel-memory.summarizer.js +128 -0
- package/dist/plugins/matrix-group-chats/channel-memory.types.d.ts +50 -0
- package/dist/plugins/matrix-group-chats/channel-memory.types.d.ts.map +1 -0
- package/dist/plugins/matrix-group-chats/channel-memory.types.js +2 -0
- package/dist/plugins/matrix-group-chats/guard.d.ts +83 -0
- package/dist/plugins/matrix-group-chats/guard.d.ts.map +1 -0
- package/dist/plugins/matrix-group-chats/guard.js +138 -0
- package/dist/plugins/matrix-group-chats/index.d.ts +59 -0
- package/dist/plugins/matrix-group-chats/index.d.ts.map +1 -0
- package/dist/plugins/matrix-group-chats/index.js +140 -0
- package/dist/plugins/matrix-group-chats/middleware.d.ts +31 -0
- package/dist/plugins/matrix-group-chats/middleware.d.ts.map +1 -0
- package/dist/plugins/matrix-group-chats/middleware.js +186 -0
- package/dist/plugins/matrix-group-chats/power-levels.d.ts +15 -0
- package/dist/plugins/matrix-group-chats/power-levels.d.ts.map +1 -0
- package/dist/plugins/matrix-group-chats/power-levels.js +36 -0
- package/dist/plugins/matrix-group-chats/room-info.d.ts +23 -0
- package/dist/plugins/matrix-group-chats/room-info.d.ts.map +1 -0
- package/dist/plugins/matrix-group-chats/room-info.js +34 -0
- package/dist/plugins/matrix-group-chats/tools.d.ts +8 -0
- package/dist/plugins/matrix-group-chats/tools.d.ts.map +1 -0
- package/dist/plugins/matrix-group-chats/tools.js +154 -0
- package/dist/plugins/memory/index.d.ts +5 -0
- package/dist/plugins/memory/index.d.ts.map +1 -0
- package/dist/plugins/memory/index.js +3 -0
- package/dist/plugins/memory/memory-tools.d.ts +52 -0
- package/dist/plugins/memory/memory-tools.d.ts.map +1 -0
- package/dist/plugins/memory/memory-tools.js +88 -0
- package/dist/plugins/memory/memory-ucan.d.ts +17 -0
- package/dist/plugins/memory/memory-ucan.d.ts.map +1 -0
- package/dist/plugins/memory/memory-ucan.js +43 -0
- package/dist/plugins/memory/memory.plugin.d.ts +51 -0
- package/dist/plugins/memory/memory.plugin.d.ts.map +1 -0
- package/dist/plugins/memory/memory.plugin.js +76 -0
- package/dist/plugins/memory/types.d.ts +75 -0
- package/dist/plugins/memory/types.d.ts.map +1 -0
- package/dist/plugins/memory/types.js +2 -0
- package/dist/plugins/portal/index.d.ts +3 -0
- package/dist/plugins/portal/index.d.ts.map +1 -0
- package/dist/plugins/portal/index.js +2 -0
- package/dist/plugins/portal/portal-agent.d.ts +9 -0
- package/dist/plugins/portal/portal-agent.d.ts.map +1 -0
- package/dist/plugins/portal/portal-agent.js +70 -0
- package/dist/plugins/portal/portal.plugin.d.ts +16 -0
- package/dist/plugins/portal/portal.plugin.d.ts.map +1 -0
- package/dist/plugins/portal/portal.plugin.js +115 -0
- package/dist/plugins/sandbox/index.d.ts +3 -0
- package/dist/plugins/sandbox/index.d.ts.map +1 -0
- package/dist/plugins/sandbox/index.js +2 -0
- package/dist/plugins/sandbox/sandbox-mcp.d.ts +46 -0
- package/dist/plugins/sandbox/sandbox-mcp.d.ts.map +1 -0
- package/dist/plugins/sandbox/sandbox-mcp.js +80 -0
- package/dist/plugins/sandbox/sandbox-write-blob.d.ts +22 -0
- package/dist/plugins/sandbox/sandbox-write-blob.d.ts.map +1 -0
- package/dist/plugins/sandbox/sandbox-write-blob.js +80 -0
- package/dist/plugins/sandbox/sandbox.plugin.d.ts +80 -0
- package/dist/plugins/sandbox/sandbox.plugin.d.ts.map +1 -0
- package/dist/plugins/sandbox/sandbox.plugin.js +204 -0
- package/dist/plugins/skills/index.d.ts +3 -0
- package/dist/plugins/skills/index.d.ts.map +1 -0
- package/dist/plugins/skills/index.js +2 -0
- package/dist/plugins/skills/skills-tools.d.ts +20 -0
- package/dist/plugins/skills/skills-tools.d.ts.map +1 -0
- package/dist/plugins/skills/skills-tools.js +204 -0
- package/dist/plugins/skills/skills-ucan.d.ts +24 -0
- package/dist/plugins/skills/skills-ucan.d.ts.map +1 -0
- package/dist/plugins/skills/skills-ucan.js +28 -0
- package/dist/plugins/skills/skills.plugin.d.ts +37 -0
- package/dist/plugins/skills/skills.plugin.d.ts.map +1 -0
- package/dist/plugins/skills/skills.plugin.js +82 -0
- package/dist/plugins/slack/index.d.ts +4 -0
- package/dist/plugins/slack/index.d.ts.map +1 -0
- package/dist/plugins/slack/index.js +3 -0
- package/dist/plugins/slack/slack.module.d.ts +9 -0
- package/dist/plugins/slack/slack.module.d.ts.map +1 -0
- package/dist/plugins/slack/slack.module.js +27 -0
- package/dist/plugins/slack/slack.plugin.d.ts +30 -0
- package/dist/plugins/slack/slack.plugin.d.ts.map +1 -0
- package/dist/plugins/slack/slack.plugin.js +40 -0
- package/dist/plugins/slack/slack.service.d.ts +32 -0
- package/dist/plugins/slack/slack.service.d.ts.map +1 -0
- package/dist/plugins/slack/slack.service.js +157 -0
- package/dist/plugins/user-preferences/index.d.ts +4 -0
- package/dist/plugins/user-preferences/index.d.ts.map +1 -0
- package/dist/plugins/user-preferences/index.js +3 -0
- package/dist/plugins/user-preferences/service/user-preferences.service.d.ts +61 -0
- package/dist/plugins/user-preferences/service/user-preferences.service.d.ts.map +1 -0
- package/dist/plugins/user-preferences/service/user-preferences.service.js +105 -0
- package/dist/plugins/user-preferences/user-preferences-http.module.d.ts +10 -0
- package/dist/plugins/user-preferences/user-preferences-http.module.d.ts.map +1 -0
- package/dist/plugins/user-preferences/user-preferences-http.module.js +23 -0
- package/dist/plugins/user-preferences/user-preferences-tool.d.ts +22 -0
- package/dist/plugins/user-preferences/user-preferences-tool.d.ts.map +1 -0
- package/dist/plugins/user-preferences/user-preferences-tool.js +103 -0
- package/dist/plugins/user-preferences/user-preferences.controller.d.ts +18 -0
- package/dist/plugins/user-preferences/user-preferences.controller.d.ts.map +1 -0
- package/dist/plugins/user-preferences/user-preferences.controller.js +72 -0
- package/dist/plugins/user-preferences/user-preferences.plugin.d.ts +27 -0
- package/dist/plugins/user-preferences/user-preferences.plugin.d.ts.map +1 -0
- package/dist/plugins/user-preferences/user-preferences.plugin.js +66 -0
- package/dist/registries/config-schema-registry.d.ts +24 -0
- package/dist/registries/config-schema-registry.d.ts.map +1 -0
- package/dist/registries/config-schema-registry.js +27 -0
- package/dist/registries/index.d.ts +13 -0
- package/dist/registries/index.d.ts.map +1 -0
- package/dist/registries/index.js +6 -0
- package/dist/registries/manifest-registry.d.ts +49 -0
- package/dist/registries/manifest-registry.d.ts.map +1 -0
- package/dist/registries/manifest-registry.js +53 -0
- package/dist/registries/middleware-registry.d.ts +41 -0
- package/dist/registries/middleware-registry.d.ts.map +1 -0
- package/dist/registries/middleware-registry.js +52 -0
- package/dist/registries/shared-state-registry.d.ts +41 -0
- package/dist/registries/shared-state-registry.d.ts.map +1 -0
- package/dist/registries/shared-state-registry.js +65 -0
- package/dist/registries/subagent-registry.d.ts +55 -0
- package/dist/registries/subagent-registry.d.ts.map +1 -0
- package/dist/registries/subagent-registry.js +106 -0
- package/dist/registries/test-fixtures.d.ts +47 -0
- package/dist/registries/test-fixtures.d.ts.map +1 -0
- package/dist/registries/test-fixtures.js +168 -0
- package/dist/registries/tool-registry.d.ts +74 -0
- package/dist/registries/tool-registry.d.ts.map +1 -0
- package/dist/registries/tool-registry.js +130 -0
- package/dist/runtime-context/ambient.d.ts +118 -0
- package/dist/runtime-context/ambient.d.ts.map +1 -0
- package/dist/runtime-context/ambient.js +1 -0
- package/dist/runtime-context/build-plugin.d.ts +20 -0
- package/dist/runtime-context/build-plugin.d.ts.map +1 -0
- package/dist/runtime-context/build-plugin.js +16 -0
- package/dist/runtime-context/build-runtime.d.ts +60 -0
- package/dist/runtime-context/build-runtime.d.ts.map +1 -0
- package/dist/runtime-context/build-runtime.js +81 -0
- package/dist/testing/create-test-runtime.d.ts +95 -0
- package/dist/testing/create-test-runtime.d.ts.map +1 -0
- package/dist/testing/create-test-runtime.js +302 -0
- package/dist/testing/index.d.ts +5 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +5 -0
- package/dist/testing/integration/chat-client.d.ts +143 -0
- package/dist/testing/integration/chat-client.d.ts.map +1 -0
- package/dist/testing/integration/chat-client.js +238 -0
- package/dist/testing/integration/harness.d.ts +189 -0
- package/dist/testing/integration/harness.d.ts.map +1 -0
- package/dist/testing/integration/harness.js +461 -0
- package/dist/testing/integration/index.d.ts +14 -0
- package/dist/testing/integration/index.d.ts.map +1 -0
- package/dist/testing/integration/index.js +18 -0
- package/dist/testing/integration/setup.d.ts +2 -0
- package/dist/testing/integration/setup.d.ts.map +1 -0
- package/dist/testing/integration/setup.js +41 -0
- package/dist/testing/integration/sse-parser.d.ts +99 -0
- package/dist/testing/integration/sse-parser.d.ts.map +1 -0
- package/dist/testing/integration/sse-parser.js +125 -0
- package/dist/testing/integration/ucan.d.ts +74 -0
- package/dist/testing/integration/ucan.d.ts.map +1 -0
- package/dist/testing/integration/ucan.js +95 -0
- package/dist/testing/integration/wait-for-matrix-loaded.d.ts +19 -0
- package/dist/testing/integration/wait-for-matrix-loaded.d.ts.map +1 -0
- package/dist/testing/integration/wait-for-matrix-loaded.js +31 -0
- package/dist/testing/mocks.d.ts +64 -0
- package/dist/testing/mocks.d.ts.map +1 -0
- package/dist/testing/mocks.js +141 -0
- package/dist/testing/nest-doubles.d.ts +10 -0
- package/dist/testing/nest-doubles.d.ts.map +1 -0
- package/dist/testing/nest-doubles.js +19 -0
- package/dist/utils/emoji.d.ts +3 -0
- package/dist/utils/emoji.d.ts.map +1 -0
- package/dist/utils/emoji.js +36 -0
- package/package.json +102 -0
|
@@ -0,0 +1,856 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
var UserMatrixSqliteSyncService_1;
|
|
11
|
+
import { MatrixManager } from '@ixo/matrix';
|
|
12
|
+
import { getMatrixHomeServerCroppedForDid } from '@ixo/oracles-chain-client';
|
|
13
|
+
import { Injectable, Logger, NotFoundException, } from '@nestjs/common';
|
|
14
|
+
import { createHash } from 'crypto';
|
|
15
|
+
import { Cron, CronExpression } from '@nestjs/schedule';
|
|
16
|
+
import { hours } from '@nestjs/throttler';
|
|
17
|
+
import { File } from 'node:buffer';
|
|
18
|
+
import fsSync from 'node:fs';
|
|
19
|
+
import * as fs from 'node:fs/promises';
|
|
20
|
+
import { promisify } from 'node:util';
|
|
21
|
+
import { gunzip, gzip } from 'node:zlib';
|
|
22
|
+
import Database from 'better-sqlite3';
|
|
23
|
+
import path from 'path';
|
|
24
|
+
import { deleteMediaFromRoom, getMediaFromRoom, getMediaFromRoomByStorageKey, uploadMediaToRoom, } from './matrix-upload-utils.js';
|
|
25
|
+
import { getBaseEnvConfig as getConfig } from '../../config/base-env-config.js';
|
|
26
|
+
const gzipAsync = promisify(gzip);
|
|
27
|
+
const gunzipAsync = promisify(gunzip);
|
|
28
|
+
/**
|
|
29
|
+
* Returns true if the error is permanent (data genuinely unrecoverable),
|
|
30
|
+
* meaning it's safe to create a fresh DB. All other errors are assumed
|
|
31
|
+
* transient and should propagate to prevent data loss.
|
|
32
|
+
*/
|
|
33
|
+
function isUnrecoverableDownloadError(error) {
|
|
34
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
35
|
+
// Crypto/decryption failures from Rust NAPI layer (hash mismatch, invalid key, corrupt JSON)
|
|
36
|
+
// These mean the encrypted payload is broken — retrying won't help
|
|
37
|
+
const cryptoPatterns = [
|
|
38
|
+
/decrypt/i,
|
|
39
|
+
/hash/i,
|
|
40
|
+
/mismatch/i,
|
|
41
|
+
/base64/i,
|
|
42
|
+
/serde/i,
|
|
43
|
+
/invalid.*key/i,
|
|
44
|
+
/missing field/i,
|
|
45
|
+
];
|
|
46
|
+
// Matrix-specific permanent errors
|
|
47
|
+
const matrixPatterns = [
|
|
48
|
+
/M_NOT_FOUND/, // media deleted/redacted from Matrix
|
|
49
|
+
/Event not found/, // event no longer exists
|
|
50
|
+
/not a media event/i, // event type mismatch
|
|
51
|
+
/mxcUrl.*does not begin/i, // malformed content.file.url
|
|
52
|
+
/M_FORBIDDEN/, // access permanently denied
|
|
53
|
+
];
|
|
54
|
+
return [...cryptoPatterns, ...matrixPatterns].some((p) => p.test(message));
|
|
55
|
+
}
|
|
56
|
+
const config = getConfig();
|
|
57
|
+
/** Configure a SQLite connection with busy timeout for safe concurrent access */
|
|
58
|
+
/** Configure a SQLite connection with pragmas for safe concurrent access on VPS */
|
|
59
|
+
function configureSqliteConnection(db) {
|
|
60
|
+
db.pragma('journal_mode = DELETE');
|
|
61
|
+
db.pragma('busy_timeout = 5000');
|
|
62
|
+
db.pragma('synchronous = NORMAL');
|
|
63
|
+
}
|
|
64
|
+
let UserMatrixSqliteSyncService = class UserMatrixSqliteSyncService {
|
|
65
|
+
static { UserMatrixSqliteSyncService_1 = this; }
|
|
66
|
+
static instance;
|
|
67
|
+
fileEventsDatabase;
|
|
68
|
+
constructor() {
|
|
69
|
+
// check if path exists
|
|
70
|
+
const pathExists = fsSync.existsSync(path.join(config.getOrThrow('SQLITE_DATABASE_PATH')));
|
|
71
|
+
if (!pathExists) {
|
|
72
|
+
fsSync.mkdirSync(path.join(config.getOrThrow('SQLITE_DATABASE_PATH')), {
|
|
73
|
+
recursive: true,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
this.fileEventsDatabase = new Database(path.join(config.getOrThrow('SQLITE_DATABASE_PATH'), 'file_events.db'));
|
|
77
|
+
configureSqliteConnection(this.fileEventsDatabase);
|
|
78
|
+
}
|
|
79
|
+
filePathCache = new Map();
|
|
80
|
+
dbConnectionCache = new Map();
|
|
81
|
+
/** Reference-counted active users — supports nested markUserActive/markUserInactive calls */
|
|
82
|
+
activeUsers = new Map();
|
|
83
|
+
downloadInProgress = new Map();
|
|
84
|
+
recoveryInProgress = new Map();
|
|
85
|
+
lastUploadedChecksum = new Map();
|
|
86
|
+
/**
|
|
87
|
+
* Users whose SQLite checkpoint has been synced from Matrix at least once
|
|
88
|
+
* in this process lifetime. We're a single-node deployment: after the
|
|
89
|
+
* first sync, the local copy IS the source of truth until shutdown (which
|
|
90
|
+
* uploads back to Matrix). Skipping re-syncs on subsequent requests for
|
|
91
|
+
* the same user is the dominant TTFB win.
|
|
92
|
+
*/
|
|
93
|
+
syncedUsers = new Set();
|
|
94
|
+
/** Prevents overlapping cron executions from interleaving I/O on the same files */
|
|
95
|
+
cronRunning = false;
|
|
96
|
+
markUserActive(userDid) {
|
|
97
|
+
const count = this.activeUsers.get(userDid) ?? 0;
|
|
98
|
+
this.activeUsers.set(userDid, count + 1);
|
|
99
|
+
}
|
|
100
|
+
markUserInactive(userDid) {
|
|
101
|
+
const count = this.activeUsers.get(userDid) ?? 0;
|
|
102
|
+
if (count <= 1) {
|
|
103
|
+
this.activeUsers.delete(userDid);
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
this.activeUsers.set(userDid, count - 1);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
isUserActive(userDid) {
|
|
110
|
+
return (this.activeUsers.get(userDid) ?? 0) > 0;
|
|
111
|
+
}
|
|
112
|
+
static createUserStorageKey(userDid) {
|
|
113
|
+
const key = `checkpoint_${userDid}_${config.getOrThrow('ORACLE_DID')}`;
|
|
114
|
+
return createHash('sha256').update(key).digest('hex').substring(0, 17);
|
|
115
|
+
}
|
|
116
|
+
static getUserCheckpointDbPath(userDid) {
|
|
117
|
+
const dbPath = path.join(UserMatrixSqliteSyncService_1.checkpointsFolder, userDid, `${UserMatrixSqliteSyncService_1.createUserStorageKey(userDid)}.db`);
|
|
118
|
+
return dbPath;
|
|
119
|
+
}
|
|
120
|
+
static checkpointsFolder = path.join(config.getOrThrow('SQLITE_DATABASE_PATH'), 'user_dbs');
|
|
121
|
+
async onModuleInit() {
|
|
122
|
+
// create checkpoints folder if it doesn't exist
|
|
123
|
+
const exists = await fs
|
|
124
|
+
.access(UserMatrixSqliteSyncService_1.checkpointsFolder)
|
|
125
|
+
.then(() => true)
|
|
126
|
+
.catch(() => false);
|
|
127
|
+
if (!exists) {
|
|
128
|
+
Logger.debug(`Creating checkpoints folder at ${UserMatrixSqliteSyncService_1.checkpointsFolder}`);
|
|
129
|
+
await fs.mkdir(UserMatrixSqliteSyncService_1.checkpointsFolder, {
|
|
130
|
+
recursive: true,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
this.fileEventsDatabase
|
|
134
|
+
.prepare('CREATE TABLE IF NOT EXISTS file_events (storage_key TEXT PRIMARY KEY, event_id TEXT, event TEXT)')
|
|
135
|
+
.run();
|
|
136
|
+
// Add content_checksum column if it doesn't exist (backward-compatible migration)
|
|
137
|
+
try {
|
|
138
|
+
this.fileEventsDatabase
|
|
139
|
+
.prepare('ALTER TABLE file_events ADD COLUMN content_checksum TEXT')
|
|
140
|
+
.run();
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
// Column already exists, ignore
|
|
144
|
+
}
|
|
145
|
+
// Populate in-memory checksum cache from DB
|
|
146
|
+
const rows = this.fileEventsDatabase
|
|
147
|
+
.prepare('SELECT storage_key, content_checksum FROM file_events WHERE content_checksum IS NOT NULL')
|
|
148
|
+
.all();
|
|
149
|
+
for (const row of rows) {
|
|
150
|
+
this.lastUploadedChecksum.set(row.storage_key, row.content_checksum);
|
|
151
|
+
}
|
|
152
|
+
// Seed filePathCache from disk so the upload cron can find checkpoint
|
|
153
|
+
// files that survived a restart (hybrid approach: scan once on startup,
|
|
154
|
+
// then use the cache for subsequent cron ticks).
|
|
155
|
+
try {
|
|
156
|
+
const userFolders = await fs.readdir(UserMatrixSqliteSyncService_1.checkpointsFolder);
|
|
157
|
+
for (const userDid of userFolders) {
|
|
158
|
+
const dbPath = UserMatrixSqliteSyncService_1.getUserCheckpointDbPath(userDid);
|
|
159
|
+
const fileExists = await fs
|
|
160
|
+
.access(dbPath)
|
|
161
|
+
.then(() => true)
|
|
162
|
+
.catch(() => false);
|
|
163
|
+
if (fileExists) {
|
|
164
|
+
this.filePathCache.set(userDid, {
|
|
165
|
+
filePath: dbPath,
|
|
166
|
+
lastAccessedAt: Date.now(),
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
if (this.filePathCache.size > 0) {
|
|
171
|
+
Logger.log(`Seeded filePathCache with ${this.filePathCache.size} existing checkpoint(s) from disk`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
// Checkpoints folder might be empty or inaccessible on first run
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Get or create database connection for a user.
|
|
180
|
+
* Ensures database exists and is synced from Matrix on the first request
|
|
181
|
+
* per user per process; subsequent calls reuse the local file.
|
|
182
|
+
* Includes automatic corruption recovery.
|
|
183
|
+
*/
|
|
184
|
+
async getUserDatabase(userDid) {
|
|
185
|
+
if (!this.syncedUsers.has(userDid)) {
|
|
186
|
+
await this.syncLocalStorageFromMatrixStorage({ userDid });
|
|
187
|
+
this.syncedUsers.add(userDid);
|
|
188
|
+
}
|
|
189
|
+
return this.openUserDatabaseFromDisk(userDid);
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Same as `getUserDatabase` but never triggers a Matrix → SQLite sync.
|
|
193
|
+
* Used by hot paths that follow an earlier `getUserDatabase` call within
|
|
194
|
+
* the same request (e.g. the fire-and-forget post-message sync).
|
|
195
|
+
*/
|
|
196
|
+
async getUserDatabaseNoSync(userDid) {
|
|
197
|
+
return this.openUserDatabaseFromDisk(userDid);
|
|
198
|
+
}
|
|
199
|
+
async openUserDatabaseFromDisk(userDid) {
|
|
200
|
+
const dbPath = UserMatrixSqliteSyncService_1.getUserCheckpointDbPath(userDid);
|
|
201
|
+
// Check cache
|
|
202
|
+
const cached = this.dbConnectionCache.get(userDid);
|
|
203
|
+
if (cached) {
|
|
204
|
+
cached.lastAccessedAt = Date.now();
|
|
205
|
+
return cached.db;
|
|
206
|
+
}
|
|
207
|
+
// Open and validate — recover from corruption if needed
|
|
208
|
+
let db = this.openAndValidateDatabase(dbPath, userDid);
|
|
209
|
+
if (!db) {
|
|
210
|
+
// Deduplicate concurrent recovery attempts for the same user
|
|
211
|
+
const existingRecovery = this.recoveryInProgress.get(userDid);
|
|
212
|
+
if (existingRecovery) {
|
|
213
|
+
// Wait for the in-flight recovery but don't skip init/caching below
|
|
214
|
+
db = await existingRecovery;
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
const recoveryPromise = this.recoverCorruptDatabase(userDid, dbPath);
|
|
218
|
+
this.recoveryInProgress.set(userDid, recoveryPromise);
|
|
219
|
+
try {
|
|
220
|
+
db = await recoveryPromise;
|
|
221
|
+
}
|
|
222
|
+
finally {
|
|
223
|
+
this.recoveryInProgress.delete(userDid);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
// Initialize sessions and calls tables if needed
|
|
228
|
+
try {
|
|
229
|
+
this.initializeSessionsAndCallsTables(db);
|
|
230
|
+
}
|
|
231
|
+
catch (error) {
|
|
232
|
+
// Prevent leaked DB handle if table init fails
|
|
233
|
+
try {
|
|
234
|
+
db.close();
|
|
235
|
+
}
|
|
236
|
+
catch {
|
|
237
|
+
// Ignore close errors
|
|
238
|
+
}
|
|
239
|
+
throw error;
|
|
240
|
+
}
|
|
241
|
+
// Cache it
|
|
242
|
+
this.dbConnectionCache.set(userDid, {
|
|
243
|
+
db,
|
|
244
|
+
lastAccessedAt: Date.now(),
|
|
245
|
+
});
|
|
246
|
+
return db;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Attempts cascading recovery when a local database is corrupt:
|
|
250
|
+
* 1. Clear local → re-download from Matrix → validate
|
|
251
|
+
* 2. If Matrix copy also corrupt → delete from Matrix → create fresh empty DB
|
|
252
|
+
*/
|
|
253
|
+
async recoverCorruptDatabase(userDid, dbPath) {
|
|
254
|
+
Logger.error(`[CORRUPTION DETECTED] Local SQLite database is corrupt for user ${userDid} at ${dbPath}. Attempting recovery from Matrix backup...`);
|
|
255
|
+
// Clear local corrupt file and re-download from Matrix
|
|
256
|
+
await this.clearLocalCheckpoint(userDid, dbPath);
|
|
257
|
+
await this.syncLocalStorageFromMatrixStorage({ userDid });
|
|
258
|
+
// Check if Matrix had a backup
|
|
259
|
+
const fileExists = await fs
|
|
260
|
+
.access(dbPath)
|
|
261
|
+
.then(() => true)
|
|
262
|
+
.catch(() => false);
|
|
263
|
+
if (fileExists) {
|
|
264
|
+
const db = this.openAndValidateDatabase(dbPath, userDid);
|
|
265
|
+
if (db)
|
|
266
|
+
return db;
|
|
267
|
+
// Matrix copy is also corrupt
|
|
268
|
+
Logger.error(`[CORRUPTION DETECTED] Matrix backup is ALSO corrupt for user ${userDid}. Deleting corrupt backup and starting fresh. User will lose session history.`);
|
|
269
|
+
await this.clearLocalCheckpoint(userDid, dbPath);
|
|
270
|
+
try {
|
|
271
|
+
await this.deleteUserStorageFromMatrix(userDid);
|
|
272
|
+
Logger.warn(`Deleted corrupt Matrix backup for user ${userDid}. Corruption loop broken.`);
|
|
273
|
+
}
|
|
274
|
+
catch (deleteError) {
|
|
275
|
+
Logger.error(`Failed to delete corrupt Matrix backup for user ${userDid}: ${deleteError}`);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
// Create a brand new empty database
|
|
279
|
+
Logger.warn(`Creating fresh database for user ${userDid} after corruption recovery. All previous sessions are lost.`);
|
|
280
|
+
const dir = path.dirname(dbPath);
|
|
281
|
+
await fs.mkdir(dir, { recursive: true });
|
|
282
|
+
const db = new Database(dbPath);
|
|
283
|
+
configureSqliteConnection(db);
|
|
284
|
+
// Ensure the fresh DB is tracked so the upload cron (which iterates
|
|
285
|
+
// filePathCache.keys()) will back it up to Matrix.
|
|
286
|
+
this.filePathCache.set(userDid, {
|
|
287
|
+
filePath: dbPath,
|
|
288
|
+
lastAccessedAt: Date.now(),
|
|
289
|
+
});
|
|
290
|
+
return db;
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Opens a SQLite database and validates it is not corrupt.
|
|
294
|
+
* Returns the Database instance if valid, or null if corrupt/missing.
|
|
295
|
+
*/
|
|
296
|
+
openAndValidateDatabase(dbPath, userDid) {
|
|
297
|
+
try {
|
|
298
|
+
if (!fsSync.existsSync(dbPath)) {
|
|
299
|
+
return null;
|
|
300
|
+
}
|
|
301
|
+
const db = new Database(dbPath);
|
|
302
|
+
configureSqliteConnection(db);
|
|
303
|
+
// Run integrity check — returns 'ok' if database is healthy
|
|
304
|
+
const result = db.pragma('integrity_check');
|
|
305
|
+
const isOk = result.length === 1 && result[0]?.integrity_check === 'ok';
|
|
306
|
+
if (!isOk) {
|
|
307
|
+
const details = result.map((r) => r.integrity_check).join('; ');
|
|
308
|
+
Logger.error(`[CORRUPTION DETECTED] PRAGMA integrity_check failed for user ${userDid}: ${details}`);
|
|
309
|
+
try {
|
|
310
|
+
db.close();
|
|
311
|
+
}
|
|
312
|
+
catch {
|
|
313
|
+
// Ignore close errors on corrupt DB
|
|
314
|
+
}
|
|
315
|
+
return null;
|
|
316
|
+
}
|
|
317
|
+
return db;
|
|
318
|
+
}
|
|
319
|
+
catch (error) {
|
|
320
|
+
Logger.error(`[CORRUPTION DETECTED] Failed to open SQLite database for user ${userDid} at ${dbPath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
321
|
+
return null;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Deletes local checkpoint file and clears all associated caches for a user.
|
|
326
|
+
*/
|
|
327
|
+
async clearLocalCheckpoint(userDid, dbPath) {
|
|
328
|
+
// Close cached connection if exists
|
|
329
|
+
const cached = this.dbConnectionCache.get(userDid);
|
|
330
|
+
if (cached) {
|
|
331
|
+
try {
|
|
332
|
+
cached.db.close();
|
|
333
|
+
}
|
|
334
|
+
catch {
|
|
335
|
+
// Ignore close errors
|
|
336
|
+
}
|
|
337
|
+
this.dbConnectionCache.delete(userDid);
|
|
338
|
+
}
|
|
339
|
+
// Clear file path cache, checksum cache, AND the "synced-once" flag.
|
|
340
|
+
// Dropping `syncedUsers` here is critical: without it, the next
|
|
341
|
+
// `getUserDatabase` call after the local file is deleted would skip the
|
|
342
|
+
// Matrix → SQLite re-download (because we'd think we're still synced),
|
|
343
|
+
// landing in the corruption-recovery path instead of a clean sync.
|
|
344
|
+
this.syncedUsers.delete(userDid);
|
|
345
|
+
this.filePathCache.delete(userDid);
|
|
346
|
+
const storageKey = UserMatrixSqliteSyncService_1.createUserStorageKey(userDid);
|
|
347
|
+
try {
|
|
348
|
+
this.fileEventsDatabase
|
|
349
|
+
.prepare('UPDATE file_events SET content_checksum = NULL WHERE storage_key = ?')
|
|
350
|
+
.run(storageKey);
|
|
351
|
+
// Clear in-memory cache AFTER successful DB update to keep them consistent
|
|
352
|
+
this.lastUploadedChecksum.delete(storageKey);
|
|
353
|
+
}
|
|
354
|
+
catch (error) {
|
|
355
|
+
// Still clear in-memory cache on DB failure — worst case is a redundant upload
|
|
356
|
+
this.lastUploadedChecksum.delete(storageKey);
|
|
357
|
+
Logger.warn(`Failed to clear content_checksum for ${storageKey}: ${error instanceof Error ? error.message : error}`);
|
|
358
|
+
}
|
|
359
|
+
// Delete local file + temp files + leftover WAL/SHM/journal files
|
|
360
|
+
for (const suffix of ['', '.tmp', '-wal', '-shm', '-journal']) {
|
|
361
|
+
try {
|
|
362
|
+
await fs.unlink(dbPath + suffix);
|
|
363
|
+
}
|
|
364
|
+
catch {
|
|
365
|
+
// File may not exist, that's fine
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
initializeSessionsAndCallsTables(db) {
|
|
370
|
+
db.exec(`
|
|
371
|
+
CREATE TABLE IF NOT EXISTS sessions (
|
|
372
|
+
session_id TEXT PRIMARY KEY,
|
|
373
|
+
title TEXT,
|
|
374
|
+
last_updated_at TEXT NOT NULL,
|
|
375
|
+
created_at TEXT NOT NULL,
|
|
376
|
+
oracle_name TEXT NOT NULL,
|
|
377
|
+
oracle_did TEXT NOT NULL,
|
|
378
|
+
oracle_entity_did TEXT NOT NULL,
|
|
379
|
+
last_processed_count INTEGER,
|
|
380
|
+
user_context TEXT,
|
|
381
|
+
room_id TEXT,
|
|
382
|
+
slack_thread_ts TEXT
|
|
383
|
+
);
|
|
384
|
+
|
|
385
|
+
CREATE TABLE IF NOT EXISTS calls (
|
|
386
|
+
call_id TEXT PRIMARY KEY,
|
|
387
|
+
session_id TEXT NOT NULL,
|
|
388
|
+
created_at TEXT NOT NULL
|
|
389
|
+
);
|
|
390
|
+
|
|
391
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_updated ON sessions(last_updated_at);
|
|
392
|
+
CREATE INDEX IF NOT EXISTS idx_calls_session ON calls(session_id);
|
|
393
|
+
`);
|
|
394
|
+
}
|
|
395
|
+
async localStorageCacheCleanUpTask() {
|
|
396
|
+
if (this.cronRunning) {
|
|
397
|
+
Logger.debug('Skipping hourly cleanup — another cron task is still running');
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
this.cronRunning = true;
|
|
401
|
+
try {
|
|
402
|
+
const now = Date.now();
|
|
403
|
+
// Close idle database connections
|
|
404
|
+
for (const [userDid, { db, lastAccessedAt },] of this.dbConnectionCache.entries()) {
|
|
405
|
+
if (this.isUserActive(userDid)) {
|
|
406
|
+
Logger.debug(`Skipping DB cleanup for active user ${userDid}`);
|
|
407
|
+
continue;
|
|
408
|
+
}
|
|
409
|
+
if (now - lastAccessedAt > hours(1)) {
|
|
410
|
+
try {
|
|
411
|
+
// Sync to Matrix before closing
|
|
412
|
+
await this.uploadCheckpointToMatrixStorage({ userDid });
|
|
413
|
+
// Close connection (db is already from the loop iteration)
|
|
414
|
+
db.close();
|
|
415
|
+
this.dbConnectionCache.delete(userDid);
|
|
416
|
+
Logger.log(`Closed idle database connection for user ${userDid}`);
|
|
417
|
+
}
|
|
418
|
+
catch (error) {
|
|
419
|
+
Logger.error(`Failed to cleanup DB connection for user ${userDid}`, error);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
// Clean up file cache
|
|
424
|
+
for (const [userDid, { lastAccessedAt },] of this.filePathCache.entries()) {
|
|
425
|
+
if (this.isUserActive(userDid)) {
|
|
426
|
+
Logger.debug(`Skipping file cache cleanup for active user ${userDid}`);
|
|
427
|
+
continue;
|
|
428
|
+
}
|
|
429
|
+
if (now - lastAccessedAt > hours(1)) {
|
|
430
|
+
try {
|
|
431
|
+
await this.uploadCheckpointToMatrixStorage({ userDid });
|
|
432
|
+
}
|
|
433
|
+
catch (error) {
|
|
434
|
+
Logger.error(`Failed to sync checkpoint file to matrix storage for user ${userDid}`, error);
|
|
435
|
+
// failed to sync, continue to next user so we can retry next hour
|
|
436
|
+
continue;
|
|
437
|
+
}
|
|
438
|
+
// sync successful, delete local cache
|
|
439
|
+
const userFolder = path.join(UserMatrixSqliteSyncService_1.checkpointsFolder, userDid);
|
|
440
|
+
const storageKey = UserMatrixSqliteSyncService_1.createUserStorageKey(userDid);
|
|
441
|
+
try {
|
|
442
|
+
await fs.rm(userFolder, { recursive: true });
|
|
443
|
+
Logger.log(`Deleted Local Storage checkpoint folder for user ${userDid} and path ${userFolder}`);
|
|
444
|
+
}
|
|
445
|
+
catch (error) {
|
|
446
|
+
Logger.error(`Failed to delete local checkpoint folder for user ${userDid}: ${error instanceof Error ? error.message : error}`);
|
|
447
|
+
}
|
|
448
|
+
// Always clear caches regardless of fs.rm result — stale cache
|
|
449
|
+
// entries are worse than missing ones (next access re-downloads).
|
|
450
|
+
// `syncedUsers` is cleared here for the same reason: the local
|
|
451
|
+
// file is gone, the next request must re-pull from Matrix.
|
|
452
|
+
this.syncedUsers.delete(userDid);
|
|
453
|
+
this.filePathCache.delete(userDid);
|
|
454
|
+
this.lastUploadedChecksum.delete(storageKey);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
finally {
|
|
459
|
+
this.cronRunning = false;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* Get the singleton instance of UserMatrixSqliteSyncService
|
|
464
|
+
* @param maxCacheSize - Maximum number of cached files (default: 100)
|
|
465
|
+
* @returns The singleton instance
|
|
466
|
+
*/
|
|
467
|
+
static getInstance() {
|
|
468
|
+
if (!UserMatrixSqliteSyncService_1.instance) {
|
|
469
|
+
UserMatrixSqliteSyncService_1.instance = new UserMatrixSqliteSyncService_1();
|
|
470
|
+
}
|
|
471
|
+
return UserMatrixSqliteSyncService_1.instance;
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* Load the checkpoint SQLite file for a user.
|
|
475
|
+
* First checks the local cache, then matrix storage if not cached.
|
|
476
|
+
* @param userDid - The user's DID identifier
|
|
477
|
+
* @returns Promise resolving to the SQLite file buffer
|
|
478
|
+
*/
|
|
479
|
+
async syncLocalStorageFromMatrixStorage(params) {
|
|
480
|
+
const { userDid } = params;
|
|
481
|
+
// If a download is already in progress for this user, await it instead of starting another
|
|
482
|
+
const existingDownload = this.downloadInProgress.get(userDid);
|
|
483
|
+
if (existingDownload) {
|
|
484
|
+
Logger.debug(`Download already in progress for user ${userDid}, awaiting existing download`);
|
|
485
|
+
return existingDownload;
|
|
486
|
+
}
|
|
487
|
+
const downloadPromise = this._syncLocalStorageFromMatrixStorage(userDid);
|
|
488
|
+
this.downloadInProgress.set(userDid, downloadPromise);
|
|
489
|
+
try {
|
|
490
|
+
await downloadPromise;
|
|
491
|
+
}
|
|
492
|
+
finally {
|
|
493
|
+
this.downloadInProgress.delete(userDid);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
async _syncLocalStorageFromMatrixStorage(userDid) {
|
|
497
|
+
const storageKey = UserMatrixSqliteSyncService_1.createUserStorageKey(userDid);
|
|
498
|
+
const checkpointPath = UserMatrixSqliteSyncService_1.getUserCheckpointDbPath(userDid);
|
|
499
|
+
Logger.debug(`Syncing checkpoint for user ${userDid}, storageKey: ${storageKey}, path: ${checkpointPath}`);
|
|
500
|
+
// Ensure the user's checkpoint directory exists
|
|
501
|
+
const userCheckpointDir = path.dirname(checkpointPath);
|
|
502
|
+
const dirExists = await fs
|
|
503
|
+
.access(userCheckpointDir)
|
|
504
|
+
.then(() => true)
|
|
505
|
+
.catch(() => false);
|
|
506
|
+
if (!dirExists) {
|
|
507
|
+
Logger.debug(`Creating checkpoint directory for user ${userDid}: ${userCheckpointDir}`);
|
|
508
|
+
await fs.mkdir(userCheckpointDir, { recursive: true });
|
|
509
|
+
}
|
|
510
|
+
// check if file exists
|
|
511
|
+
const exists = await fs
|
|
512
|
+
.access(checkpointPath)
|
|
513
|
+
.then(() => true)
|
|
514
|
+
.catch(() => false);
|
|
515
|
+
if (exists) {
|
|
516
|
+
Logger.debug(`Checkpoint file already exists locally for user ${userDid} at ${checkpointPath}`);
|
|
517
|
+
this.filePathCache.set(userDid, {
|
|
518
|
+
filePath: checkpointPath,
|
|
519
|
+
lastAccessedAt: Date.now(),
|
|
520
|
+
});
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
Logger.debug(`Checkpoint file not found locally for user ${userDid}, attempting to download from Matrix`);
|
|
524
|
+
let userDB = null;
|
|
525
|
+
// Step 1: Try cached event lookup (local SQLite — independent concern)
|
|
526
|
+
let cachedEvent;
|
|
527
|
+
try {
|
|
528
|
+
const cachedEventText = this.fileEventsDatabase
|
|
529
|
+
.prepare('SELECT event FROM file_events WHERE storage_key = ?')
|
|
530
|
+
.get(storageKey);
|
|
531
|
+
cachedEvent = cachedEventText
|
|
532
|
+
? JSON.parse(cachedEventText.event)
|
|
533
|
+
: undefined;
|
|
534
|
+
}
|
|
535
|
+
catch (cacheError) {
|
|
536
|
+
// file_events.db corrupt or locked — skip cache, fall through to direct Matrix lookup
|
|
537
|
+
Logger.warn(`Failed to read cached event for user ${userDid}, falling through to Matrix lookup: ${cacheError instanceof Error ? cacheError.message : String(cacheError)}`);
|
|
538
|
+
}
|
|
539
|
+
// Step 2: Download from Matrix
|
|
540
|
+
try {
|
|
541
|
+
if (cachedEvent) {
|
|
542
|
+
const result = await getMediaFromRoom(undefined, undefined, cachedEvent);
|
|
543
|
+
userDB = {
|
|
544
|
+
...result,
|
|
545
|
+
contentInfo: {
|
|
546
|
+
...result.contentInfo,
|
|
547
|
+
storageKey,
|
|
548
|
+
},
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
else {
|
|
552
|
+
const mxManager = MatrixManager.getInstance();
|
|
553
|
+
const userHomeServer = await getMatrixHomeServerCroppedForDid(userDid);
|
|
554
|
+
const { roomId } = await mxManager.getOracleRoomIdWithHomeServer({
|
|
555
|
+
userDid,
|
|
556
|
+
oracleEntityDid: config.getOrThrow('ORACLE_ENTITY_DID'),
|
|
557
|
+
userHomeServer,
|
|
558
|
+
});
|
|
559
|
+
if (!roomId) {
|
|
560
|
+
throw new NotFoundException('Room not found or Invalid Session Id');
|
|
561
|
+
}
|
|
562
|
+
Logger.debug(`Downloading checkpoint from Matrix room ${roomId} for user ${userDid}`);
|
|
563
|
+
userDB = await getMediaFromRoomByStorageKey(roomId, storageKey);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
catch (error) {
|
|
567
|
+
if (isUnrecoverableDownloadError(error)) {
|
|
568
|
+
// Permanent failure — data genuinely unrecoverable, safe to start fresh
|
|
569
|
+
Logger.warn(`Unrecoverable download failure for user ${userDid}, will start with fresh database: ${error instanceof Error ? error.message : String(error)}`);
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
// Transient/unknown error — let it propagate so the request fails with 500
|
|
573
|
+
// and the user retries later. This prevents creating an empty DB that would
|
|
574
|
+
// overwrite the good Matrix backup on the next upload cron cycle.
|
|
575
|
+
throw error;
|
|
576
|
+
}
|
|
577
|
+
if (!userDB) {
|
|
578
|
+
Logger.debug(`No checkpoint found in Matrix for user ${userDid} with storageKey ${storageKey}, this is expected for new users`);
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
581
|
+
// Decompress the checkpoint
|
|
582
|
+
const SQLITE_MAGIC = Buffer.from('SQLite format 3\0');
|
|
583
|
+
let decompressedBuffer;
|
|
584
|
+
try {
|
|
585
|
+
decompressedBuffer = await gunzipAsync(userDB.mediaBuffer);
|
|
586
|
+
Logger.log(`Decompressed checkpoint for user ${userDid}: ${bytesToHumanReadable(userDB.mediaBuffer.length)} -> ${bytesToHumanReadable(decompressedBuffer.length)}`);
|
|
587
|
+
}
|
|
588
|
+
catch (_error) {
|
|
589
|
+
// Decompression failed — check if the raw buffer is a valid uncompressed SQLite file
|
|
590
|
+
if (userDB.mediaBuffer.length >= 16 &&
|
|
591
|
+
userDB.mediaBuffer.subarray(0, 16).equals(SQLITE_MAGIC)) {
|
|
592
|
+
Logger.warn(`Checkpoint for user ${userDid} is uncompressed SQLite (legacy format), using as-is`);
|
|
593
|
+
decompressedBuffer = userDB.mediaBuffer;
|
|
594
|
+
}
|
|
595
|
+
else {
|
|
596
|
+
Logger.error(`Checkpoint for user ${userDid} is neither valid gzip nor valid SQLite — skipping download to prevent corruption. Raw bytes (first 16): ${userDB.mediaBuffer.subarray(0, 16).toString('hex')}`);
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
// Validate decompressed data is a valid SQLite file
|
|
601
|
+
if (decompressedBuffer.length < 16 ||
|
|
602
|
+
!decompressedBuffer.subarray(0, 16).equals(SQLITE_MAGIC)) {
|
|
603
|
+
Logger.error(`Decompressed checkpoint for user ${userDid} does not have valid SQLite header — skipping to prevent corruption. Header bytes: ${decompressedBuffer.subarray(0, Math.min(16, decompressedBuffer.length)).toString('hex')}`);
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
606
|
+
Logger.debug(`Saving checkpoint to local cache for user ${userDid} at ${checkpointPath}`);
|
|
607
|
+
// Atomic write: write to temp file then rename (rename is atomic on POSIX)
|
|
608
|
+
const tmpPath = checkpointPath + '.tmp';
|
|
609
|
+
try {
|
|
610
|
+
await fs.writeFile(tmpPath, decompressedBuffer);
|
|
611
|
+
await fs.rename(tmpPath, checkpointPath);
|
|
612
|
+
}
|
|
613
|
+
catch (error) {
|
|
614
|
+
// Clean up orphaned temp file on failure
|
|
615
|
+
try {
|
|
616
|
+
await fs.unlink(tmpPath);
|
|
617
|
+
}
|
|
618
|
+
catch {
|
|
619
|
+
// Ignore cleanup errors
|
|
620
|
+
}
|
|
621
|
+
throw error;
|
|
622
|
+
}
|
|
623
|
+
// Update cache AFTER file is successfully written to disk
|
|
624
|
+
this.filePathCache.set(userDid, {
|
|
625
|
+
filePath: checkpointPath,
|
|
626
|
+
lastAccessedAt: Date.now(),
|
|
627
|
+
});
|
|
628
|
+
Logger.debug(`Successfully saved checkpoint for user ${userDid} at ${checkpointPath}`);
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* Sync checkpoint file from local cache to S3.
|
|
633
|
+
* @param userDid - The user's DID identifier
|
|
634
|
+
* @returns Promise that resolves when sync is complete
|
|
635
|
+
*/
|
|
636
|
+
async uploadCheckpointToMatrixStorage(params) {
|
|
637
|
+
const { userDid } = params;
|
|
638
|
+
const storageKey = UserMatrixSqliteSyncService_1.createUserStorageKey(userDid);
|
|
639
|
+
const checkpointPath = UserMatrixSqliteSyncService_1.getUserCheckpointDbPath(userDid);
|
|
640
|
+
Logger.debug(`Uploading checkpoint for user ${userDid}, storageKey: ${storageKey}, path: ${checkpointPath}`);
|
|
641
|
+
const exists = await fs
|
|
642
|
+
.access(checkpointPath)
|
|
643
|
+
.then(() => true)
|
|
644
|
+
.catch(() => false);
|
|
645
|
+
if (!exists) {
|
|
646
|
+
Logger.warn(`Checkpoint file not found for user ${userDid} at ${checkpointPath}`);
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
649
|
+
// Handle open database connections — don't close if user has active request
|
|
650
|
+
const cached = this.dbConnectionCache.get(userDid);
|
|
651
|
+
if (cached) {
|
|
652
|
+
if (this.isUserActive(userDid)) {
|
|
653
|
+
// User has an in-flight request — in DELETE journal mode the DB file may be
|
|
654
|
+
// inconsistent mid-transaction, so skip upload. Next cron cycle will pick it up.
|
|
655
|
+
Logger.debug(`Skipping upload for active user ${userDid}, will retry next cycle`);
|
|
656
|
+
return;
|
|
657
|
+
}
|
|
658
|
+
else {
|
|
659
|
+
// No active request — safe to close
|
|
660
|
+
try {
|
|
661
|
+
cached.db.close();
|
|
662
|
+
this.dbConnectionCache.delete(userDid);
|
|
663
|
+
Logger.debug(`Closed cached database connection for user ${userDid}`);
|
|
664
|
+
}
|
|
665
|
+
catch (error) {
|
|
666
|
+
Logger.warn(`Failed to close cached database connection for user ${userDid}: ${error}`);
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
// Compute checksum via streaming to avoid loading the entire DB into memory.
|
|
671
|
+
// Streaming reads ~64KB chunks at a time instead of the full file (which can be 100MB+).
|
|
672
|
+
const currentChecksum = await computeFileChecksum(checkpointPath);
|
|
673
|
+
const lastChecksum = this.lastUploadedChecksum.get(storageKey);
|
|
674
|
+
if (currentChecksum === lastChecksum) {
|
|
675
|
+
Logger.debug(`Skipping upload for user ${userDid} — checkpoint unchanged (checksum: ${currentChecksum.substring(0, 12)}...)`);
|
|
676
|
+
return;
|
|
677
|
+
}
|
|
678
|
+
// Only load file into memory when we know the content has changed and needs uploading
|
|
679
|
+
Logger.debug(`Reading checkpoint file for user ${userDid} from ${checkpointPath}`);
|
|
680
|
+
const checkpoint = await fs.readFile(checkpointPath);
|
|
681
|
+
const originalSize = checkpoint.length;
|
|
682
|
+
// Compress the database file with gzip before upload
|
|
683
|
+
const compressedCheckpoint = await gzipAsync(checkpoint);
|
|
684
|
+
const compressedSize = compressedCheckpoint.length;
|
|
685
|
+
const compressionRatio = ((1 - compressedSize / originalSize) *
|
|
686
|
+
100).toFixed(1);
|
|
687
|
+
Logger.log(`Checkpoint for user ${userDid}: ${bytesToHumanReadable(originalSize)} -> ${bytesToHumanReadable(compressedSize)} (${compressionRatio}% reduction)`);
|
|
688
|
+
const mxManager = MatrixManager.getInstance();
|
|
689
|
+
const userHomeServer = await getMatrixHomeServerCroppedForDid(userDid);
|
|
690
|
+
const { roomId } = await mxManager.getOracleRoomIdWithHomeServer({
|
|
691
|
+
userDid,
|
|
692
|
+
oracleEntityDid: config.getOrThrow('ORACLE_ENTITY_DID'),
|
|
693
|
+
userHomeServer,
|
|
694
|
+
});
|
|
695
|
+
if (!roomId) {
|
|
696
|
+
throw new NotFoundException('Room not found or Invalid Session Id');
|
|
697
|
+
}
|
|
698
|
+
Logger.debug(`Uploading compressed checkpoint to Matrix room ${roomId} for user ${userDid}`);
|
|
699
|
+
const event = await uploadMediaToRoom(roomId, new File([compressedCheckpoint], `${storageKey}.db.gz`, {
|
|
700
|
+
type: 'application/gzip',
|
|
701
|
+
lastModified: Date.now(),
|
|
702
|
+
}), storageKey);
|
|
703
|
+
await this.saveFileEventToDB({
|
|
704
|
+
eventId: event.eventId,
|
|
705
|
+
storageKey: event.storageKey,
|
|
706
|
+
event: event.event,
|
|
707
|
+
contentChecksum: currentChecksum,
|
|
708
|
+
});
|
|
709
|
+
Logger.log(`Successfully uploaded checkpoint to Matrix for user ${userDid}`);
|
|
710
|
+
}
|
|
711
|
+
// Run at :10, :20, :30, :40, :50 — skips :00 to avoid overlapping with the hourly cleanup cron
|
|
712
|
+
async uploadCheckpointToMatrixStorageTask() {
|
|
713
|
+
if (this.cronRunning) {
|
|
714
|
+
Logger.debug('Skipping upload task — another cron task is still running');
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
717
|
+
this.cronRunning = true;
|
|
718
|
+
try {
|
|
719
|
+
Logger.log(`Uploading checkpoint to Matrix storage task started`);
|
|
720
|
+
// Iterate cached file paths instead of scanning the filesystem —
|
|
721
|
+
// only users with known local checkpoints need uploading.
|
|
722
|
+
for (const userDid of this.filePathCache.keys()) {
|
|
723
|
+
try {
|
|
724
|
+
await this.uploadCheckpointToMatrixStorage({ userDid });
|
|
725
|
+
}
|
|
726
|
+
catch (error) {
|
|
727
|
+
Logger.error(`Failed to upload checkpoint to Matrix storage for user ${userDid}`, error instanceof Error ? error.message : String(error), 'File path: ' +
|
|
728
|
+
UserMatrixSqliteSyncService_1.getUserCheckpointDbPath(userDid), 'File Size before gzip: ' +
|
|
729
|
+
bytesToHumanReadable(await fs
|
|
730
|
+
.stat(UserMatrixSqliteSyncService_1.getUserCheckpointDbPath(userDid))
|
|
731
|
+
.then((stats) => stats.size)));
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
finally {
|
|
736
|
+
this.cronRunning = false;
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
/**
|
|
740
|
+
* Deletes user storage from Matrix and cleans up local cache
|
|
741
|
+
* @param userDid The user DID
|
|
742
|
+
* @param storageKey Optional storage key. If not provided, uses the default user storage key
|
|
743
|
+
* @returns True if deletion was successful, false if not found
|
|
744
|
+
*/
|
|
745
|
+
async deleteUserStorageFromMatrix(userDid, storageKey) {
|
|
746
|
+
const key = storageKey || UserMatrixSqliteSyncService_1.createUserStorageKey(userDid);
|
|
747
|
+
Logger.debug(`Deleting storage for user ${userDid} with storageKey ${key}`);
|
|
748
|
+
// Get the user's Matrix room
|
|
749
|
+
const mxManager = MatrixManager.getInstance();
|
|
750
|
+
const userHomeServer = await getMatrixHomeServerCroppedForDid(userDid);
|
|
751
|
+
const { roomId } = await mxManager.getOracleRoomIdWithHomeServer({
|
|
752
|
+
userDid,
|
|
753
|
+
oracleEntityDid: config.getOrThrow('ORACLE_ENTITY_DID'),
|
|
754
|
+
userHomeServer,
|
|
755
|
+
});
|
|
756
|
+
if (!roomId) {
|
|
757
|
+
Logger.warn(`No Matrix room found for user ${userDid}, cannot delete storage`);
|
|
758
|
+
return false;
|
|
759
|
+
}
|
|
760
|
+
// Delete from Matrix
|
|
761
|
+
const deleted = await deleteMediaFromRoom(roomId, key);
|
|
762
|
+
if (deleted) {
|
|
763
|
+
// Clean up local cache
|
|
764
|
+
try {
|
|
765
|
+
// Delete from file events database
|
|
766
|
+
this.fileEventsDatabase
|
|
767
|
+
.prepare('DELETE FROM file_events WHERE storage_key = ?')
|
|
768
|
+
.run(key);
|
|
769
|
+
Logger.debug(`Deleted file event cache for storageKey ${key} from database`);
|
|
770
|
+
}
|
|
771
|
+
catch (error) {
|
|
772
|
+
Logger.warn(`Failed to delete file event cache for storageKey ${key}:`, error);
|
|
773
|
+
}
|
|
774
|
+
// Delete local file if it exists
|
|
775
|
+
try {
|
|
776
|
+
const dbPath = UserMatrixSqliteSyncService_1.getUserCheckpointDbPath(userDid);
|
|
777
|
+
const exists = await fs
|
|
778
|
+
.access(dbPath)
|
|
779
|
+
.then(() => true)
|
|
780
|
+
.catch(() => false);
|
|
781
|
+
if (exists) {
|
|
782
|
+
await fs.unlink(dbPath);
|
|
783
|
+
Logger.debug(`Deleted local checkpoint file at ${dbPath}`);
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
catch (error) {
|
|
787
|
+
Logger.warn(`Failed to delete local checkpoint file for user ${userDid}:`, error);
|
|
788
|
+
}
|
|
789
|
+
// Clear database connection cache
|
|
790
|
+
const cached = this.dbConnectionCache.get(userDid);
|
|
791
|
+
if (cached) {
|
|
792
|
+
try {
|
|
793
|
+
cached.db.close();
|
|
794
|
+
this.dbConnectionCache.delete(userDid);
|
|
795
|
+
Logger.debug(`Closed and cleared database connection for ${userDid}`);
|
|
796
|
+
}
|
|
797
|
+
catch (error) {
|
|
798
|
+
Logger.warn(`Failed to close database connection for ${userDid}:`, error);
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
// Clear file path cache and checksum cache
|
|
802
|
+
this.filePathCache.delete(userDid);
|
|
803
|
+
this.lastUploadedChecksum.delete(key);
|
|
804
|
+
Logger.log(`Successfully deleted storage for user ${userDid} with storageKey ${key}`);
|
|
805
|
+
}
|
|
806
|
+
return deleted;
|
|
807
|
+
}
|
|
808
|
+
async saveFileEventToDB({ eventId, storageKey, event, contentChecksum, }) {
|
|
809
|
+
this.fileEventsDatabase
|
|
810
|
+
.prepare('INSERT OR REPLACE INTO file_events (storage_key, event_id, event, content_checksum) VALUES (?, ?, ?, ?)')
|
|
811
|
+
.run(storageKey, eventId, JSON.stringify(event), contentChecksum ?? null);
|
|
812
|
+
// Update in-memory cache
|
|
813
|
+
if (contentChecksum) {
|
|
814
|
+
this.lastUploadedChecksum.set(storageKey, contentChecksum);
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
};
|
|
818
|
+
__decorate([
|
|
819
|
+
Cron(CronExpression.EVERY_HOUR),
|
|
820
|
+
__metadata("design:type", Function),
|
|
821
|
+
__metadata("design:paramtypes", []),
|
|
822
|
+
__metadata("design:returntype", Promise)
|
|
823
|
+
], UserMatrixSqliteSyncService.prototype, "localStorageCacheCleanUpTask", null);
|
|
824
|
+
__decorate([
|
|
825
|
+
Cron('0 10,20,30,40,50 * * * *'),
|
|
826
|
+
__metadata("design:type", Function),
|
|
827
|
+
__metadata("design:paramtypes", []),
|
|
828
|
+
__metadata("design:returntype", Promise)
|
|
829
|
+
], UserMatrixSqliteSyncService.prototype, "uploadCheckpointToMatrixStorageTask", null);
|
|
830
|
+
UserMatrixSqliteSyncService = UserMatrixSqliteSyncService_1 = __decorate([
|
|
831
|
+
Injectable(),
|
|
832
|
+
__metadata("design:paramtypes", [])
|
|
833
|
+
], UserMatrixSqliteSyncService);
|
|
834
|
+
export { UserMatrixSqliteSyncService };
|
|
835
|
+
/**
|
|
836
|
+
* Compute SHA-256 checksum of a file using streaming reads.
|
|
837
|
+
* Reads in ~64KB chunks to avoid loading the entire file into memory,
|
|
838
|
+
* which matters for large SQLite databases (100MB+).
|
|
839
|
+
*/
|
|
840
|
+
function computeFileChecksum(filePath) {
|
|
841
|
+
return new Promise((resolve, reject) => {
|
|
842
|
+
const hash = createHash('sha256');
|
|
843
|
+
const stream = fsSync.createReadStream(filePath);
|
|
844
|
+
stream.on('data', (chunk) => hash.update(chunk));
|
|
845
|
+
stream.on('end', () => resolve(hash.digest('hex')));
|
|
846
|
+
stream.on('error', (err) => {
|
|
847
|
+
stream.destroy();
|
|
848
|
+
reject(err);
|
|
849
|
+
});
|
|
850
|
+
});
|
|
851
|
+
}
|
|
852
|
+
const bytesToHumanReadable = (bytes) => {
|
|
853
|
+
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
|
854
|
+
const index = Math.floor(Math.log(bytes) / Math.log(1024));
|
|
855
|
+
return (bytes / Math.pow(1024, index)).toFixed(2) + ' ' + units[index];
|
|
856
|
+
};
|