aimeat 1.2.0
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/.env.example +300 -0
- package/dist/bin/aimeat.d.ts +3 -0
- package/dist/bin/aimeat.d.ts.map +1 -0
- package/dist/bin/aimeat.js +22 -0
- package/dist/bin/aimeat.js.map +1 -0
- package/dist/locales/en.json +4566 -0
- package/dist/locales/fi.json +4566 -0
- package/dist/public/BingSiteAuth.xml +4 -0
- package/dist/public/agent-consent.html +521 -0
- package/dist/public/aimeat-developers.html +679 -0
- package/dist/public/assets/mcp_1_add_custom_connector.png +0 -0
- package/dist/public/assets/mcp_2_connect_disconnect_connector.png +0 -0
- package/dist/public/components/Alert.js +18 -0
- package/dist/public/components/Card.js +19 -0
- package/dist/public/components/CopyButton.js +24 -0
- package/dist/public/components/FormField.js +16 -0
- package/dist/public/components/Modal.js +121 -0
- package/dist/public/components/Spinner.js +11 -0
- package/dist/public/components/Toast.js +26 -0
- package/dist/public/components/index.js +8 -0
- package/dist/public/components/useViewCSS.js +12 -0
- package/dist/public/cortex-bundled/aimeat-canvas.js +1408 -0
- package/dist/public/cortex-bundled/aimeat-canvas.yaml +241 -0
- package/dist/public/cortex-bundled/aimeat-charts.js +455 -0
- package/dist/public/cortex-bundled/aimeat-charts.yaml +178 -0
- package/dist/public/cortex-bundled/aimeat-ui-dialogs.js +667 -0
- package/dist/public/cortex-bundled/aimeat-ui-dialogs.yaml +66 -0
- package/dist/public/cortex-bundled/aimeat-ui-forms.js +1106 -0
- package/dist/public/cortex-bundled/aimeat-ui-forms.yaml +90 -0
- package/dist/public/cortex-bundled/aimeat-ui-layout.js +436 -0
- package/dist/public/cortex-bundled/aimeat-ui-layout.yaml +87 -0
- package/dist/public/cortex-bundled/aimeat-ui-nav.js +722 -0
- package/dist/public/cortex-bundled/aimeat-ui-nav.yaml +73 -0
- package/dist/public/cortex-bundled/aimeat-ui-viewers.js +602 -0
- package/dist/public/cortex-bundled/aimeat-ui-viewers.yaml +73 -0
- package/dist/public/css/components/tags.css +67 -0
- package/dist/public/css/theme.css +716 -0
- package/dist/public/css/views/admin.css +576 -0
- package/dist/public/css/views/aimeat-os.css +153 -0
- package/dist/public/css/views/calibrator.css +480 -0
- package/dist/public/css/views/foundry.css +1334 -0
- package/dist/public/css/views/help.css +136 -0
- package/dist/public/css/views/hobbies.css +73 -0
- package/dist/public/css/views/marketplace.css +134 -0
- package/dist/public/css/views/openclaw.css +33 -0
- package/dist/public/css/views/portal-classic.css +484 -0
- package/dist/public/css/views/portal-dev.css +139 -0
- package/dist/public/css/views/portal.css +365 -0
- package/dist/public/css/views/portfolio.css +322 -0
- package/dist/public/css/views/profile.css +1799 -0
- package/dist/public/echat.html +442 -0
- package/dist/public/img/genesis-001-badge.png +0 -0
- package/dist/public/img/platforms/chatgpt.png +0 -0
- package/dist/public/img/platforms/claude.png +0 -0
- package/dist/public/img/platforms/copilot.png +0 -0
- package/dist/public/img/platforms/deepseek.png +0 -0
- package/dist/public/img/platforms/gemini.png +0 -0
- package/dist/public/img/platforms/grok.png +0 -0
- package/dist/public/img/platforms/lmstudio.png +0 -0
- package/dist/public/img/platforms/m365copilot.png +0 -0
- package/dist/public/img/platforms/openclaw.png +0 -0
- package/dist/public/img/platforms/other.png +0 -0
- package/dist/public/js/api.js +123 -0
- package/dist/public/js/components/auth-image.js +59 -0
- package/dist/public/js/components/tag-cloud.js +31 -0
- package/dist/public/js/components/tag-editor.js +46 -0
- package/dist/public/js/hooks.js +63 -0
- package/dist/public/js/i18n.js +76 -0
- package/dist/public/js/services/admin.js +239 -0
- package/dist/public/js/services/agents.js +57 -0
- package/dist/public/js/services/apps.js +35 -0
- package/dist/public/js/services/auth.js +40 -0
- package/dist/public/js/services/boards.js +79 -0
- package/dist/public/js/services/calibrator.js +97 -0
- package/dist/public/js/services/catalogue.js +39 -0
- package/dist/public/js/services/consent.js +57 -0
- package/dist/public/js/services/cortex.js +95 -0
- package/dist/public/js/services/extensions.js +61 -0
- package/dist/public/js/services/federation.js +11 -0
- package/dist/public/js/services/foundry-context-bundle.js +128 -0
- package/dist/public/js/services/foundry-contract.js +117 -0
- package/dist/public/js/services/foundry-packaging.js +762 -0
- package/dist/public/js/services/foundry-probe-reconcile.js +52 -0
- package/dist/public/js/services/foundry-prompts-base.js +1886 -0
- package/dist/public/js/services/foundry-prompts-build.js +2187 -0
- package/dist/public/js/services/foundry-prompts-cortex-app.js +154 -0
- package/dist/public/js/services/foundry-prompts-cortex-data.js +241 -0
- package/dist/public/js/services/foundry-prompts-cortex-feature.js +281 -0
- package/dist/public/js/services/foundry-prompts-fix.js +408 -0
- package/dist/public/js/services/foundry-prompts-test.js +520 -0
- package/dist/public/js/services/foundry-prompts.js +31 -0
- package/dist/public/js/services/foundry-smoke.js +65 -0
- package/dist/public/js/services/foundry-testing.js +64 -0
- package/dist/public/js/services/foundry-validate.js +1082 -0
- package/dist/public/js/services/foundry.js +1194 -0
- package/dist/public/js/services/generator-context-bundle.js +134 -0
- package/dist/public/js/services/generator-contract.js +117 -0
- package/dist/public/js/services/generator-packaging.js +762 -0
- package/dist/public/js/services/generator-probe-reconcile.js +52 -0
- package/dist/public/js/services/generator-prompts-base.js +1890 -0
- package/dist/public/js/services/generator-prompts-build.js +1126 -0
- package/dist/public/js/services/generator-prompts-cortex-app.js +155 -0
- package/dist/public/js/services/generator-prompts-cortex-data.js +199 -0
- package/dist/public/js/services/generator-prompts-cortex-feature.js +276 -0
- package/dist/public/js/services/generator-prompts-fix.js +316 -0
- package/dist/public/js/services/generator-prompts-test.js +402 -0
- package/dist/public/js/services/generator-prompts.js +31 -0
- package/dist/public/js/services/generator-smoke.js +65 -0
- package/dist/public/js/services/generator-spec-tests.js +280 -0
- package/dist/public/js/services/generator-spec-validate.js +192 -0
- package/dist/public/js/services/generator-specs.js +369 -0
- package/dist/public/js/services/generator-testing.js +64 -0
- package/dist/public/js/services/generator-validate.js +916 -0
- package/dist/public/js/services/generator.js +952 -0
- package/dist/public/js/services/knowledge.js +120 -0
- package/dist/public/js/services/memory.js +112 -0
- package/dist/public/js/services/nodes.js +55 -0
- package/dist/public/js/services/organisms.js +74 -0
- package/dist/public/js/services/packages.js +97 -0
- package/dist/public/js/services/security.js +67 -0
- package/dist/public/js/services/stats.js +11 -0
- package/dist/public/js/services/wallet.js +24 -0
- package/dist/public/js/services/work.js +51 -0
- package/dist/public/js/utils.js +184 -0
- package/dist/public/lib/htm.mjs +4 -0
- package/dist/public/lib/live-updates.js +89 -0
- package/dist/public/lib/minidenticons.min.js +1 -0
- package/dist/public/lib/preact-hooks.mjs +3 -0
- package/dist/public/lib/preact.mjs +3 -0
- package/dist/public/lib/realtime.js +549 -0
- package/dist/public/lib/three.min.js +6 -0
- package/dist/public/lib/yaml.mjs +141 -0
- package/dist/public/llms-template.txt +1043 -0
- package/dist/public/oauth-consent.html +388 -0
- package/dist/public/og-image.png +0 -0
- package/dist/public/robots.txt +52 -0
- package/dist/public/spa.html +533 -0
- package/dist/public/sw.js +34 -0
- package/dist/public/tmp-paste.txt +223 -0
- package/dist/public/views/_template.js +85 -0
- package/dist/public/views/admin/actions-tab.js +41 -0
- package/dist/public/views/admin/agents-tab.js +84 -0
- package/dist/public/views/admin/boards-tab.js +154 -0
- package/dist/public/views/admin/chat-instances-tab.js +207 -0
- package/dist/public/views/admin/config-tab.js +151 -0
- package/dist/public/views/admin/consul-tab.js +77 -0
- package/dist/public/views/admin/cors-tab.js +240 -0
- package/dist/public/views/admin/cortex-tab.js +171 -0
- package/dist/public/views/admin/csm-tab.js +261 -0
- package/dist/public/views/admin/directory-tab.js +44 -0
- package/dist/public/views/admin/economy-tab.js +80 -0
- package/dist/public/views/admin/email-tab.js +460 -0
- package/dist/public/views/admin/federation-tab.js +412 -0
- package/dist/public/views/admin/generator-debug-tab.js +238 -0
- package/dist/public/views/admin/genesis-tab.js +70 -0
- package/dist/public/views/admin/ghii-tab.js +101 -0
- package/dist/public/views/admin/hooks-tab.js +69 -0
- package/dist/public/views/admin/knowledge-tab.js +297 -0
- package/dist/public/views/admin/maintenance-tab.js +98 -0
- package/dist/public/views/admin/matching-tab.js +44 -0
- package/dist/public/views/admin/memory-tab.js +222 -0
- package/dist/public/views/admin/msm-tab.js +342 -0
- package/dist/public/views/admin/overview-tab.js +90 -0
- package/dist/public/views/admin/owners-tab.js +55 -0
- package/dist/public/views/admin/packages-tab.js +510 -0
- package/dist/public/views/admin/portal-tab.js +211 -0
- package/dist/public/views/admin/prompts-tab.js +287 -0
- package/dist/public/views/admin/push-tab.js +291 -0
- package/dist/public/views/admin/realtime-tab.js +68 -0
- package/dist/public/views/admin/scheduler-tab.js +156 -0
- package/dist/public/views/admin/services-tab.js +981 -0
- package/dist/public/views/admin/shared.js +148 -0
- package/dist/public/views/admin/stats-tab.js +187 -0
- package/dist/public/views/admin/work-tab.js +45 -0
- package/dist/public/views/admin.js +400 -0
- package/dist/public/views/aimeat-os.js +259 -0
- package/dist/public/views/guides.js +402 -0
- package/dist/public/views/help.js +79 -0
- package/dist/public/views/hobbies.js +697 -0
- package/dist/public/views/marketplace.js +647 -0
- package/dist/public/views/openclaw.js +223 -0
- package/dist/public/views/portal-classic.js +519 -0
- package/dist/public/views/portal-dev.js +816 -0
- package/dist/public/views/portal.js +1354 -0
- package/dist/public/views/portfolio.js +795 -0
- package/dist/public/views/profile/access-tab.js +58 -0
- package/dist/public/views/profile/agents-tab.js +689 -0
- package/dist/public/views/profile/apps-tab.js +197 -0
- package/dist/public/views/profile/boards-tab.js +323 -0
- package/dist/public/views/profile/calibrator-batch.js +926 -0
- package/dist/public/views/profile/calibrator-chart.js +135 -0
- package/dist/public/views/profile/calibrator-llm-editor.js +205 -0
- package/dist/public/views/profile/calibrator-tab.js +535 -0
- package/dist/public/views/profile/chat-sessions-tab.js +184 -0
- package/dist/public/views/profile/data-wallet-tab.js +313 -0
- package/dist/public/views/profile/email-tab.js +157 -0
- package/dist/public/views/profile/extensions-tab.js +711 -0
- package/dist/public/views/profile/federation-tab.js +63 -0
- package/dist/public/views/profile/foundry-dashboard/DebugPanel.js +193 -0
- package/dist/public/views/profile/foundry-dashboard/DiagnosticsPanel.js +69 -0
- package/dist/public/views/profile/foundry-dashboard/EditModePanel.js +141 -0
- package/dist/public/views/profile/foundry-dashboard/PackageDialog.js +96 -0
- package/dist/public/views/profile/foundry-dashboard/RemovePanel.js +53 -0
- package/dist/public/views/profile/foundry-dashboard/use-autopilot-state.js +53 -0
- package/dist/public/views/profile/foundry-dashboard/use-autopilot.js +1016 -0
- package/dist/public/views/profile/foundry-dashboard/use-dashboard-core.js +154 -0
- package/dist/public/views/profile/foundry-dashboard/use-edit-mode.js +294 -0
- package/dist/public/views/profile/foundry-dashboard/use-lifecycle.js +101 -0
- package/dist/public/views/profile/foundry-dashboard/use-packaging.js +97 -0
- package/dist/public/views/profile/foundry-dashboard/use-test-execution.js +275 -0
- package/dist/public/views/profile/foundry-dashboard.js +395 -0
- package/dist/public/views/profile/foundry-detail.js +1178 -0
- package/dist/public/views/profile/foundry-settings.js +377 -0
- package/dist/public/views/profile/foundry-tab.js +546 -0
- package/dist/public/views/profile/generator-dashboard/DebugPanel.js +193 -0
- package/dist/public/views/profile/generator-dashboard/DiagnosticsPanel.js +69 -0
- package/dist/public/views/profile/generator-dashboard/EditModePanel.js +141 -0
- package/dist/public/views/profile/generator-dashboard/PackageDialog.js +96 -0
- package/dist/public/views/profile/generator-dashboard/RemovePanel.js +53 -0
- package/dist/public/views/profile/generator-dashboard/use-autopilot-state.js +53 -0
- package/dist/public/views/profile/generator-dashboard/use-autopilot.js +136 -0
- package/dist/public/views/profile/generator-dashboard/use-dashboard-core.js +143 -0
- package/dist/public/views/profile/generator-dashboard/use-edit-mode.js +296 -0
- package/dist/public/views/profile/generator-dashboard/use-lifecycle.js +101 -0
- package/dist/public/views/profile/generator-dashboard/use-packaging.js +97 -0
- package/dist/public/views/profile/generator-dashboard/use-test-execution.js +275 -0
- package/dist/public/views/profile/generator-dashboard.js +377 -0
- package/dist/public/views/profile/generator-detail.js +1007 -0
- package/dist/public/views/profile/generator-settings.js +417 -0
- package/dist/public/views/profile/generator-tab.js +544 -0
- package/dist/public/views/profile/inline-panels.js +277 -0
- package/dist/public/views/profile/knowledge-tab.js +735 -0
- package/dist/public/views/profile/landing-page.js +539 -0
- package/dist/public/views/profile/mcp-tab.js +140 -0
- package/dist/public/views/profile/memory-tab.js +599 -0
- package/dist/public/views/profile/node-stats-tab.js +132 -0
- package/dist/public/views/profile/nodes-tab.js +195 -0
- package/dist/public/views/profile/notifications-tab.js +274 -0
- package/dist/public/views/profile/organisms-tab.js +402 -0
- package/dist/public/views/profile/packages-tab.js +393 -0
- package/dist/public/views/profile/portfolio-tab.js +31 -0
- package/dist/public/views/profile/security-tab.js +219 -0
- package/dist/public/views/profile/services-tab.js +281 -0
- package/dist/public/views/profile/shared.js +58 -0
- package/dist/public/views/profile/wallet-tab.js +272 -0
- package/dist/public/views/profile/work-tab.js +220 -0
- package/dist/public/views/profile.js +271 -0
- package/dist/public/wizard.html +1320 -0
- package/dist/scripts/db-init.d.ts +19 -0
- package/dist/scripts/db-init.d.ts.map +1 -0
- package/dist/scripts/db-init.js +168 -0
- package/dist/scripts/db-init.js.map +1 -0
- package/dist/scripts/db-reset.d.ts +15 -0
- package/dist/scripts/db-reset.d.ts.map +1 -0
- package/dist/scripts/db-reset.js +162 -0
- package/dist/scripts/db-reset.js.map +1 -0
- package/dist/scripts/indexnow.d.ts +18 -0
- package/dist/scripts/indexnow.d.ts.map +1 -0
- package/dist/scripts/indexnow.js +104 -0
- package/dist/scripts/indexnow.js.map +1 -0
- package/dist/scripts/kill-port.d.ts +3 -0
- package/dist/scripts/kill-port.d.ts.map +1 -0
- package/dist/scripts/kill-port.js +89 -0
- package/dist/scripts/kill-port.js.map +1 -0
- package/dist/scripts/seed-digital-signage.d.ts +13 -0
- package/dist/scripts/seed-digital-signage.d.ts.map +1 -0
- package/dist/scripts/seed-digital-signage.js +85 -0
- package/dist/scripts/seed-digital-signage.js.map +1 -0
- package/dist/src/auth/jwt.d.ts +40 -0
- package/dist/src/auth/jwt.d.ts.map +1 -0
- package/dist/src/auth/jwt.js +139 -0
- package/dist/src/auth/jwt.js.map +1 -0
- package/dist/src/auth/keypair.d.ts +8 -0
- package/dist/src/auth/keypair.d.ts.map +1 -0
- package/dist/src/auth/keypair.js +35 -0
- package/dist/src/auth/keypair.js.map +1 -0
- package/dist/src/auth/middleware.d.ts +50 -0
- package/dist/src/auth/middleware.d.ts.map +1 -0
- package/dist/src/auth/middleware.js +241 -0
- package/dist/src/auth/middleware.js.map +1 -0
- package/dist/src/auth/node-keys.d.ts +7 -0
- package/dist/src/auth/node-keys.d.ts.map +1 -0
- package/dist/src/auth/node-keys.js +116 -0
- package/dist/src/auth/node-keys.js.map +1 -0
- package/dist/src/cli/config-export.d.ts +9 -0
- package/dist/src/cli/config-export.d.ts.map +1 -0
- package/dist/src/cli/config-export.js +90 -0
- package/dist/src/cli/config-export.js.map +1 -0
- package/dist/src/cli/config-import.d.ts +10 -0
- package/dist/src/cli/config-import.d.ts.map +1 -0
- package/dist/src/cli/config-import.js +124 -0
- package/dist/src/cli/config-import.js.map +1 -0
- package/dist/src/cli/federation-join.d.ts +13 -0
- package/dist/src/cli/federation-join.d.ts.map +1 -0
- package/dist/src/cli/federation-join.js +395 -0
- package/dist/src/cli/federation-join.js.map +1 -0
- package/dist/src/cli/init-wizard.d.ts +8 -0
- package/dist/src/cli/init-wizard.d.ts.map +1 -0
- package/dist/src/cli/init-wizard.js +1489 -0
- package/dist/src/cli/init-wizard.js.map +1 -0
- package/dist/src/cli/scaffold.d.ts +33 -0
- package/dist/src/cli/scaffold.d.ts.map +1 -0
- package/dist/src/cli/scaffold.js +142 -0
- package/dist/src/cli/scaffold.js.map +1 -0
- package/dist/src/config.d.ts +272 -0
- package/dist/src/config.d.ts.map +1 -0
- package/dist/src/config.js +362 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/data/example-packages.d.ts +49 -0
- package/dist/src/data/example-packages.d.ts.map +1 -0
- package/dist/src/data/example-packages.js +814 -0
- package/dist/src/data/example-packages.js.map +1 -0
- package/dist/src/i18n.d.ts +19 -0
- package/dist/src/i18n.d.ts.map +1 -0
- package/dist/src/i18n.js +126 -0
- package/dist/src/i18n.js.map +1 -0
- package/dist/src/index.d.ts +3 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +608 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/mcp/boards.d.ts +18 -0
- package/dist/src/mcp/boards.d.ts.map +1 -0
- package/dist/src/mcp/boards.js +279 -0
- package/dist/src/mcp/boards.js.map +1 -0
- package/dist/src/mcp/catalogue.d.ts +17 -0
- package/dist/src/mcp/catalogue.d.ts.map +1 -0
- package/dist/src/mcp/catalogue.js +114 -0
- package/dist/src/mcp/catalogue.js.map +1 -0
- package/dist/src/mcp/chat-instances.d.ts +18 -0
- package/dist/src/mcp/chat-instances.d.ts.map +1 -0
- package/dist/src/mcp/chat-instances.js +161 -0
- package/dist/src/mcp/chat-instances.js.map +1 -0
- package/dist/src/mcp/consent.d.ts +18 -0
- package/dist/src/mcp/consent.d.ts.map +1 -0
- package/dist/src/mcp/consent.js +158 -0
- package/dist/src/mcp/consent.js.map +1 -0
- package/dist/src/mcp/core.d.ts +18 -0
- package/dist/src/mcp/core.d.ts.map +1 -0
- package/dist/src/mcp/core.js +489 -0
- package/dist/src/mcp/core.js.map +1 -0
- package/dist/src/mcp/extensions.d.ts +18 -0
- package/dist/src/mcp/extensions.d.ts.map +1 -0
- package/dist/src/mcp/extensions.js +299 -0
- package/dist/src/mcp/extensions.js.map +1 -0
- package/dist/src/mcp/flags.d.ts +18 -0
- package/dist/src/mcp/flags.d.ts.map +1 -0
- package/dist/src/mcp/flags.js +59 -0
- package/dist/src/mcp/flags.js.map +1 -0
- package/dist/src/mcp/index.d.ts +31 -0
- package/dist/src/mcp/index.d.ts.map +1 -0
- package/dist/src/mcp/index.js +694 -0
- package/dist/src/mcp/index.js.map +1 -0
- package/dist/src/mcp/knowledge.d.ts +18 -0
- package/dist/src/mcp/knowledge.d.ts.map +1 -0
- package/dist/src/mcp/knowledge.js +214 -0
- package/dist/src/mcp/knowledge.js.map +1 -0
- package/dist/src/mcp/memory-extended.d.ts +18 -0
- package/dist/src/mcp/memory-extended.d.ts.map +1 -0
- package/dist/src/mcp/memory-extended.js +73 -0
- package/dist/src/mcp/memory-extended.js.map +1 -0
- package/dist/src/mcp/organisms.d.ts +18 -0
- package/dist/src/mcp/organisms.d.ts.map +1 -0
- package/dist/src/mcp/organisms.js +291 -0
- package/dist/src/mcp/organisms.js.map +1 -0
- package/dist/src/mcp/prompts.d.ts +17 -0
- package/dist/src/mcp/prompts.d.ts.map +1 -0
- package/dist/src/mcp/prompts.js +65 -0
- package/dist/src/mcp/prompts.js.map +1 -0
- package/dist/src/mcp/wallet-extended.d.ts +17 -0
- package/dist/src/mcp/wallet-extended.d.ts.map +1 -0
- package/dist/src/mcp/wallet-extended.js +42 -0
- package/dist/src/mcp/wallet-extended.js.map +1 -0
- package/dist/src/middleware/__tests__/cookie-consent.test.d.ts +2 -0
- package/dist/src/middleware/__tests__/cookie-consent.test.d.ts.map +1 -0
- package/dist/src/middleware/__tests__/cookie-consent.test.js +432 -0
- package/dist/src/middleware/__tests__/cookie-consent.test.js.map +1 -0
- package/dist/src/middleware/cookie-consent.d.ts +19 -0
- package/dist/src/middleware/cookie-consent.d.ts.map +1 -0
- package/dist/src/middleware/cookie-consent.js +127 -0
- package/dist/src/middleware/cookie-consent.js.map +1 -0
- package/dist/src/middleware/cors.d.ts +12 -0
- package/dist/src/middleware/cors.d.ts.map +1 -0
- package/dist/src/middleware/cors.js +112 -0
- package/dist/src/middleware/cors.js.map +1 -0
- package/dist/src/middleware/envelope.d.ts +33 -0
- package/dist/src/middleware/envelope.d.ts.map +1 -0
- package/dist/src/middleware/envelope.js +32 -0
- package/dist/src/middleware/envelope.js.map +1 -0
- package/dist/src/middleware/idempotency.d.ts +3 -0
- package/dist/src/middleware/idempotency.d.ts.map +1 -0
- package/dist/src/middleware/idempotency.js +64 -0
- package/dist/src/middleware/idempotency.js.map +1 -0
- package/dist/src/middleware/metrics.d.ts +8 -0
- package/dist/src/middleware/metrics.d.ts.map +1 -0
- package/dist/src/middleware/metrics.js +25 -0
- package/dist/src/middleware/metrics.js.map +1 -0
- package/dist/src/middleware/rate-limit.d.ts +4 -0
- package/dist/src/middleware/rate-limit.d.ts.map +1 -0
- package/dist/src/middleware/rate-limit.js +73 -0
- package/dist/src/middleware/rate-limit.js.map +1 -0
- package/dist/src/middleware/request-id.d.ts +18 -0
- package/dist/src/middleware/request-id.d.ts.map +1 -0
- package/dist/src/middleware/request-id.js +23 -0
- package/dist/src/middleware/request-id.js.map +1 -0
- package/dist/src/middleware/stats.d.ts +4 -0
- package/dist/src/middleware/stats.d.ts.map +1 -0
- package/dist/src/middleware/stats.js +11 -0
- package/dist/src/middleware/stats.js.map +1 -0
- package/dist/src/middleware/workspace-access.d.ts +16 -0
- package/dist/src/middleware/workspace-access.d.ts.map +1 -0
- package/dist/src/middleware/workspace-access.js +115 -0
- package/dist/src/middleware/workspace-access.js.map +1 -0
- package/dist/src/models/schemas.d.ts +311 -0
- package/dist/src/models/schemas.d.ts.map +1 -0
- package/dist/src/models/schemas.js +281 -0
- package/dist/src/models/schemas.js.map +1 -0
- package/dist/src/routes/actions.d.ts +5 -0
- package/dist/src/routes/actions.d.ts.map +1 -0
- package/dist/src/routes/actions.js +199 -0
- package/dist/src/routes/actions.js.map +1 -0
- package/dist/src/routes/admin-agents.d.ts +5 -0
- package/dist/src/routes/admin-agents.d.ts.map +1 -0
- package/dist/src/routes/admin-agents.js +60 -0
- package/dist/src/routes/admin-agents.js.map +1 -0
- package/dist/src/routes/admin-config.d.ts +7 -0
- package/dist/src/routes/admin-config.d.ts.map +1 -0
- package/dist/src/routes/admin-config.js +209 -0
- package/dist/src/routes/admin-config.js.map +1 -0
- package/dist/src/routes/admin-economy.d.ts +5 -0
- package/dist/src/routes/admin-economy.d.ts.map +1 -0
- package/dist/src/routes/admin-economy.js +57 -0
- package/dist/src/routes/admin-economy.js.map +1 -0
- package/dist/src/routes/admin-extensions.d.ts +6 -0
- package/dist/src/routes/admin-extensions.d.ts.map +1 -0
- package/dist/src/routes/admin-extensions.js +619 -0
- package/dist/src/routes/admin-extensions.js.map +1 -0
- package/dist/src/routes/admin-features.d.ts +16 -0
- package/dist/src/routes/admin-features.d.ts.map +1 -0
- package/dist/src/routes/admin-features.js +672 -0
- package/dist/src/routes/admin-features.js.map +1 -0
- package/dist/src/routes/admin-maintenance.d.ts +8 -0
- package/dist/src/routes/admin-maintenance.d.ts.map +1 -0
- package/dist/src/routes/admin-maintenance.js +86 -0
- package/dist/src/routes/admin-maintenance.js.map +1 -0
- package/dist/src/routes/admin-memory.d.ts +12 -0
- package/dist/src/routes/admin-memory.d.ts.map +1 -0
- package/dist/src/routes/admin-memory.js +64 -0
- package/dist/src/routes/admin-memory.js.map +1 -0
- package/dist/src/routes/admin-monitoring.d.ts +5 -0
- package/dist/src/routes/admin-monitoring.d.ts.map +1 -0
- package/dist/src/routes/admin-monitoring.js +343 -0
- package/dist/src/routes/admin-monitoring.js.map +1 -0
- package/dist/src/routes/admin-prompts.d.ts +5 -0
- package/dist/src/routes/admin-prompts.d.ts.map +1 -0
- package/dist/src/routes/admin-prompts.js +198 -0
- package/dist/src/routes/admin-prompts.js.map +1 -0
- package/dist/src/routes/admin-scheduler.d.ts +6 -0
- package/dist/src/routes/admin-scheduler.d.ts.map +1 -0
- package/dist/src/routes/admin-scheduler.js +153 -0
- package/dist/src/routes/admin-scheduler.js.map +1 -0
- package/dist/src/routes/admin.d.ts +10 -0
- package/dist/src/routes/admin.d.ts.map +1 -0
- package/dist/src/routes/admin.js +847 -0
- package/dist/src/routes/admin.js.map +1 -0
- package/dist/src/routes/agents.d.ts +5 -0
- package/dist/src/routes/agents.d.ts.map +1 -0
- package/dist/src/routes/agents.js +946 -0
- package/dist/src/routes/agents.js.map +1 -0
- package/dist/src/routes/app-store.d.ts +25 -0
- package/dist/src/routes/app-store.d.ts.map +1 -0
- package/dist/src/routes/app-store.js +269 -0
- package/dist/src/routes/app-store.js.map +1 -0
- package/dist/src/routes/appeals.d.ts +5 -0
- package/dist/src/routes/appeals.d.ts.map +1 -0
- package/dist/src/routes/appeals.js +241 -0
- package/dist/src/routes/appeals.js.map +1 -0
- package/dist/src/routes/apps.d.ts +14 -0
- package/dist/src/routes/apps.d.ts.map +1 -0
- package/dist/src/routes/apps.js +395 -0
- package/dist/src/routes/apps.js.map +1 -0
- package/dist/src/routes/auth.d.ts +13 -0
- package/dist/src/routes/auth.d.ts.map +1 -0
- package/dist/src/routes/auth.js +517 -0
- package/dist/src/routes/auth.js.map +1 -0
- package/dist/src/routes/boards.d.ts +5 -0
- package/dist/src/routes/boards.d.ts.map +1 -0
- package/dist/src/routes/boards.js +614 -0
- package/dist/src/routes/boards.js.map +1 -0
- package/dist/src/routes/bootstrap.d.ts +6 -0
- package/dist/src/routes/bootstrap.d.ts.map +1 -0
- package/dist/src/routes/bootstrap.js +360 -0
- package/dist/src/routes/bootstrap.js.map +1 -0
- package/dist/src/routes/calibrator.d.ts +28 -0
- package/dist/src/routes/calibrator.d.ts.map +1 -0
- package/dist/src/routes/calibrator.js +556 -0
- package/dist/src/routes/calibrator.js.map +1 -0
- package/dist/src/routes/catalogue.d.ts +7 -0
- package/dist/src/routes/catalogue.d.ts.map +1 -0
- package/dist/src/routes/catalogue.js +377 -0
- package/dist/src/routes/catalogue.js.map +1 -0
- package/dist/src/routes/chat-instances.d.ts +5 -0
- package/dist/src/routes/chat-instances.d.ts.map +1 -0
- package/dist/src/routes/chat-instances.js +131 -0
- package/dist/src/routes/chat-instances.js.map +1 -0
- package/dist/src/routes/consent.d.ts +6 -0
- package/dist/src/routes/consent.d.ts.map +1 -0
- package/dist/src/routes/consent.js +188 -0
- package/dist/src/routes/consent.js.map +1 -0
- package/dist/src/routes/cortex.d.ts +5 -0
- package/dist/src/routes/cortex.d.ts.map +1 -0
- package/dist/src/routes/cortex.js +617 -0
- package/dist/src/routes/cortex.js.map +1 -0
- package/dist/src/routes/csm.d.ts +5 -0
- package/dist/src/routes/csm.d.ts.map +1 -0
- package/dist/src/routes/csm.js +258 -0
- package/dist/src/routes/csm.js.map +1 -0
- package/dist/src/routes/disputes.d.ts +5 -0
- package/dist/src/routes/disputes.d.ts.map +1 -0
- package/dist/src/routes/disputes.js +528 -0
- package/dist/src/routes/disputes.js.map +1 -0
- package/dist/src/routes/extensions.d.ts +6 -0
- package/dist/src/routes/extensions.d.ts.map +1 -0
- package/dist/src/routes/extensions.js +1149 -0
- package/dist/src/routes/extensions.js.map +1 -0
- package/dist/src/routes/federation-genesis.d.ts +11 -0
- package/dist/src/routes/federation-genesis.d.ts.map +1 -0
- package/dist/src/routes/federation-genesis.js +638 -0
- package/dist/src/routes/federation-genesis.js.map +1 -0
- package/dist/src/routes/federation-peer.d.ts +10 -0
- package/dist/src/routes/federation-peer.d.ts.map +1 -0
- package/dist/src/routes/federation-peer.js +613 -0
- package/dist/src/routes/federation-peer.js.map +1 -0
- package/dist/src/routes/federation-settlements.d.ts +10 -0
- package/dist/src/routes/federation-settlements.d.ts.map +1 -0
- package/dist/src/routes/federation-settlements.js +231 -0
- package/dist/src/routes/federation-settlements.js.map +1 -0
- package/dist/src/routes/federation-sync.d.ts +15 -0
- package/dist/src/routes/federation-sync.d.ts.map +1 -0
- package/dist/src/routes/federation-sync.js +629 -0
- package/dist/src/routes/federation-sync.js.map +1 -0
- package/dist/src/routes/federation.d.ts +15 -0
- package/dist/src/routes/federation.d.ts.map +1 -0
- package/dist/src/routes/federation.js +23 -0
- package/dist/src/routes/federation.js.map +1 -0
- package/dist/src/routes/flags.d.ts +5 -0
- package/dist/src/routes/flags.d.ts.map +1 -0
- package/dist/src/routes/flags.js +194 -0
- package/dist/src/routes/flags.js.map +1 -0
- package/dist/src/routes/foundry.d.ts +5 -0
- package/dist/src/routes/foundry.d.ts.map +1 -0
- package/dist/src/routes/foundry.js +955 -0
- package/dist/src/routes/foundry.js.map +1 -0
- package/dist/src/routes/generator-autopilot.d.ts +12 -0
- package/dist/src/routes/generator-autopilot.d.ts.map +1 -0
- package/dist/src/routes/generator-autopilot.js +71 -0
- package/dist/src/routes/generator-autopilot.js.map +1 -0
- package/dist/src/routes/generator.d.ts +5 -0
- package/dist/src/routes/generator.d.ts.map +1 -0
- package/dist/src/routes/generator.js +1313 -0
- package/dist/src/routes/generator.js.map +1 -0
- package/dist/src/routes/ghii.d.ts +16 -0
- package/dist/src/routes/ghii.d.ts.map +1 -0
- package/dist/src/routes/ghii.js +1115 -0
- package/dist/src/routes/ghii.js.map +1 -0
- package/dist/src/routes/instances.d.ts +31 -0
- package/dist/src/routes/instances.d.ts.map +1 -0
- package/dist/src/routes/instances.js +722 -0
- package/dist/src/routes/instances.js.map +1 -0
- package/dist/src/routes/knowledge.d.ts +33 -0
- package/dist/src/routes/knowledge.d.ts.map +1 -0
- package/dist/src/routes/knowledge.js +1108 -0
- package/dist/src/routes/knowledge.js.map +1 -0
- package/dist/src/routes/lib-data.d.ts +7 -0
- package/dist/src/routes/lib-data.d.ts.map +1 -0
- package/dist/src/routes/lib-data.js +189 -0
- package/dist/src/routes/lib-data.js.map +1 -0
- package/dist/src/routes/lib-social.d.ts +7 -0
- package/dist/src/routes/lib-social.d.ts.map +1 -0
- package/dist/src/routes/lib-social.js +159 -0
- package/dist/src/routes/lib-social.js.map +1 -0
- package/dist/src/routes/lib-storage.d.ts +7 -0
- package/dist/src/routes/lib-storage.d.ts.map +1 -0
- package/dist/src/routes/lib-storage.js +202 -0
- package/dist/src/routes/lib-storage.js.map +1 -0
- package/dist/src/routes/lib-tunnel.d.ts +8 -0
- package/dist/src/routes/lib-tunnel.d.ts.map +1 -0
- package/dist/src/routes/lib-tunnel.js +442 -0
- package/dist/src/routes/lib-tunnel.js.map +1 -0
- package/dist/src/routes/lib-wallet.d.ts +7 -0
- package/dist/src/routes/lib-wallet.d.ts.map +1 -0
- package/dist/src/routes/lib-wallet.js +129 -0
- package/dist/src/routes/lib-wallet.js.map +1 -0
- package/dist/src/routes/lib-work.d.ts +7 -0
- package/dist/src/routes/lib-work.d.ts.map +1 -0
- package/dist/src/routes/lib-work.js +210 -0
- package/dist/src/routes/lib-work.js.map +1 -0
- package/dist/src/routes/libs.d.ts +10 -0
- package/dist/src/routes/libs.d.ts.map +1 -0
- package/dist/src/routes/libs.js +989 -0
- package/dist/src/routes/libs.js.map +1 -0
- package/dist/src/routes/matches.d.ts +10 -0
- package/dist/src/routes/matches.d.ts.map +1 -0
- package/dist/src/routes/matches.js +218 -0
- package/dist/src/routes/matches.js.map +1 -0
- package/dist/src/routes/memory.d.ts +6 -0
- package/dist/src/routes/memory.d.ts.map +1 -0
- package/dist/src/routes/memory.js +796 -0
- package/dist/src/routes/memory.js.map +1 -0
- package/dist/src/routes/micro-memory.d.ts +5 -0
- package/dist/src/routes/micro-memory.d.ts.map +1 -0
- package/dist/src/routes/micro-memory.js +488 -0
- package/dist/src/routes/micro-memory.js.map +1 -0
- package/dist/src/routes/msm.d.ts +5 -0
- package/dist/src/routes/msm.d.ts.map +1 -0
- package/dist/src/routes/msm.js +242 -0
- package/dist/src/routes/msm.js.map +1 -0
- package/dist/src/routes/openrouter.d.ts +23 -0
- package/dist/src/routes/openrouter.d.ts.map +1 -0
- package/dist/src/routes/openrouter.js +337 -0
- package/dist/src/routes/openrouter.js.map +1 -0
- package/dist/src/routes/organisms.d.ts +5 -0
- package/dist/src/routes/organisms.d.ts.map +1 -0
- package/dist/src/routes/organisms.js +415 -0
- package/dist/src/routes/organisms.js.map +1 -0
- package/dist/src/routes/owners.d.ts +5 -0
- package/dist/src/routes/owners.d.ts.map +1 -0
- package/dist/src/routes/owners.js +637 -0
- package/dist/src/routes/owners.js.map +1 -0
- package/dist/src/routes/packages.d.ts +35 -0
- package/dist/src/routes/packages.d.ts.map +1 -0
- package/dist/src/routes/packages.js +600 -0
- package/dist/src/routes/packages.js.map +1 -0
- package/dist/src/routes/permissions.d.ts +5 -0
- package/dist/src/routes/permissions.d.ts.map +1 -0
- package/dist/src/routes/permissions.js +85 -0
- package/dist/src/routes/permissions.js.map +1 -0
- package/dist/src/routes/personal.d.ts +7 -0
- package/dist/src/routes/personal.d.ts.map +1 -0
- package/dist/src/routes/personal.js +594 -0
- package/dist/src/routes/personal.js.map +1 -0
- package/dist/src/routes/portal-api.d.ts +9 -0
- package/dist/src/routes/portal-api.d.ts.map +1 -0
- package/dist/src/routes/portal-api.js +49 -0
- package/dist/src/routes/portal-api.js.map +1 -0
- package/dist/src/routes/portal-human.d.ts +12 -0
- package/dist/src/routes/portal-human.d.ts.map +1 -0
- package/dist/src/routes/portal-human.js +2652 -0
- package/dist/src/routes/portal-human.js.map +1 -0
- package/dist/src/routes/portal.d.ts +24 -0
- package/dist/src/routes/portal.d.ts.map +1 -0
- package/dist/src/routes/portal.js +372 -0
- package/dist/src/routes/portal.js.map +1 -0
- package/dist/src/routes/portfolio.d.ts +5 -0
- package/dist/src/routes/portfolio.d.ts.map +1 -0
- package/dist/src/routes/portfolio.js +231 -0
- package/dist/src/routes/portfolio.js.map +1 -0
- package/dist/src/routes/profile.d.ts +5 -0
- package/dist/src/routes/profile.d.ts.map +1 -0
- package/dist/src/routes/profile.js +2042 -0
- package/dist/src/routes/profile.js.map +1 -0
- package/dist/src/routes/prompts.d.ts +5 -0
- package/dist/src/routes/prompts.d.ts.map +1 -0
- package/dist/src/routes/prompts.js +318 -0
- package/dist/src/routes/prompts.js.map +1 -0
- package/dist/src/routes/push.d.ts +6 -0
- package/dist/src/routes/push.d.ts.map +1 -0
- package/dist/src/routes/push.js +75 -0
- package/dist/src/routes/push.js.map +1 -0
- package/dist/src/routes/realtime.d.ts +7 -0
- package/dist/src/routes/realtime.d.ts.map +1 -0
- package/dist/src/routes/realtime.js +289 -0
- package/dist/src/routes/realtime.js.map +1 -0
- package/dist/src/routes/schemas.d.ts +5 -0
- package/dist/src/routes/schemas.d.ts.map +1 -0
- package/dist/src/routes/schemas.js +130 -0
- package/dist/src/routes/schemas.js.map +1 -0
- package/dist/src/routes/setup.d.ts +7 -0
- package/dist/src/routes/setup.d.ts.map +1 -0
- package/dist/src/routes/setup.js +243 -0
- package/dist/src/routes/setup.js.map +1 -0
- package/dist/src/routes/site.d.ts +6 -0
- package/dist/src/routes/site.d.ts.map +1 -0
- package/dist/src/routes/site.js +201 -0
- package/dist/src/routes/site.js.map +1 -0
- package/dist/src/routes/spec.d.ts +3 -0
- package/dist/src/routes/spec.d.ts.map +1 -0
- package/dist/src/routes/spec.js +47 -0
- package/dist/src/routes/spec.js.map +1 -0
- package/dist/src/routes/sse.d.ts +5 -0
- package/dist/src/routes/sse.d.ts.map +1 -0
- package/dist/src/routes/sse.js +66 -0
- package/dist/src/routes/sse.js.map +1 -0
- package/dist/src/routes/stats.d.ts +7 -0
- package/dist/src/routes/stats.d.ts.map +1 -0
- package/dist/src/routes/stats.js +98 -0
- package/dist/src/routes/stats.js.map +1 -0
- package/dist/src/routes/storage-files.d.ts +5 -0
- package/dist/src/routes/storage-files.d.ts.map +1 -0
- package/dist/src/routes/storage-files.js +460 -0
- package/dist/src/routes/storage-files.js.map +1 -0
- package/dist/src/routes/templates.d.ts +30 -0
- package/dist/src/routes/templates.d.ts.map +1 -0
- package/dist/src/routes/templates.js +511 -0
- package/dist/src/routes/templates.js.map +1 -0
- package/dist/src/routes/totp.d.ts +5 -0
- package/dist/src/routes/totp.d.ts.map +1 -0
- package/dist/src/routes/totp.js +166 -0
- package/dist/src/routes/totp.js.map +1 -0
- package/dist/src/routes/validate.d.ts +4 -0
- package/dist/src/routes/validate.d.ts.map +1 -0
- package/dist/src/routes/validate.js +71 -0
- package/dist/src/routes/validate.js.map +1 -0
- package/dist/src/routes/verification.d.ts +8 -0
- package/dist/src/routes/verification.d.ts.map +1 -0
- package/dist/src/routes/verification.js +259 -0
- package/dist/src/routes/verification.js.map +1 -0
- package/dist/src/routes/wallet.d.ts +18 -0
- package/dist/src/routes/wallet.d.ts.map +1 -0
- package/dist/src/routes/wallet.js +164 -0
- package/dist/src/routes/wallet.js.map +1 -0
- package/dist/src/routes/wellknown.d.ts +5 -0
- package/dist/src/routes/wellknown.d.ts.map +1 -0
- package/dist/src/routes/wellknown.js +57 -0
- package/dist/src/routes/wellknown.js.map +1 -0
- package/dist/src/routes/work.d.ts +21 -0
- package/dist/src/routes/work.d.ts.map +1 -0
- package/dist/src/routes/work.js +654 -0
- package/dist/src/routes/work.js.map +1 -0
- package/dist/src/schemas/knowledge-package.d.ts +203 -0
- package/dist/src/schemas/knowledge-package.d.ts.map +1 -0
- package/dist/src/schemas/knowledge-package.js +233 -0
- package/dist/src/schemas/knowledge-package.js.map +1 -0
- package/dist/src/server-bootstrap/config-init.d.ts +16 -0
- package/dist/src/server-bootstrap/config-init.d.ts.map +1 -0
- package/dist/src/server-bootstrap/config-init.js +113 -0
- package/dist/src/server-bootstrap/config-init.js.map +1 -0
- package/dist/src/server-bootstrap/middleware-guards.d.ts +15 -0
- package/dist/src/server-bootstrap/middleware-guards.d.ts.map +1 -0
- package/dist/src/server-bootstrap/middleware-guards.js +244 -0
- package/dist/src/server-bootstrap/middleware-guards.js.map +1 -0
- package/dist/src/server-bootstrap/routes-loader.d.ts +35 -0
- package/dist/src/server-bootstrap/routes-loader.d.ts.map +1 -0
- package/dist/src/server-bootstrap/routes-loader.js +290 -0
- package/dist/src/server-bootstrap/routes-loader.js.map +1 -0
- package/dist/src/server-bootstrap/service-init.d.ts +24 -0
- package/dist/src/server-bootstrap/service-init.d.ts.map +1 -0
- package/dist/src/server-bootstrap/service-init.js +195 -0
- package/dist/src/server-bootstrap/service-init.js.map +1 -0
- package/dist/src/server-bootstrap/static-files.d.ts +7 -0
- package/dist/src/server-bootstrap/static-files.d.ts.map +1 -0
- package/dist/src/server-bootstrap/static-files.js +119 -0
- package/dist/src/server-bootstrap/static-files.js.map +1 -0
- package/dist/src/server.d.ts +22 -0
- package/dist/src/server.d.ts.map +1 -0
- package/dist/src/server.js +133 -0
- package/dist/src/server.js.map +1 -0
- package/dist/src/services/cache-cleanup.d.ts +31 -0
- package/dist/src/services/cache-cleanup.d.ts.map +1 -0
- package/dist/src/services/cache-cleanup.js +123 -0
- package/dist/src/services/cache-cleanup.js.map +1 -0
- package/dist/src/services/catalogue-sync.d.ts +45 -0
- package/dist/src/services/catalogue-sync.d.ts.map +1 -0
- package/dist/src/services/catalogue-sync.js +196 -0
- package/dist/src/services/catalogue-sync.js.map +1 -0
- package/dist/src/services/component-registrar.d.ts +46 -0
- package/dist/src/services/component-registrar.d.ts.map +1 -0
- package/dist/src/services/component-registrar.js +389 -0
- package/dist/src/services/component-registrar.js.map +1 -0
- package/dist/src/services/config-loader.d.ts +42 -0
- package/dist/src/services/config-loader.d.ts.map +1 -0
- package/dist/src/services/config-loader.js +187 -0
- package/dist/src/services/config-loader.js.map +1 -0
- package/dist/src/services/config-provenance.d.ts +32 -0
- package/dist/src/services/config-provenance.d.ts.map +1 -0
- package/dist/src/services/config-provenance.js +59 -0
- package/dist/src/services/config-provenance.js.map +1 -0
- package/dist/src/services/config-schema.d.ts +52 -0
- package/dist/src/services/config-schema.d.ts.map +1 -0
- package/dist/src/services/config-schema.js +288 -0
- package/dist/src/services/config-schema.js.map +1 -0
- package/dist/src/services/consent.d.ts +43 -0
- package/dist/src/services/consent.d.ts.map +1 -0
- package/dist/src/services/consent.js +137 -0
- package/dist/src/services/consent.js.map +1 -0
- package/dist/src/services/consul-config.d.ts +30 -0
- package/dist/src/services/consul-config.d.ts.map +1 -0
- package/dist/src/services/consul-config.js +110 -0
- package/dist/src/services/consul-config.js.map +1 -0
- package/dist/src/services/core-jobs.d.ts +8 -0
- package/dist/src/services/core-jobs.d.ts.map +1 -0
- package/dist/src/services/core-jobs.js +161 -0
- package/dist/src/services/core-jobs.js.map +1 -0
- package/dist/src/services/cortex-manifest.d.ts +17 -0
- package/dist/src/services/cortex-manifest.d.ts.map +1 -0
- package/dist/src/services/cortex-manifest.js +343 -0
- package/dist/src/services/cortex-manifest.js.map +1 -0
- package/dist/src/services/cortex-seeder.d.ts +18 -0
- package/dist/src/services/cortex-seeder.d.ts.map +1 -0
- package/dist/src/services/cortex-seeder.js +86 -0
- package/dist/src/services/cortex-seeder.js.map +1 -0
- package/dist/src/services/cross-node-matching.d.ts +23 -0
- package/dist/src/services/cross-node-matching.d.ts.map +1 -0
- package/dist/src/services/cross-node-matching.js +62 -0
- package/dist/src/services/cross-node-matching.js.map +1 -0
- package/dist/src/services/csm-parser.d.ts +57 -0
- package/dist/src/services/csm-parser.d.ts.map +1 -0
- package/dist/src/services/csm-parser.js +161 -0
- package/dist/src/services/csm-parser.js.map +1 -0
- package/dist/src/services/csm-seed.d.ts +15 -0
- package/dist/src/services/csm-seed.d.ts.map +1 -0
- package/dist/src/services/csm-seed.js +89 -0
- package/dist/src/services/csm-seed.js.map +1 -0
- package/dist/src/services/directory.d.ts +86 -0
- package/dist/src/services/directory.d.ts.map +1 -0
- package/dist/src/services/directory.js +307 -0
- package/dist/src/services/directory.js.map +1 -0
- package/dist/src/services/email-templates.d.ts +29 -0
- package/dist/src/services/email-templates.d.ts.map +1 -0
- package/dist/src/services/email-templates.js +204 -0
- package/dist/src/services/email-templates.js.map +1 -0
- package/dist/src/services/email.d.ts +18 -0
- package/dist/src/services/email.d.ts.map +1 -0
- package/dist/src/services/email.js +102 -0
- package/dist/src/services/email.js.map +1 -0
- package/dist/src/services/encryption.d.ts +31 -0
- package/dist/src/services/encryption.d.ts.map +1 -0
- package/dist/src/services/encryption.js +51 -0
- package/dist/src/services/encryption.js.map +1 -0
- package/dist/src/services/eudiw.d.ts +15 -0
- package/dist/src/services/eudiw.d.ts.map +1 -0
- package/dist/src/services/eudiw.js +89 -0
- package/dist/src/services/eudiw.js.map +1 -0
- package/dist/src/services/event-bus.d.ts +8 -0
- package/dist/src/services/event-bus.d.ts.map +1 -0
- package/dist/src/services/event-bus.js +13 -0
- package/dist/src/services/event-bus.js.map +1 -0
- package/dist/src/services/extension-runtime.d.ts +72 -0
- package/dist/src/services/extension-runtime.d.ts.map +1 -0
- package/dist/src/services/extension-runtime.js +282 -0
- package/dist/src/services/extension-runtime.js.map +1 -0
- package/dist/src/services/federation-helpers.d.ts +37 -0
- package/dist/src/services/federation-helpers.d.ts.map +1 -0
- package/dist/src/services/federation-helpers.js +100 -0
- package/dist/src/services/federation-helpers.js.map +1 -0
- package/dist/src/services/federation.d.ts +32 -0
- package/dist/src/services/federation.d.ts.map +1 -0
- package/dist/src/services/federation.js +201 -0
- package/dist/src/services/federation.js.map +1 -0
- package/dist/src/services/foundry-debug.d.ts +77 -0
- package/dist/src/services/foundry-debug.d.ts.map +1 -0
- package/dist/src/services/foundry-debug.js +214 -0
- package/dist/src/services/foundry-debug.js.map +1 -0
- package/dist/src/services/foundry-registration.d.ts +42 -0
- package/dist/src/services/foundry-registration.d.ts.map +1 -0
- package/dist/src/services/foundry-registration.js +330 -0
- package/dist/src/services/foundry-registration.js.map +1 -0
- package/dist/src/services/foundry-testing.d.ts +90 -0
- package/dist/src/services/foundry-testing.d.ts.map +1 -0
- package/dist/src/services/foundry-testing.js +253 -0
- package/dist/src/services/foundry-testing.js.map +1 -0
- package/dist/src/services/foundry-validate.d.ts +115 -0
- package/dist/src/services/foundry-validate.d.ts.map +1 -0
- package/dist/src/services/foundry-validate.js +781 -0
- package/dist/src/services/foundry-validate.js.map +1 -0
- package/dist/src/services/generator-autopilot.d.ts +37 -0
- package/dist/src/services/generator-autopilot.d.ts.map +1 -0
- package/dist/src/services/generator-autopilot.js +1076 -0
- package/dist/src/services/generator-autopilot.js.map +1 -0
- package/dist/src/services/generator-debug.d.ts +79 -0
- package/dist/src/services/generator-debug.d.ts.map +1 -0
- package/dist/src/services/generator-debug.js +218 -0
- package/dist/src/services/generator-debug.js.map +1 -0
- package/dist/src/services/generator-prompt-seeds.d.ts +13 -0
- package/dist/src/services/generator-prompt-seeds.d.ts.map +1 -0
- package/dist/src/services/generator-prompt-seeds.js +2694 -0
- package/dist/src/services/generator-prompt-seeds.js.map +1 -0
- package/dist/src/services/generator-prompts/bundle.d.ts +17 -0
- package/dist/src/services/generator-prompts/bundle.d.ts.map +1 -0
- package/dist/src/services/generator-prompts/bundle.js +95 -0
- package/dist/src/services/generator-prompts/bundle.js.map +1 -0
- package/dist/src/services/generator-prompts/index.d.ts +48 -0
- package/dist/src/services/generator-prompts/index.d.ts.map +1 -0
- package/dist/src/services/generator-prompts/index.js +97 -0
- package/dist/src/services/generator-prompts/index.js.map +1 -0
- package/dist/src/services/generator-prompts/resolvers.d.ts +21 -0
- package/dist/src/services/generator-prompts/resolvers.d.ts.map +1 -0
- package/dist/src/services/generator-prompts/resolvers.js +1553 -0
- package/dist/src/services/generator-prompts/resolvers.js.map +1 -0
- package/dist/src/services/generator-prompts/spec-validate.d.ts +29 -0
- package/dist/src/services/generator-prompts/spec-validate.d.ts.map +1 -0
- package/dist/src/services/generator-prompts/spec-validate.js +190 -0
- package/dist/src/services/generator-prompts/spec-validate.js.map +1 -0
- package/dist/src/services/generator-prompts/strip.d.ts +13 -0
- package/dist/src/services/generator-prompts/strip.d.ts.map +1 -0
- package/dist/src/services/generator-prompts/strip.js +60 -0
- package/dist/src/services/generator-prompts/strip.js.map +1 -0
- package/dist/src/services/generator-prompts/types.d.ts +229 -0
- package/dist/src/services/generator-prompts/types.d.ts.map +1 -0
- package/dist/src/services/generator-prompts/types.js +8 -0
- package/dist/src/services/generator-prompts/types.js.map +1 -0
- package/dist/src/services/generator-prompts/validate.d.ts +48 -0
- package/dist/src/services/generator-prompts/validate.d.ts.map +1 -0
- package/dist/src/services/generator-prompts/validate.js +927 -0
- package/dist/src/services/generator-prompts/validate.js.map +1 -0
- package/dist/src/services/generator-registration.d.ts +43 -0
- package/dist/src/services/generator-registration.d.ts.map +1 -0
- package/dist/src/services/generator-registration.js +343 -0
- package/dist/src/services/generator-registration.js.map +1 -0
- package/dist/src/services/generator-testing.d.ts +93 -0
- package/dist/src/services/generator-testing.d.ts.map +1 -0
- package/dist/src/services/generator-testing.js +300 -0
- package/dist/src/services/generator-testing.js.map +1 -0
- package/dist/src/services/generator-validate.d.ts +117 -0
- package/dist/src/services/generator-validate.d.ts.map +1 -0
- package/dist/src/services/generator-validate.js +790 -0
- package/dist/src/services/generator-validate.js.map +1 -0
- package/dist/src/services/genesis-peering.d.ts +12 -0
- package/dist/src/services/genesis-peering.d.ts.map +1 -0
- package/dist/src/services/genesis-peering.js +91 -0
- package/dist/src/services/genesis-peering.js.map +1 -0
- package/dist/src/services/genesis-sync.d.ts +29 -0
- package/dist/src/services/genesis-sync.d.ts.map +1 -0
- package/dist/src/services/genesis-sync.js +370 -0
- package/dist/src/services/genesis-sync.js.map +1 -0
- package/dist/src/services/hooks.d.ts +21 -0
- package/dist/src/services/hooks.d.ts.map +1 -0
- package/dist/src/services/hooks.js +85 -0
- package/dist/src/services/hooks.js.map +1 -0
- package/dist/src/services/job-seeding.d.ts +7 -0
- package/dist/src/services/job-seeding.d.ts.map +1 -0
- package/dist/src/services/job-seeding.js +39 -0
- package/dist/src/services/job-seeding.js.map +1 -0
- package/dist/src/services/knowledge.d.ts +13 -0
- package/dist/src/services/knowledge.d.ts.map +1 -0
- package/dist/src/services/knowledge.js +15 -0
- package/dist/src/services/knowledge.js.map +1 -0
- package/dist/src/services/mailbox-notification.d.ts +37 -0
- package/dist/src/services/mailbox-notification.d.ts.map +1 -0
- package/dist/src/services/mailbox-notification.js +249 -0
- package/dist/src/services/mailbox-notification.js.map +1 -0
- package/dist/src/services/mailbox.d.ts +16 -0
- package/dist/src/services/mailbox.d.ts.map +1 -0
- package/dist/src/services/mailbox.js +77 -0
- package/dist/src/services/mailbox.js.map +1 -0
- package/dist/src/services/match-notification.d.ts +12 -0
- package/dist/src/services/match-notification.d.ts.map +1 -0
- package/dist/src/services/match-notification.js +121 -0
- package/dist/src/services/match-notification.js.map +1 -0
- package/dist/src/services/matching.d.ts +54 -0
- package/dist/src/services/matching.d.ts.map +1 -0
- package/dist/src/services/matching.js +293 -0
- package/dist/src/services/matching.js.map +1 -0
- package/dist/src/services/memory-replication.d.ts +43 -0
- package/dist/src/services/memory-replication.d.ts.map +1 -0
- package/dist/src/services/memory-replication.js +213 -0
- package/dist/src/services/memory-replication.js.map +1 -0
- package/dist/src/services/morsel.d.ts +49 -0
- package/dist/src/services/morsel.d.ts.map +1 -0
- package/dist/src/services/morsel.js +174 -0
- package/dist/src/services/morsel.js.map +1 -0
- package/dist/src/services/msm-parser.d.ts +55 -0
- package/dist/src/services/msm-parser.d.ts.map +1 -0
- package/dist/src/services/msm-parser.js +186 -0
- package/dist/src/services/msm-parser.js.map +1 -0
- package/dist/src/services/mydata-receipt.d.ts +29 -0
- package/dist/src/services/mydata-receipt.d.ts.map +1 -0
- package/dist/src/services/mydata-receipt.js +31 -0
- package/dist/src/services/mydata-receipt.js.map +1 -0
- package/dist/src/services/notification-templates.d.ts +40 -0
- package/dist/src/services/notification-templates.d.ts.map +1 -0
- package/dist/src/services/notification-templates.js +109 -0
- package/dist/src/services/notification-templates.js.map +1 -0
- package/dist/src/services/openrouter.d.ts +30 -0
- package/dist/src/services/openrouter.d.ts.map +1 -0
- package/dist/src/services/openrouter.js +95 -0
- package/dist/src/services/openrouter.js.map +1 -0
- package/dist/src/services/organism-reputation.d.ts +7 -0
- package/dist/src/services/organism-reputation.d.ts.map +1 -0
- package/dist/src/services/organism-reputation.js +107 -0
- package/dist/src/services/organism-reputation.js.map +1 -0
- package/dist/src/services/package-hash.d.ts +19 -0
- package/dist/src/services/package-hash.d.ts.map +1 -0
- package/dist/src/services/package-hash.js +22 -0
- package/dist/src/services/package-hash.js.map +1 -0
- package/dist/src/services/package-zip.d.ts +60 -0
- package/dist/src/services/package-zip.d.ts.map +1 -0
- package/dist/src/services/package-zip.js +267 -0
- package/dist/src/services/package-zip.js.map +1 -0
- package/dist/src/services/password.d.ts +9 -0
- package/dist/src/services/password.d.ts.map +1 -0
- package/dist/src/services/password.js +32 -0
- package/dist/src/services/password.js.map +1 -0
- package/dist/src/services/personal-routing.d.ts +21 -0
- package/dist/src/services/personal-routing.d.ts.map +1 -0
- package/dist/src/services/personal-routing.js +77 -0
- package/dist/src/services/personal-routing.js.map +1 -0
- package/dist/src/services/personal-tunnel.d.ts +39 -0
- package/dist/src/services/personal-tunnel.d.ts.map +1 -0
- package/dist/src/services/personal-tunnel.js +371 -0
- package/dist/src/services/personal-tunnel.js.map +1 -0
- package/dist/src/services/profile-schemas.d.ts +12 -0
- package/dist/src/services/profile-schemas.d.ts.map +1 -0
- package/dist/src/services/profile-schemas.js +100 -0
- package/dist/src/services/profile-schemas.js.map +1 -0
- package/dist/src/services/prometheus.d.ts +32 -0
- package/dist/src/services/prometheus.d.ts.map +1 -0
- package/dist/src/services/prometheus.js +162 -0
- package/dist/src/services/prometheus.js.map +1 -0
- package/dist/src/services/prompt-defaults.d.ts +33 -0
- package/dist/src/services/prompt-defaults.d.ts.map +1 -0
- package/dist/src/services/prompt-defaults.js +1675 -0
- package/dist/src/services/prompt-defaults.js.map +1 -0
- package/dist/src/services/prompt-seeder.d.ts +8 -0
- package/dist/src/services/prompt-seeder.d.ts.map +1 -0
- package/dist/src/services/prompt-seeder.js +65 -0
- package/dist/src/services/prompt-seeder.js.map +1 -0
- package/dist/src/services/prompt-variables.d.ts +15 -0
- package/dist/src/services/prompt-variables.d.ts.map +1 -0
- package/dist/src/services/prompt-variables.js +34 -0
- package/dist/src/services/prompt-variables.js.map +1 -0
- package/dist/src/services/push.d.ts +26 -0
- package/dist/src/services/push.d.ts.map +1 -0
- package/dist/src/services/push.js +70 -0
- package/dist/src/services/push.js.map +1 -0
- package/dist/src/services/quota.d.ts +45 -0
- package/dist/src/services/quota.d.ts.map +1 -0
- package/dist/src/services/quota.js +129 -0
- package/dist/src/services/quota.js.map +1 -0
- package/dist/src/services/realtime-manager.d.ts +144 -0
- package/dist/src/services/realtime-manager.d.ts.map +1 -0
- package/dist/src/services/realtime-manager.js +640 -0
- package/dist/src/services/realtime-manager.js.map +1 -0
- package/dist/src/services/scheduler.d.ts +55 -0
- package/dist/src/services/scheduler.d.ts.map +1 -0
- package/dist/src/services/scheduler.js +420 -0
- package/dist/src/services/scheduler.js.map +1 -0
- package/dist/src/services/schema-validator.d.ts +20 -0
- package/dist/src/services/schema-validator.d.ts.map +1 -0
- package/dist/src/services/schema-validator.js +68 -0
- package/dist/src/services/schema-validator.js.map +1 -0
- package/dist/src/services/site-sync.d.ts +15 -0
- package/dist/src/services/site-sync.d.ts.map +1 -0
- package/dist/src/services/site-sync.js +99 -0
- package/dist/src/services/site-sync.js.map +1 -0
- package/dist/src/services/site.d.ts +65 -0
- package/dist/src/services/site.d.ts.map +1 -0
- package/dist/src/services/site.js +378 -0
- package/dist/src/services/site.js.map +1 -0
- package/dist/src/services/stats.d.ts +75 -0
- package/dist/src/services/stats.d.ts.map +1 -0
- package/dist/src/services/stats.js +124 -0
- package/dist/src/services/stats.js.map +1 -0
- package/dist/src/services/sync-health.d.ts +53 -0
- package/dist/src/services/sync-health.d.ts.map +1 -0
- package/dist/src/services/sync-health.js +122 -0
- package/dist/src/services/sync-health.js.map +1 -0
- package/dist/src/services/sync-scheduler.d.ts +25 -0
- package/dist/src/services/sync-scheduler.d.ts.map +1 -0
- package/dist/src/services/sync-scheduler.js +211 -0
- package/dist/src/services/sync-scheduler.js.map +1 -0
- package/dist/src/services/totp.d.ts +31 -0
- package/dist/src/services/totp.d.ts.map +1 -0
- package/dist/src/services/totp.js +96 -0
- package/dist/src/services/totp.js.map +1 -0
- package/dist/src/services/trust-broadcast.d.ts +37 -0
- package/dist/src/services/trust-broadcast.d.ts.map +1 -0
- package/dist/src/services/trust-broadcast.js +107 -0
- package/dist/src/services/trust-broadcast.js.map +1 -0
- package/dist/src/services/trust.d.ts +16 -0
- package/dist/src/services/trust.d.ts.map +1 -0
- package/dist/src/services/trust.js +109 -0
- package/dist/src/services/trust.js.map +1 -0
- package/dist/src/services/vc-issuer.d.ts +14 -0
- package/dist/src/services/vc-issuer.d.ts.map +1 -0
- package/dist/src/services/vc-issuer.js +24 -0
- package/dist/src/services/vc-issuer.js.map +1 -0
- package/dist/src/storage/interface.d.ts +1161 -0
- package/dist/src/storage/interface.d.ts.map +1 -0
- package/dist/src/storage/interface.js +2 -0
- package/dist/src/storage/interface.js.map +1 -0
- package/dist/src/storage/pattern-utils.d.ts +20 -0
- package/dist/src/storage/pattern-utils.d.ts.map +1 -0
- package/dist/src/storage/pattern-utils.js +57 -0
- package/dist/src/storage/pattern-utils.js.map +1 -0
- package/dist/src/storage/providers/mongodb/index.d.ts +608 -0
- package/dist/src/storage/providers/mongodb/index.d.ts.map +1 -0
- package/dist/src/storage/providers/mongodb/index.js +4931 -0
- package/dist/src/storage/providers/mongodb/index.js.map +1 -0
- package/dist/src/storage/providers/sqlite/index.d.ts +602 -0
- package/dist/src/storage/providers/sqlite/index.d.ts.map +1 -0
- package/dist/src/storage/providers/sqlite/index.js +4156 -0
- package/dist/src/storage/providers/sqlite/index.js.map +1 -0
- package/dist/src/storage/providers/sqlite/repos/action.d.ts +13 -0
- package/dist/src/storage/providers/sqlite/repos/action.d.ts.map +1 -0
- package/dist/src/storage/providers/sqlite/repos/action.js +79 -0
- package/dist/src/storage/providers/sqlite/repos/action.js.map +1 -0
- package/dist/src/storage/providers/sqlite/repos/agent.d.ts +13 -0
- package/dist/src/storage/providers/sqlite/repos/agent.d.ts.map +1 -0
- package/dist/src/storage/providers/sqlite/repos/agent.js +103 -0
- package/dist/src/storage/providers/sqlite/repos/agent.js.map +1 -0
- package/dist/src/storage/providers/sqlite/repos/auth.d.ts +56 -0
- package/dist/src/storage/providers/sqlite/repos/auth.d.ts.map +1 -0
- package/dist/src/storage/providers/sqlite/repos/auth.js +282 -0
- package/dist/src/storage/providers/sqlite/repos/auth.js.map +1 -0
- package/dist/src/storage/providers/sqlite/repos/board.d.ts +24 -0
- package/dist/src/storage/providers/sqlite/repos/board.d.ts.map +1 -0
- package/dist/src/storage/providers/sqlite/repos/board.js +154 -0
- package/dist/src/storage/providers/sqlite/repos/board.js.map +1 -0
- package/dist/src/storage/providers/sqlite/repos/community.d.ts +33 -0
- package/dist/src/storage/providers/sqlite/repos/community.d.ts.map +1 -0
- package/dist/src/storage/providers/sqlite/repos/community.js +202 -0
- package/dist/src/storage/providers/sqlite/repos/community.js.map +1 -0
- package/dist/src/storage/providers/sqlite/repos/consent.d.ts +23 -0
- package/dist/src/storage/providers/sqlite/repos/consent.d.ts.map +1 -0
- package/dist/src/storage/providers/sqlite/repos/consent.js +187 -0
- package/dist/src/storage/providers/sqlite/repos/consent.js.map +1 -0
- package/dist/src/storage/providers/sqlite/repos/dispute.d.ts +20 -0
- package/dist/src/storage/providers/sqlite/repos/dispute.d.ts.map +1 -0
- package/dist/src/storage/providers/sqlite/repos/dispute.js +118 -0
- package/dist/src/storage/providers/sqlite/repos/dispute.js.map +1 -0
- package/dist/src/storage/providers/sqlite/repos/flag.d.ts +26 -0
- package/dist/src/storage/providers/sqlite/repos/flag.d.ts.map +1 -0
- package/dist/src/storage/providers/sqlite/repos/flag.js +147 -0
- package/dist/src/storage/providers/sqlite/repos/flag.js.map +1 -0
- package/dist/src/storage/providers/sqlite/repos/identity.d.ts +36 -0
- package/dist/src/storage/providers/sqlite/repos/identity.d.ts.map +1 -0
- package/dist/src/storage/providers/sqlite/repos/identity.js +297 -0
- package/dist/src/storage/providers/sqlite/repos/identity.js.map +1 -0
- package/dist/src/storage/providers/sqlite/repos/mailbox.d.ts +18 -0
- package/dist/src/storage/providers/sqlite/repos/mailbox.d.ts.map +1 -0
- package/dist/src/storage/providers/sqlite/repos/mailbox.js +91 -0
- package/dist/src/storage/providers/sqlite/repos/mailbox.js.map +1 -0
- package/dist/src/storage/providers/sqlite/repos/memory.d.ts +28 -0
- package/dist/src/storage/providers/sqlite/repos/memory.d.ts.map +1 -0
- package/dist/src/storage/providers/sqlite/repos/memory.js +152 -0
- package/dist/src/storage/providers/sqlite/repos/memory.js.map +1 -0
- package/dist/src/storage/providers/sqlite/repos/owner.d.ts +14 -0
- package/dist/src/storage/providers/sqlite/repos/owner.d.ts.map +1 -0
- package/dist/src/storage/providers/sqlite/repos/owner.js +124 -0
- package/dist/src/storage/providers/sqlite/repos/owner.js.map +1 -0
- package/dist/src/storage/providers/sqlite/repos/service-manifest.d.ts +17 -0
- package/dist/src/storage/providers/sqlite/repos/service-manifest.d.ts.map +1 -0
- package/dist/src/storage/providers/sqlite/repos/service-manifest.js +114 -0
- package/dist/src/storage/providers/sqlite/repos/service-manifest.js.map +1 -0
- package/dist/src/storage/providers/sqlite/repos/storage-file.d.ts +19 -0
- package/dist/src/storage/providers/sqlite/repos/storage-file.d.ts.map +1 -0
- package/dist/src/storage/providers/sqlite/repos/storage-file.js +128 -0
- package/dist/src/storage/providers/sqlite/repos/storage-file.js.map +1 -0
- package/dist/src/storage/providers/sqlite/repos/work.d.ts +13 -0
- package/dist/src/storage/providers/sqlite/repos/work.d.ts.map +1 -0
- package/dist/src/storage/providers/sqlite/repos/work.js +86 -0
- package/dist/src/storage/providers/sqlite/repos/work.js.map +1 -0
- package/dist/src/storage/providers/sqlite/schema.d.ts +7 -0
- package/dist/src/storage/providers/sqlite/schema.d.ts.map +1 -0
- package/dist/src/storage/providers/sqlite/schema.js +1090 -0
- package/dist/src/storage/providers/sqlite/schema.js.map +1 -0
- package/dist/src/storage/repositories/action.repository.d.ts +14 -0
- package/dist/src/storage/repositories/action.repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/action.repository.js +2 -0
- package/dist/src/storage/repositories/action.repository.js.map +1 -0
- package/dist/src/storage/repositories/agent.repository.d.ts +18 -0
- package/dist/src/storage/repositories/agent.repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/agent.repository.js +2 -0
- package/dist/src/storage/repositories/agent.repository.js.map +1 -0
- package/dist/src/storage/repositories/app-marketplace.repository.d.ts +9 -0
- package/dist/src/storage/repositories/app-marketplace.repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/app-marketplace.repository.js +2 -0
- package/dist/src/storage/repositories/app-marketplace.repository.js.map +1 -0
- package/dist/src/storage/repositories/app.repository.d.ts +17 -0
- package/dist/src/storage/repositories/app.repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/app.repository.js +2 -0
- package/dist/src/storage/repositories/app.repository.js.map +1 -0
- package/dist/src/storage/repositories/auth.repository.d.ts +6 -0
- package/dist/src/storage/repositories/auth.repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/auth.repository.js +2 -0
- package/dist/src/storage/repositories/auth.repository.js.map +1 -0
- package/dist/src/storage/repositories/board.repository.d.ts +27 -0
- package/dist/src/storage/repositories/board.repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/board.repository.js +2 -0
- package/dist/src/storage/repositories/board.repository.js.map +1 -0
- package/dist/src/storage/repositories/catalogue.repository.d.ts +18 -0
- package/dist/src/storage/repositories/catalogue.repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/catalogue.repository.js +2 -0
- package/dist/src/storage/repositories/catalogue.repository.js.map +1 -0
- package/dist/src/storage/repositories/config.repository.d.ts +17 -0
- package/dist/src/storage/repositories/config.repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/config.repository.js +2 -0
- package/dist/src/storage/repositories/config.repository.js.map +1 -0
- package/dist/src/storage/repositories/consent.repository.d.ts +19 -0
- package/dist/src/storage/repositories/consent.repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/consent.repository.js +2 -0
- package/dist/src/storage/repositories/consent.repository.js.map +1 -0
- package/dist/src/storage/repositories/device-auth.repository.d.ts +11 -0
- package/dist/src/storage/repositories/device-auth.repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/device-auth.repository.js +2 -0
- package/dist/src/storage/repositories/device-auth.repository.js.map +1 -0
- package/dist/src/storage/repositories/dispute.repository.d.ts +12 -0
- package/dist/src/storage/repositories/dispute.repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/dispute.repository.js +2 -0
- package/dist/src/storage/repositories/dispute.repository.js.map +1 -0
- package/dist/src/storage/repositories/extension-instance.repository.d.ts +9 -0
- package/dist/src/storage/repositories/extension-instance.repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/extension-instance.repository.js +2 -0
- package/dist/src/storage/repositories/extension-instance.repository.js.map +1 -0
- package/dist/src/storage/repositories/federation.repository.d.ts +37 -0
- package/dist/src/storage/repositories/federation.repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/federation.repository.js +2 -0
- package/dist/src/storage/repositories/federation.repository.js.map +1 -0
- package/dist/src/storage/repositories/file.repository.d.ts +14 -0
- package/dist/src/storage/repositories/file.repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/file.repository.js +2 -0
- package/dist/src/storage/repositories/file.repository.js.map +1 -0
- package/dist/src/storage/repositories/identity.repository.d.ts +29 -0
- package/dist/src/storage/repositories/identity.repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/identity.repository.js +2 -0
- package/dist/src/storage/repositories/identity.repository.js.map +1 -0
- package/dist/src/storage/repositories/index.d.ts +34 -0
- package/dist/src/storage/repositories/index.d.ts.map +1 -0
- package/dist/src/storage/repositories/index.js +2 -0
- package/dist/src/storage/repositories/index.js.map +1 -0
- package/dist/src/storage/repositories/knowledge.repository.d.ts +18 -0
- package/dist/src/storage/repositories/knowledge.repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/knowledge.repository.js +2 -0
- package/dist/src/storage/repositories/knowledge.repository.js.map +1 -0
- package/dist/src/storage/repositories/marketplace.repository.d.ts +23 -0
- package/dist/src/storage/repositories/marketplace.repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/marketplace.repository.js +2 -0
- package/dist/src/storage/repositories/marketplace.repository.js.map +1 -0
- package/dist/src/storage/repositories/memory.repository.d.ts +32 -0
- package/dist/src/storage/repositories/memory.repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/memory.repository.js +2 -0
- package/dist/src/storage/repositories/memory.repository.js.map +1 -0
- package/dist/src/storage/repositories/micro-memory.repository.d.ts +10 -0
- package/dist/src/storage/repositories/micro-memory.repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/micro-memory.repository.js +2 -0
- package/dist/src/storage/repositories/micro-memory.repository.js.map +1 -0
- package/dist/src/storage/repositories/moderation.repository.d.ts +36 -0
- package/dist/src/storage/repositories/moderation.repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/moderation.repository.js +2 -0
- package/dist/src/storage/repositories/moderation.repository.js.map +1 -0
- package/dist/src/storage/repositories/node.repository.d.ts +59 -0
- package/dist/src/storage/repositories/node.repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/node.repository.js +2 -0
- package/dist/src/storage/repositories/node.repository.js.map +1 -0
- package/dist/src/storage/repositories/notification-template.repository.d.ts +8 -0
- package/dist/src/storage/repositories/notification-template.repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/notification-template.repository.js +2 -0
- package/dist/src/storage/repositories/notification-template.repository.js.map +1 -0
- package/dist/src/storage/repositories/notification.repository.d.ts +14 -0
- package/dist/src/storage/repositories/notification.repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/notification.repository.js +2 -0
- package/dist/src/storage/repositories/notification.repository.js.map +1 -0
- package/dist/src/storage/repositories/oauth.repository.d.ts +19 -0
- package/dist/src/storage/repositories/oauth.repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/oauth.repository.js +2 -0
- package/dist/src/storage/repositories/oauth.repository.js.map +1 -0
- package/dist/src/storage/repositories/organism.repository.d.ts +34 -0
- package/dist/src/storage/repositories/organism.repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/organism.repository.js +2 -0
- package/dist/src/storage/repositories/organism.repository.js.map +1 -0
- package/dist/src/storage/repositories/otk.repository.d.ts +9 -0
- package/dist/src/storage/repositories/otk.repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/otk.repository.js +2 -0
- package/dist/src/storage/repositories/otk.repository.js.map +1 -0
- package/dist/src/storage/repositories/owner.repository.d.ts +9 -0
- package/dist/src/storage/repositories/owner.repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/owner.repository.js +2 -0
- package/dist/src/storage/repositories/owner.repository.js.map +1 -0
- package/dist/src/storage/repositories/package-instance.repository.d.ts +26 -0
- package/dist/src/storage/repositories/package-instance.repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/package-instance.repository.js +12 -0
- package/dist/src/storage/repositories/package-instance.repository.js.map +1 -0
- package/dist/src/storage/repositories/package.repository.d.ts +28 -0
- package/dist/src/storage/repositories/package.repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/package.repository.js +12 -0
- package/dist/src/storage/repositories/package.repository.js.map +1 -0
- package/dist/src/storage/repositories/replication-queue.repository.d.ts +10 -0
- package/dist/src/storage/repositories/replication-queue.repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/replication-queue.repository.js +2 -0
- package/dist/src/storage/repositories/replication-queue.repository.js.map +1 -0
- package/dist/src/storage/repositories/scheduler.repository.d.ts +30 -0
- package/dist/src/storage/repositories/scheduler.repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/scheduler.repository.js +2 -0
- package/dist/src/storage/repositories/scheduler.repository.js.map +1 -0
- package/dist/src/storage/repositories/schema.repository.d.ts +9 -0
- package/dist/src/storage/repositories/schema.repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/schema.repository.js +2 -0
- package/dist/src/storage/repositories/schema.repository.js.map +1 -0
- package/dist/src/storage/repositories/session.repository.d.ts +22 -0
- package/dist/src/storage/repositories/session.repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/session.repository.js +2 -0
- package/dist/src/storage/repositories/session.repository.js.map +1 -0
- package/dist/src/storage/repositories/system-prompt.repository.d.ts +14 -0
- package/dist/src/storage/repositories/system-prompt.repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/system-prompt.repository.js +2 -0
- package/dist/src/storage/repositories/system-prompt.repository.js.map +1 -0
- package/dist/src/storage/repositories/template-listing.repository.d.ts +44 -0
- package/dist/src/storage/repositories/template-listing.repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/template-listing.repository.js +13 -0
- package/dist/src/storage/repositories/template-listing.repository.js.map +1 -0
- package/dist/src/storage/repositories/wallet.repository.d.ts +8 -0
- package/dist/src/storage/repositories/wallet.repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/wallet.repository.js +2 -0
- package/dist/src/storage/repositories/wallet.repository.js.map +1 -0
- package/dist/src/storage/repositories/work.repository.d.ts +10 -0
- package/dist/src/storage/repositories/work.repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/work.repository.js +2 -0
- package/dist/src/storage/repositories/work.repository.js.map +1 -0
- package/dist/src/storage/storage-factory.d.ts +9 -0
- package/dist/src/storage/storage-factory.d.ts.map +1 -0
- package/dist/src/storage/storage-factory.js +20 -0
- package/dist/src/storage/storage-factory.js.map +1 -0
- package/dist/src/types/route-manifest.d.ts +53 -0
- package/dist/src/types/route-manifest.d.ts.map +1 -0
- package/dist/src/types/route-manifest.js +44 -0
- package/dist/src/types/route-manifest.js.map +1 -0
- package/dist/src/utils/catalogue-hash.d.ts +10 -0
- package/dist/src/utils/catalogue-hash.d.ts.map +1 -0
- package/dist/src/utils/catalogue-hash.js +23 -0
- package/dist/src/utils/catalogue-hash.js.map +1 -0
- package/dist/src/utils/env-config.d.ts +8 -0
- package/dist/src/utils/env-config.d.ts.map +1 -0
- package/dist/src/utils/env-config.js +1264 -0
- package/dist/src/utils/env-config.js.map +1 -0
- package/dist/src/utils/env-validator.d.ts +14 -0
- package/dist/src/utils/env-validator.d.ts.map +1 -0
- package/dist/src/utils/env-validator.js +338 -0
- package/dist/src/utils/env-validator.js.map +1 -0
- package/dist/src/utils/fire-hook.d.ts +9 -0
- package/dist/src/utils/fire-hook.d.ts.map +1 -0
- package/dist/src/utils/fire-hook.js +12 -0
- package/dist/src/utils/fire-hook.js.map +1 -0
- package/dist/src/utils/gaii.d.ts +57 -0
- package/dist/src/utils/gaii.d.ts.map +1 -0
- package/dist/src/utils/gaii.js +148 -0
- package/dist/src/utils/gaii.js.map +1 -0
- package/dist/src/utils/ghii-resolver.d.ts +14 -0
- package/dist/src/utils/ghii-resolver.d.ts.map +1 -0
- package/dist/src/utils/ghii-resolver.js +21 -0
- package/dist/src/utils/ghii-resolver.js.map +1 -0
- package/dist/src/utils/logger.d.ts +9 -0
- package/dist/src/utils/logger.d.ts.map +1 -0
- package/dist/src/utils/logger.js +36 -0
- package/dist/src/utils/logger.js.map +1 -0
- package/dist/src/utils/otk.d.ts +2 -0
- package/dist/src/utils/otk.d.ts.map +1 -0
- package/dist/src/utils/otk.js +5 -0
- package/dist/src/utils/otk.js.map +1 -0
- package/dist/src/utils/tracking-code.d.ts +9 -0
- package/dist/src/utils/tracking-code.d.ts.map +1 -0
- package/dist/src/utils/tracking-code.js +16 -0
- package/dist/src/utils/tracking-code.js.map +1 -0
- package/dist/src/utils/url-validator.d.ts +5 -0
- package/dist/src/utils/url-validator.d.ts.map +1 -0
- package/dist/src/utils/url-validator.js +56 -0
- package/dist/src/utils/url-validator.js.map +1 -0
- package/dist/static/app-catalog.html +4401 -0
- package/dist/static/cookieconsent.css +1 -0
- package/dist/static/cookieconsent.umd.js +7 -0
- package/dist/static/icons/icon-maskable.svg +27 -0
- package/dist/static/icons/icon.svg +27 -0
- package/dist/static/manifest.json +17 -0
- package/dist/static/offline.html +167 -0
- package/dist/static/sw.js +260 -0
- package/package.json +182 -0
- package/prisma/schema.prisma +1086 -0
|
@@ -0,0 +1,4156 @@
|
|
|
1
|
+
import Database from 'better-sqlite3';
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
import { mkdirSync } from 'node:fs';
|
|
4
|
+
import { dirname } from 'node:path';
|
|
5
|
+
import { initializeSchema } from './schema.js';
|
|
6
|
+
import { matchWildcardPattern, consentMatchPattern } from '../../pattern-utils.js';
|
|
7
|
+
import { matchesRecipient } from '../../../services/consent.js';
|
|
8
|
+
import { parseGaiiLoose } from '../../../utils/gaii.js';
|
|
9
|
+
export class SqliteStorage {
|
|
10
|
+
db;
|
|
11
|
+
chunkedUploads = new Map();
|
|
12
|
+
constructor(dbPath) {
|
|
13
|
+
mkdirSync(dirname(dbPath), { recursive: true });
|
|
14
|
+
this.db = new Database(dbPath);
|
|
15
|
+
this.db.pragma('journal_mode = WAL');
|
|
16
|
+
this.db.pragma('busy_timeout = 5000');
|
|
17
|
+
this.db.pragma('foreign_keys = ON');
|
|
18
|
+
initializeSchema(this.db);
|
|
19
|
+
}
|
|
20
|
+
/** Close the database connection */
|
|
21
|
+
close() {
|
|
22
|
+
this.db.close();
|
|
23
|
+
}
|
|
24
|
+
// ══════════════════════════════════════════════════════════
|
|
25
|
+
// ── Owners ──
|
|
26
|
+
// ══════════════════════════════════════════════════════════
|
|
27
|
+
async createOwner(owner) {
|
|
28
|
+
try {
|
|
29
|
+
this.db.prepare(`INSERT INTO owners (name, displayName, publicKey, roles, createdAt)
|
|
30
|
+
VALUES (?, ?, ?, ?, ?)`).run(owner.name, owner.displayName ?? null, owner.publicKey, JSON.stringify(owner.roles), owner.createdAt);
|
|
31
|
+
return owner;
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
if (err instanceof Error && err.message?.includes('UNIQUE constraint failed'))
|
|
35
|
+
throw new Error('NAME_TAKEN', { cause: err });
|
|
36
|
+
throw err;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
async getOwner(name) {
|
|
40
|
+
const row = this.db.prepare('SELECT * FROM owners WHERE name = ?').get(name);
|
|
41
|
+
return row ? this.deserializeOwner(row) : null;
|
|
42
|
+
}
|
|
43
|
+
async listOwners() {
|
|
44
|
+
const rows = this.db.prepare('SELECT * FROM owners').all();
|
|
45
|
+
return rows.map(r => this.deserializeOwner(r));
|
|
46
|
+
}
|
|
47
|
+
async updateOwner(name, updates) {
|
|
48
|
+
const existing = await this.getOwner(name);
|
|
49
|
+
if (!existing)
|
|
50
|
+
return null;
|
|
51
|
+
const updated = { ...existing, ...updates };
|
|
52
|
+
this.db.prepare(`UPDATE owners SET displayName = ?, publicKey = ?, roles = ?, createdAt = ? WHERE name = ?`).run(updated.displayName ?? null, updated.publicKey, JSON.stringify(updated.roles), updated.createdAt, name);
|
|
53
|
+
return updated;
|
|
54
|
+
}
|
|
55
|
+
async deleteOwner(name) {
|
|
56
|
+
const txn = this.db.transaction(() => {
|
|
57
|
+
// 1. Get all agents belonging to this owner
|
|
58
|
+
const agentRows = this.db.prepare('SELECT gaii FROM agents WHERE owner = ?').all(name);
|
|
59
|
+
const agentGaiis = agentRows.map(r => r.gaii);
|
|
60
|
+
// 2. Cascade delete all agent-related data for each agent
|
|
61
|
+
for (const gaii of agentGaiis) {
|
|
62
|
+
this.cascadeDeleteAgentData(gaii);
|
|
63
|
+
}
|
|
64
|
+
// 3. Delete all agents for this owner
|
|
65
|
+
this.db.prepare('DELETE FROM agents WHERE owner = ?').run(name);
|
|
66
|
+
// 4. Delete GHII records for this owner
|
|
67
|
+
this.db.prepare('DELETE FROM ghiis WHERE ownerName = ?').run(name);
|
|
68
|
+
// 5. Delete personal nodes and their mailbox items & push subscriptions
|
|
69
|
+
const nodeRows = this.db.prepare('SELECT nodeId FROM personal_nodes WHERE ownerName = ?').all(name);
|
|
70
|
+
for (const node of nodeRows) {
|
|
71
|
+
this.db.prepare('DELETE FROM mailbox_items WHERE personalNodeId = ?').run(node.nodeId);
|
|
72
|
+
this.db.prepare('DELETE FROM personal_push_subscriptions WHERE personalNodeId = ?').run(node.nodeId);
|
|
73
|
+
this.db.prepare('DELETE FROM notification_preferences WHERE personalNodeId = ?').run(node.nodeId);
|
|
74
|
+
}
|
|
75
|
+
this.db.prepare('DELETE FROM personal_nodes WHERE ownerName = ?').run(name);
|
|
76
|
+
// 6. Delete push subscriptions for this owner
|
|
77
|
+
this.db.prepare('DELETE FROM push_subscriptions WHERE ownerName = ?').run(name);
|
|
78
|
+
this.db.prepare('DELETE FROM personal_push_subscriptions WHERE ownerName = ?').run(name);
|
|
79
|
+
// 7. Delete listings for this owner
|
|
80
|
+
this.db.prepare('DELETE FROM listings WHERE ownerName = ?').run(name);
|
|
81
|
+
// 8. Delete purchases for this owner (as buyer or seller)
|
|
82
|
+
this.db.prepare('DELETE FROM purchases WHERE buyerOwner = ? OR sellerOwner = ?').run(name, name);
|
|
83
|
+
// 9. Delete chat instances for this owner
|
|
84
|
+
this.db.prepare('DELETE FROM chat_instances WHERE ownerName = ?').run(name);
|
|
85
|
+
// 10. Delete email verifications for this owner
|
|
86
|
+
this.db.prepare('DELETE FROM email_verifications WHERE ownerName = ?').run(name);
|
|
87
|
+
// 11. Delete the owner record itself
|
|
88
|
+
const result = this.db.prepare('DELETE FROM owners WHERE name = ?').run(name);
|
|
89
|
+
return result.changes > 0;
|
|
90
|
+
});
|
|
91
|
+
return txn();
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Cascade-delete all data associated with a single agent GAII.
|
|
95
|
+
* Called inside a transaction by both deleteOwner and deleteAgent.
|
|
96
|
+
*/
|
|
97
|
+
cascadeDeleteAgentData(gaii) {
|
|
98
|
+
// Memory
|
|
99
|
+
this.db.prepare('DELETE FROM memory WHERE ownerGaii = ?').run(gaii);
|
|
100
|
+
// Micro-memory
|
|
101
|
+
this.db.prepare('DELETE FROM micro_memory WHERE gaii = ?').run(gaii);
|
|
102
|
+
// Actions
|
|
103
|
+
this.db.prepare('DELETE FROM actions WHERE providerGaii = ?').run(gaii);
|
|
104
|
+
// Work (as provider or requester) — also clean up related disputes
|
|
105
|
+
const workRows = this.db.prepare('SELECT trackingCode FROM work WHERE providerGaii = ? OR requesterGaii = ?').all(gaii, gaii);
|
|
106
|
+
for (const w of workRows) {
|
|
107
|
+
this.db.prepare('DELETE FROM dispute_audit WHERE disputeId IN (SELECT id FROM disputes WHERE trackingCode = ?)').run(w.trackingCode);
|
|
108
|
+
this.db.prepare('DELETE FROM disputes WHERE trackingCode = ?').run(w.trackingCode);
|
|
109
|
+
}
|
|
110
|
+
this.db.prepare('DELETE FROM work WHERE providerGaii = ? OR requesterGaii = ?').run(gaii, gaii);
|
|
111
|
+
// Wallet transactions
|
|
112
|
+
this.db.prepare('DELETE FROM wallet_transactions WHERE gaii = ?').run(gaii);
|
|
113
|
+
// Board posts authored by this agent
|
|
114
|
+
this.db.prepare('DELETE FROM board_posts WHERE authorGaii = ?').run(gaii);
|
|
115
|
+
// Board subscriptions
|
|
116
|
+
this.db.prepare('DELETE FROM board_subscriptions WHERE gaii = ?').run(gaii);
|
|
117
|
+
// Boards owned by this agent — also delete their posts and subscriptions
|
|
118
|
+
const boardRows = this.db.prepare('SELECT id FROM boards WHERE ownerGaii = ?').all(gaii);
|
|
119
|
+
for (const b of boardRows) {
|
|
120
|
+
this.db.prepare('DELETE FROM board_posts WHERE boardId = ?').run(b.id);
|
|
121
|
+
this.db.prepare('DELETE FROM board_subscriptions WHERE boardId = ?').run(b.id);
|
|
122
|
+
}
|
|
123
|
+
this.db.prepare('DELETE FROM boards WHERE ownerGaii = ?').run(gaii);
|
|
124
|
+
// Consents and consent audit
|
|
125
|
+
this.db.prepare('DELETE FROM consent_audit WHERE ownerGaii = ?').run(gaii);
|
|
126
|
+
this.db.prepare('DELETE FROM consents WHERE ownerGaii = ?').run(gaii);
|
|
127
|
+
// Storage files
|
|
128
|
+
this.db.prepare('DELETE FROM storage_files WHERE ownerGaii = ?').run(gaii);
|
|
129
|
+
// Matches (as profileA or profileB)
|
|
130
|
+
this.db.prepare('DELETE FROM matches WHERE profileA = ? OR profileB = ?').run(gaii, gaii);
|
|
131
|
+
// Flags raised by this agent
|
|
132
|
+
this.db.prepare('DELETE FROM flags WHERE flaggedBy = ?').run(gaii);
|
|
133
|
+
// Escrow holds
|
|
134
|
+
this.db.prepare('DELETE FROM escrow_holds WHERE fromGaii = ?').run(gaii);
|
|
135
|
+
// OTKs (one-time keys)
|
|
136
|
+
this.db.prepare('DELETE FROM otks WHERE ownerGaii = ?').run(gaii);
|
|
137
|
+
// OAuth refresh tokens and approvals for this agent
|
|
138
|
+
this.db.prepare('DELETE FROM oauth_refresh_tokens WHERE gaii = ?').run(gaii);
|
|
139
|
+
this.db.prepare('DELETE FROM oauth_approvals WHERE gaii = ?').run(gaii);
|
|
140
|
+
}
|
|
141
|
+
deserializeOwner(row) {
|
|
142
|
+
return {
|
|
143
|
+
name: row.name,
|
|
144
|
+
displayName: row.displayName ?? undefined,
|
|
145
|
+
publicKey: row.publicKey,
|
|
146
|
+
roles: JSON.parse(row.roles),
|
|
147
|
+
createdAt: row.createdAt,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
// ══════════════════════════════════════════════════════════
|
|
151
|
+
// ── Agents ──
|
|
152
|
+
// ══════════════════════════════════════════════════════════
|
|
153
|
+
async createAgent(agent) {
|
|
154
|
+
try {
|
|
155
|
+
this.db.prepare(`INSERT INTO agents (gaii, name, owner, displayName, description, capabilities, publicKey, trustScore, morselBalance, createdAt, lastSeen, semantic, allowedOrigins, defaultScopes)
|
|
156
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(agent.gaii, agent.name, agent.owner, agent.displayName ?? null, agent.description ?? null, JSON.stringify(agent.capabilities), agent.publicKey, agent.trustScore, agent.morselBalance, agent.createdAt, agent.lastSeen, agent.semantic ? JSON.stringify(agent.semantic) : null, agent.allowedOrigins ? JSON.stringify(agent.allowedOrigins) : null, agent.defaultScopes ? JSON.stringify(agent.defaultScopes) : null);
|
|
157
|
+
return agent;
|
|
158
|
+
}
|
|
159
|
+
catch (err) {
|
|
160
|
+
if (err instanceof Error && err.message?.includes('UNIQUE constraint failed'))
|
|
161
|
+
throw new Error('NAME_TAKEN', { cause: err });
|
|
162
|
+
throw err;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
async getAgent(gaii) {
|
|
166
|
+
const row = this.db.prepare('SELECT * FROM agents WHERE gaii = ?').get(gaii);
|
|
167
|
+
return row ? this.deserializeAgent(row) : null;
|
|
168
|
+
}
|
|
169
|
+
async getAgentsByOwner(owner) {
|
|
170
|
+
const rows = this.db.prepare('SELECT * FROM agents WHERE owner = ?').all(owner);
|
|
171
|
+
return rows.map(r => this.deserializeAgent(r));
|
|
172
|
+
}
|
|
173
|
+
async updateAgent(gaii, updates) {
|
|
174
|
+
const existing = await this.getAgent(gaii);
|
|
175
|
+
if (!existing)
|
|
176
|
+
return null;
|
|
177
|
+
const updated = { ...existing, ...updates };
|
|
178
|
+
this.db.prepare(`UPDATE agents SET name = ?, owner = ?, displayName = ?, description = ?, capabilities = ?,
|
|
179
|
+
publicKey = ?, trustScore = ?, morselBalance = ?, createdAt = ?, lastSeen = ?, semantic = ?,
|
|
180
|
+
allowedOrigins = ?, defaultScopes = ?
|
|
181
|
+
WHERE gaii = ?`).run(updated.name, updated.owner, updated.displayName ?? null, updated.description ?? null, JSON.stringify(updated.capabilities), updated.publicKey, updated.trustScore, updated.morselBalance, updated.createdAt, updated.lastSeen, updated.semantic ? JSON.stringify(updated.semantic) : null, updated.allowedOrigins ? JSON.stringify(updated.allowedOrigins) : null, updated.defaultScopes ? JSON.stringify(updated.defaultScopes) : null, gaii);
|
|
182
|
+
return updated;
|
|
183
|
+
}
|
|
184
|
+
async deleteAgent(gaii) {
|
|
185
|
+
const txn = this.db.transaction(() => {
|
|
186
|
+
// Cascade delete all agent-related data
|
|
187
|
+
this.cascadeDeleteAgentData(gaii);
|
|
188
|
+
// Delete the agent record itself
|
|
189
|
+
const result = this.db.prepare('DELETE FROM agents WHERE gaii = ?').run(gaii);
|
|
190
|
+
return result.changes > 0;
|
|
191
|
+
});
|
|
192
|
+
return txn();
|
|
193
|
+
}
|
|
194
|
+
async listAgents() {
|
|
195
|
+
const rows = this.db.prepare('SELECT * FROM agents').all();
|
|
196
|
+
return rows.map(r => this.deserializeAgent(r));
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Resolve any identity (GAII, GHII, bare owner) to the owner's GHII identifier.
|
|
200
|
+
* All balance operations go through GHII — agents don't have their own balance.
|
|
201
|
+
*/
|
|
202
|
+
resolveGhii(identity) {
|
|
203
|
+
// GHII format: owner@node (no #)
|
|
204
|
+
if (!identity.includes('#') && identity.includes('@'))
|
|
205
|
+
return identity;
|
|
206
|
+
// GAII format: agent#owner@node → extract owner → lookup GHII
|
|
207
|
+
if (identity.includes('#')) {
|
|
208
|
+
const hashIdx = identity.indexOf('#');
|
|
209
|
+
const atIdx = identity.lastIndexOf('@');
|
|
210
|
+
if (atIdx > hashIdx) {
|
|
211
|
+
const owner = identity.slice(hashIdx + 1, atIdx);
|
|
212
|
+
const row = this.db.prepare('SELECT ghii FROM ghiis WHERE username = ?').get(owner);
|
|
213
|
+
return row?.ghii ?? null;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
// Bare owner name → lookup GHII
|
|
217
|
+
const row = this.db.prepare('SELECT ghii FROM ghiis WHERE username = ?').get(identity);
|
|
218
|
+
return row?.ghii ?? null;
|
|
219
|
+
}
|
|
220
|
+
async debitBalance(gaii, amount) {
|
|
221
|
+
const ghii = this.resolveGhii(gaii);
|
|
222
|
+
if (!ghii)
|
|
223
|
+
return false;
|
|
224
|
+
const result = this.db.prepare(`UPDATE ghiis SET morselBalance = COALESCE(morselBalance, 0) - ? WHERE ghii = ? AND COALESCE(morselBalance, 0) >= ?`).run(amount, ghii, amount);
|
|
225
|
+
return result.changes > 0;
|
|
226
|
+
}
|
|
227
|
+
async creditBalance(gaii, amount) {
|
|
228
|
+
const ghii = this.resolveGhii(gaii);
|
|
229
|
+
if (!ghii)
|
|
230
|
+
return false;
|
|
231
|
+
const result = this.db.prepare(`UPDATE ghiis SET morselBalance = COALESCE(morselBalance, 0) + ? WHERE ghii = ?`).run(amount, ghii);
|
|
232
|
+
return result.changes > 0;
|
|
233
|
+
}
|
|
234
|
+
async creditBalanceCapped(gaii, amount, cap) {
|
|
235
|
+
const ghii = this.resolveGhii(gaii);
|
|
236
|
+
if (!ghii)
|
|
237
|
+
return 0;
|
|
238
|
+
const txn = this.db.transaction(() => {
|
|
239
|
+
const row = this.db.prepare('SELECT morselBalance FROM ghiis WHERE ghii = ?').get(ghii);
|
|
240
|
+
if (!row)
|
|
241
|
+
return 0;
|
|
242
|
+
const oldBalance = row.morselBalance ?? 0;
|
|
243
|
+
if (oldBalance >= cap)
|
|
244
|
+
return 0;
|
|
245
|
+
const actualCredit = Math.min(amount, cap - oldBalance);
|
|
246
|
+
if (actualCredit <= 0)
|
|
247
|
+
return 0;
|
|
248
|
+
this.db.prepare('UPDATE ghiis SET morselBalance = COALESCE(morselBalance, 0) + ? WHERE ghii = ?').run(actualCredit, ghii);
|
|
249
|
+
return actualCredit;
|
|
250
|
+
});
|
|
251
|
+
return txn();
|
|
252
|
+
}
|
|
253
|
+
async transferBalance(fromGaii, toGaii, amount) {
|
|
254
|
+
const fromGhii = this.resolveGhii(fromGaii);
|
|
255
|
+
const toGhii = this.resolveGhii(toGaii);
|
|
256
|
+
if (!fromGhii || !toGhii)
|
|
257
|
+
return false;
|
|
258
|
+
if (fromGhii === toGhii)
|
|
259
|
+
return true; // Same owner — no-op
|
|
260
|
+
const txn = this.db.transaction(() => {
|
|
261
|
+
const debit = this.db.prepare(`UPDATE ghiis SET morselBalance = COALESCE(morselBalance, 0) - ? WHERE ghii = ? AND COALESCE(morselBalance, 0) >= ?`).run(amount, fromGhii, amount);
|
|
262
|
+
if (debit.changes === 0)
|
|
263
|
+
return false;
|
|
264
|
+
this.db.prepare(`UPDATE ghiis SET morselBalance = COALESCE(morselBalance, 0) + ? WHERE ghii = ?`).run(amount, toGhii);
|
|
265
|
+
return true;
|
|
266
|
+
});
|
|
267
|
+
return txn();
|
|
268
|
+
}
|
|
269
|
+
deserializeAgent(row) {
|
|
270
|
+
const record = {
|
|
271
|
+
gaii: row.gaii,
|
|
272
|
+
name: row.name,
|
|
273
|
+
owner: row.owner,
|
|
274
|
+
capabilities: JSON.parse(row.capabilities),
|
|
275
|
+
publicKey: row.publicKey,
|
|
276
|
+
trustScore: row.trustScore,
|
|
277
|
+
morselBalance: row.morselBalance,
|
|
278
|
+
createdAt: row.createdAt,
|
|
279
|
+
lastSeen: row.lastSeen,
|
|
280
|
+
};
|
|
281
|
+
if (row.displayName)
|
|
282
|
+
record.displayName = row.displayName;
|
|
283
|
+
if (row.description)
|
|
284
|
+
record.description = row.description;
|
|
285
|
+
if (row.semantic)
|
|
286
|
+
record.semantic = JSON.parse(row.semantic);
|
|
287
|
+
if (row.allowedOrigins)
|
|
288
|
+
record.allowedOrigins = JSON.parse(row.allowedOrigins);
|
|
289
|
+
if (row.defaultScopes)
|
|
290
|
+
record.defaultScopes = JSON.parse(row.defaultScopes);
|
|
291
|
+
return record;
|
|
292
|
+
}
|
|
293
|
+
// ══════════════════════════════════════════════════════════
|
|
294
|
+
// ── Memory ──
|
|
295
|
+
// ══════════════════════════════════════════════════════════
|
|
296
|
+
async setMemory(record) {
|
|
297
|
+
const existing = await this.getMemory(record.ownerGaii, record.key);
|
|
298
|
+
if (existing) {
|
|
299
|
+
record.version = existing.version + 1;
|
|
300
|
+
this.db.prepare(`UPDATE memory SET value = ?, visibility = ?, tags = ?, ttlHours = ?, version = ?,
|
|
301
|
+
createdAt = ?, updatedAt = ?, flagCount = ?, allowedOrigins = ? WHERE ownerGaii = ? AND key = ?`).run(JSON.stringify(record.value), record.visibility, JSON.stringify(record.tags), record.ttlHours, record.version, record.createdAt, record.updatedAt, record.flagCount ?? 0, record.allowedOrigins ? JSON.stringify(record.allowedOrigins) : null, record.ownerGaii, record.key);
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
this.db.prepare(`INSERT INTO memory (ownerGaii, key, value, visibility, tags, ttlHours, version, createdAt, updatedAt, flagCount, allowedOrigins)
|
|
305
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(record.ownerGaii, record.key, JSON.stringify(record.value), record.visibility, JSON.stringify(record.tags), record.ttlHours, record.version, record.createdAt, record.updatedAt, record.flagCount ?? 0, record.allowedOrigins ? JSON.stringify(record.allowedOrigins) : null);
|
|
306
|
+
}
|
|
307
|
+
return record;
|
|
308
|
+
}
|
|
309
|
+
async setMemoryIfVersion(record, expectedVersion) {
|
|
310
|
+
const result = this.db.prepare(`UPDATE memory SET value = ?, visibility = ?, tags = ?, ttlHours = ?, version = ?,
|
|
311
|
+
updatedAt = ?, flagCount = ?, allowedOrigins = ? WHERE ownerGaii = ? AND key = ? AND version = ?`).run(JSON.stringify(record.value), record.visibility, JSON.stringify(record.tags), record.ttlHours, record.version, record.updatedAt, record.flagCount ?? 0, record.allowedOrigins ? JSON.stringify(record.allowedOrigins) : null, record.ownerGaii, record.key, expectedVersion);
|
|
312
|
+
if (result.changes === 0)
|
|
313
|
+
return null; // version conflict
|
|
314
|
+
return record;
|
|
315
|
+
}
|
|
316
|
+
isMemoryExpired(record) {
|
|
317
|
+
if (!record.ttlHours)
|
|
318
|
+
return false;
|
|
319
|
+
const createdMs = new Date(record.createdAt).getTime();
|
|
320
|
+
return Date.now() > createdMs + record.ttlHours * 3_600_000;
|
|
321
|
+
}
|
|
322
|
+
async getMemory(ownerGaii, key) {
|
|
323
|
+
const row = this.db.prepare('SELECT * FROM memory WHERE ownerGaii = ? AND key = ?').get(ownerGaii, key);
|
|
324
|
+
if (!row)
|
|
325
|
+
return null;
|
|
326
|
+
const record = this.deserializeMemory(row);
|
|
327
|
+
if (this.isMemoryExpired(record)) {
|
|
328
|
+
this.db.prepare('DELETE FROM memory WHERE ownerGaii = ? AND key = ?').run(ownerGaii, key);
|
|
329
|
+
return null;
|
|
330
|
+
}
|
|
331
|
+
return record;
|
|
332
|
+
}
|
|
333
|
+
async listMemory(ownerGaii, opts) {
|
|
334
|
+
let sql = 'SELECT * FROM memory WHERE ownerGaii = ?';
|
|
335
|
+
const params = [ownerGaii];
|
|
336
|
+
if (opts?.prefix) {
|
|
337
|
+
sql += ' AND key LIKE ?';
|
|
338
|
+
params.push(opts.prefix + '%');
|
|
339
|
+
}
|
|
340
|
+
if (opts?.visibility) {
|
|
341
|
+
sql += ' AND visibility = ?';
|
|
342
|
+
params.push(opts.visibility);
|
|
343
|
+
}
|
|
344
|
+
const rows = this.db.prepare(sql).all(...params);
|
|
345
|
+
const results = [];
|
|
346
|
+
for (const row of rows) {
|
|
347
|
+
const record = this.deserializeMemory(row);
|
|
348
|
+
if (this.isMemoryExpired(record)) {
|
|
349
|
+
this.db.prepare('DELETE FROM memory WHERE ownerGaii = ? AND key = ?').run(record.ownerGaii, record.key);
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
if (opts?.tags?.length) {
|
|
353
|
+
const hasTags = opts.tags.every(t => record.tags.includes(t));
|
|
354
|
+
if (!hasTags)
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
357
|
+
if (opts?.maxFlags !== undefined && (record.flagCount ?? 0) > opts.maxFlags)
|
|
358
|
+
continue;
|
|
359
|
+
results.push(record);
|
|
360
|
+
}
|
|
361
|
+
return results;
|
|
362
|
+
}
|
|
363
|
+
async listAllMemory(opts) {
|
|
364
|
+
let whereClauses = '';
|
|
365
|
+
const params = [];
|
|
366
|
+
if (opts?.ownerPrefix) {
|
|
367
|
+
whereClauses += ' AND ownerGaii LIKE ?';
|
|
368
|
+
params.push(opts.ownerPrefix + '%');
|
|
369
|
+
}
|
|
370
|
+
if (opts?.prefix) {
|
|
371
|
+
whereClauses += ' AND key LIKE ?';
|
|
372
|
+
params.push(opts.prefix + '%');
|
|
373
|
+
}
|
|
374
|
+
if (opts?.visibility) {
|
|
375
|
+
whereClauses += ' AND visibility = ?';
|
|
376
|
+
params.push(opts.visibility);
|
|
377
|
+
}
|
|
378
|
+
const whereStr = whereClauses ? ' WHERE ' + whereClauses.slice(5) : '';
|
|
379
|
+
const countRow = this.db.prepare('SELECT COUNT(*) as cnt FROM memory' + whereStr).get(...params);
|
|
380
|
+
const total = countRow.cnt;
|
|
381
|
+
const limit = opts?.limit ?? 50;
|
|
382
|
+
const offset = opts?.offset ?? 0;
|
|
383
|
+
const rows = this.db.prepare('SELECT * FROM memory' + whereStr + ' ORDER BY updatedAt DESC LIMIT ? OFFSET ?').all(...params, limit, offset);
|
|
384
|
+
const items = [];
|
|
385
|
+
for (const row of rows) {
|
|
386
|
+
const record = this.deserializeMemory(row);
|
|
387
|
+
if (this.isMemoryExpired(record)) {
|
|
388
|
+
this.db.prepare('DELETE FROM memory WHERE ownerGaii = ? AND key = ?').run(record.ownerGaii, record.key);
|
|
389
|
+
continue;
|
|
390
|
+
}
|
|
391
|
+
items.push(record);
|
|
392
|
+
}
|
|
393
|
+
return { items, total };
|
|
394
|
+
}
|
|
395
|
+
async deleteMemory(ownerGaii, key) {
|
|
396
|
+
const result = this.db.prepare('DELETE FROM memory WHERE ownerGaii = ? AND key = ?').run(ownerGaii, key);
|
|
397
|
+
return result.changes > 0;
|
|
398
|
+
}
|
|
399
|
+
async deleteAllMemory(ownerGaii) {
|
|
400
|
+
const result = this.db.prepare('DELETE FROM memory WHERE ownerGaii = ?').run(ownerGaii);
|
|
401
|
+
return result.changes;
|
|
402
|
+
}
|
|
403
|
+
async incrementMemoryFlagCount(ownerGaii, key) {
|
|
404
|
+
this.db.prepare('UPDATE memory SET flagCount = COALESCE(flagCount, 0) + 1 WHERE ownerGaii = ? AND key = ?').run(ownerGaii, key);
|
|
405
|
+
}
|
|
406
|
+
async searchMemory(ownerGaii, query, opts) {
|
|
407
|
+
const q = query.toLowerCase();
|
|
408
|
+
let sql = 'SELECT * FROM memory WHERE ownerGaii = ?';
|
|
409
|
+
const params = [ownerGaii];
|
|
410
|
+
if (opts?.visibility) {
|
|
411
|
+
sql += ' AND visibility = ?';
|
|
412
|
+
params.push(opts.visibility);
|
|
413
|
+
}
|
|
414
|
+
const rows = this.db.prepare(sql).all(...params);
|
|
415
|
+
const results = [];
|
|
416
|
+
for (const row of rows) {
|
|
417
|
+
const record = this.deserializeMemory(row);
|
|
418
|
+
if (this.isMemoryExpired(record)) {
|
|
419
|
+
this.db.prepare('DELETE FROM memory WHERE ownerGaii = ? AND key = ?').run(record.ownerGaii, record.key);
|
|
420
|
+
continue;
|
|
421
|
+
}
|
|
422
|
+
if (opts?.maxFlags !== undefined && (record.flagCount ?? 0) > opts.maxFlags)
|
|
423
|
+
continue;
|
|
424
|
+
const valStr = typeof record.value === 'string' ? record.value : JSON.stringify(record.value);
|
|
425
|
+
if (record.key.toLowerCase().includes(q) ||
|
|
426
|
+
valStr.toLowerCase().includes(q) ||
|
|
427
|
+
record.tags.some(t => t.toLowerCase().includes(q))) {
|
|
428
|
+
results.push(record);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
return results;
|
|
432
|
+
}
|
|
433
|
+
deserializeMemory(row) {
|
|
434
|
+
const record = {
|
|
435
|
+
key: row.key,
|
|
436
|
+
ownerGaii: row.ownerGaii,
|
|
437
|
+
value: JSON.parse(row.value),
|
|
438
|
+
visibility: row.visibility,
|
|
439
|
+
tags: JSON.parse(row.tags),
|
|
440
|
+
ttlHours: row.ttlHours,
|
|
441
|
+
version: row.version,
|
|
442
|
+
createdAt: row.createdAt,
|
|
443
|
+
updatedAt: row.updatedAt,
|
|
444
|
+
};
|
|
445
|
+
if (row.flagCount !== null && row.flagCount !== undefined) {
|
|
446
|
+
record.flagCount = row.flagCount;
|
|
447
|
+
}
|
|
448
|
+
if (row.allowedOrigins)
|
|
449
|
+
record.allowedOrigins = JSON.parse(row.allowedOrigins);
|
|
450
|
+
return record;
|
|
451
|
+
}
|
|
452
|
+
// ══════════════════════════════════════════════════════════
|
|
453
|
+
// ── Actions ──
|
|
454
|
+
// ══════════════════════════════════════════════════════════
|
|
455
|
+
async createAction(action) {
|
|
456
|
+
try {
|
|
457
|
+
this.db.prepare(`INSERT INTO actions (providerGaii, id, displayName, description, category, inputSchema, outputSchema, pricing, estimatedTimeSeconds, maxInputSizeBytes, tags, webhookUrl, createdAt, updatedAt, semantic)
|
|
458
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(action.providerGaii, action.id, action.displayName, action.description, action.category ?? null, JSON.stringify(action.inputSchema), JSON.stringify(action.outputSchema), JSON.stringify(action.pricing), action.estimatedTimeSeconds ?? null, action.maxInputSizeBytes ?? null, JSON.stringify(action.tags), action.webhookUrl ?? null, action.createdAt, action.updatedAt, action.semantic ? JSON.stringify(action.semantic) : null);
|
|
459
|
+
return action;
|
|
460
|
+
}
|
|
461
|
+
catch (err) {
|
|
462
|
+
if (err instanceof Error && err.message?.includes('UNIQUE constraint failed'))
|
|
463
|
+
throw new Error('ACTION_EXISTS', { cause: err });
|
|
464
|
+
throw err;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
async getAction(id, providerGaii) {
|
|
468
|
+
const row = this.db.prepare('SELECT * FROM actions WHERE providerGaii = ? AND id = ?').get(providerGaii, id);
|
|
469
|
+
return row ? this.deserializeAction(row) : null;
|
|
470
|
+
}
|
|
471
|
+
async listActions(opts) {
|
|
472
|
+
const rows = this.db.prepare('SELECT * FROM actions').all();
|
|
473
|
+
let results = rows.map(r => this.deserializeAction(r));
|
|
474
|
+
if (opts?.category) {
|
|
475
|
+
results = results.filter(a => a.category === opts.category);
|
|
476
|
+
}
|
|
477
|
+
if (opts?.search) {
|
|
478
|
+
const q = opts.search.toLowerCase();
|
|
479
|
+
results = results.filter(a => a.displayName.toLowerCase().includes(q) ||
|
|
480
|
+
a.description.toLowerCase().includes(q) ||
|
|
481
|
+
a.tags.some(t => t.toLowerCase().includes(q)));
|
|
482
|
+
}
|
|
483
|
+
return results;
|
|
484
|
+
}
|
|
485
|
+
async deleteAction(id, providerGaii) {
|
|
486
|
+
const result = this.db.prepare('DELETE FROM actions WHERE providerGaii = ? AND id = ?').run(providerGaii, id);
|
|
487
|
+
return result.changes > 0;
|
|
488
|
+
}
|
|
489
|
+
async deleteActionsByProvider(gaii) {
|
|
490
|
+
const result = this.db.prepare('DELETE FROM actions WHERE providerGaii = ?').run(gaii);
|
|
491
|
+
return result.changes;
|
|
492
|
+
}
|
|
493
|
+
async listActionsByProvider(gaii) {
|
|
494
|
+
const rows = this.db.prepare('SELECT * FROM actions WHERE providerGaii = ?').all(gaii);
|
|
495
|
+
return rows.map(r => this.deserializeAction(r));
|
|
496
|
+
}
|
|
497
|
+
async updateAction(id, providerGaii, updates) {
|
|
498
|
+
const existing = await this.getAction(id, providerGaii);
|
|
499
|
+
if (!existing)
|
|
500
|
+
return null;
|
|
501
|
+
const updated = { ...existing, ...updates };
|
|
502
|
+
this.db.prepare(`UPDATE actions SET displayName = ?, description = ?, category = ?, inputSchema = ?,
|
|
503
|
+
outputSchema = ?, pricing = ?, estimatedTimeSeconds = ?, maxInputSizeBytes = ?,
|
|
504
|
+
tags = ?, webhookUrl = ?, createdAt = ?, updatedAt = ?, semantic = ?
|
|
505
|
+
WHERE providerGaii = ? AND id = ?`).run(updated.displayName, updated.description, updated.category ?? null, JSON.stringify(updated.inputSchema), JSON.stringify(updated.outputSchema), JSON.stringify(updated.pricing), updated.estimatedTimeSeconds ?? null, updated.maxInputSizeBytes ?? null, JSON.stringify(updated.tags), updated.webhookUrl ?? null, updated.createdAt, updated.updatedAt, updated.semantic ? JSON.stringify(updated.semantic) : null, providerGaii, id);
|
|
506
|
+
return updated;
|
|
507
|
+
}
|
|
508
|
+
deserializeAction(row) {
|
|
509
|
+
const record = {
|
|
510
|
+
id: row.id,
|
|
511
|
+
providerGaii: row.providerGaii,
|
|
512
|
+
displayName: row.displayName,
|
|
513
|
+
description: row.description,
|
|
514
|
+
inputSchema: JSON.parse(row.inputSchema),
|
|
515
|
+
outputSchema: JSON.parse(row.outputSchema),
|
|
516
|
+
pricing: JSON.parse(row.pricing),
|
|
517
|
+
tags: JSON.parse(row.tags),
|
|
518
|
+
createdAt: row.createdAt,
|
|
519
|
+
updatedAt: row.updatedAt,
|
|
520
|
+
};
|
|
521
|
+
if (row.category)
|
|
522
|
+
record.category = row.category;
|
|
523
|
+
if (row.estimatedTimeSeconds !== null)
|
|
524
|
+
record.estimatedTimeSeconds = row.estimatedTimeSeconds;
|
|
525
|
+
if (row.maxInputSizeBytes !== null)
|
|
526
|
+
record.maxInputSizeBytes = row.maxInputSizeBytes;
|
|
527
|
+
if (row.webhookUrl)
|
|
528
|
+
record.webhookUrl = row.webhookUrl;
|
|
529
|
+
if (row.semantic)
|
|
530
|
+
record.semantic = JSON.parse(row.semantic);
|
|
531
|
+
return record;
|
|
532
|
+
}
|
|
533
|
+
// ══════════════════════════════════════════════════════════
|
|
534
|
+
// ── Work ──
|
|
535
|
+
// ══════════════════════════════════════════════════════════
|
|
536
|
+
async createWork(work) {
|
|
537
|
+
this.db.prepare(`INSERT INTO work (trackingCode, status, actionId, providerGaii, requesterGaii, input, output, cost, ttlExpiresAt, callbackUrl, rating, createdAt, updatedAt)
|
|
538
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(work.trackingCode, work.status, work.actionId, work.providerGaii, work.requesterGaii, JSON.stringify(work.input), work.output ? JSON.stringify(work.output) : null, JSON.stringify(work.cost), work.ttlExpiresAt, work.callbackUrl ?? null, work.rating ? JSON.stringify(work.rating) : null, work.createdAt, work.updatedAt);
|
|
539
|
+
return work;
|
|
540
|
+
}
|
|
541
|
+
async getWork(trackingCode) {
|
|
542
|
+
const row = this.db.prepare('SELECT * FROM work WHERE trackingCode = ?').get(trackingCode);
|
|
543
|
+
return row ? this.deserializeWork(row) : null;
|
|
544
|
+
}
|
|
545
|
+
async updateWork(trackingCode, updates) {
|
|
546
|
+
const existing = await this.getWork(trackingCode);
|
|
547
|
+
if (!existing)
|
|
548
|
+
return null;
|
|
549
|
+
const updated = { ...existing, ...updates };
|
|
550
|
+
this.db.prepare(`UPDATE work SET status = ?, actionId = ?, providerGaii = ?, requesterGaii = ?,
|
|
551
|
+
input = ?, output = ?, cost = ?, ttlExpiresAt = ?, callbackUrl = ?, rating = ?,
|
|
552
|
+
createdAt = ?, updatedAt = ? WHERE trackingCode = ?`).run(updated.status, updated.actionId, updated.providerGaii, updated.requesterGaii, JSON.stringify(updated.input), updated.output ? JSON.stringify(updated.output) : null, JSON.stringify(updated.cost), updated.ttlExpiresAt, updated.callbackUrl ?? null, updated.rating ? JSON.stringify(updated.rating) : null, updated.createdAt, updated.updatedAt, trackingCode);
|
|
553
|
+
return updated;
|
|
554
|
+
}
|
|
555
|
+
async listWorkByProvider(gaii) {
|
|
556
|
+
const rows = this.db.prepare('SELECT * FROM work WHERE providerGaii = ?').all(gaii);
|
|
557
|
+
return rows.map(r => this.deserializeWork(r));
|
|
558
|
+
}
|
|
559
|
+
async listWorkByRequester(gaii) {
|
|
560
|
+
const rows = this.db.prepare('SELECT * FROM work WHERE requesterGaii = ?').all(gaii);
|
|
561
|
+
return rows.map(r => this.deserializeWork(r));
|
|
562
|
+
}
|
|
563
|
+
async listAllWork(limit = 10000) {
|
|
564
|
+
const rows = this.db.prepare('SELECT * FROM work ORDER BY createdAt DESC LIMIT ?').all(Math.min(limit, 10000));
|
|
565
|
+
return rows.map(r => this.deserializeWork(r));
|
|
566
|
+
}
|
|
567
|
+
deserializeWork(row) {
|
|
568
|
+
const record = {
|
|
569
|
+
trackingCode: row.trackingCode,
|
|
570
|
+
status: row.status,
|
|
571
|
+
actionId: row.actionId,
|
|
572
|
+
providerGaii: row.providerGaii,
|
|
573
|
+
requesterGaii: row.requesterGaii,
|
|
574
|
+
input: JSON.parse(row.input),
|
|
575
|
+
cost: JSON.parse(row.cost),
|
|
576
|
+
ttlExpiresAt: row.ttlExpiresAt,
|
|
577
|
+
createdAt: row.createdAt,
|
|
578
|
+
updatedAt: row.updatedAt,
|
|
579
|
+
};
|
|
580
|
+
if (row.output)
|
|
581
|
+
record.output = JSON.parse(row.output);
|
|
582
|
+
if (row.callbackUrl)
|
|
583
|
+
record.callbackUrl = row.callbackUrl;
|
|
584
|
+
if (row.rating)
|
|
585
|
+
record.rating = JSON.parse(row.rating);
|
|
586
|
+
return record;
|
|
587
|
+
}
|
|
588
|
+
// ══════════════════════════════════════════════════════════
|
|
589
|
+
// ── Wallet Transactions ──
|
|
590
|
+
// ══════════════════════════════════════════════════════════
|
|
591
|
+
async addTransaction(tx) {
|
|
592
|
+
this.db.prepare(`INSERT INTO wallet_transactions (id, gaii, type, amount, counterpartyGaii, trackingCode, timestamp)
|
|
593
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`).run(tx.id, tx.gaii, tx.type, tx.amount, tx.counterpartyGaii ?? null, tx.trackingCode ?? null, tx.timestamp);
|
|
594
|
+
return tx;
|
|
595
|
+
}
|
|
596
|
+
async getTransactions(gaii, limit = 50) {
|
|
597
|
+
const rows = this.db.prepare('SELECT * FROM wallet_transactions WHERE gaii = ? ORDER BY timestamp DESC LIMIT ?').all(gaii, limit);
|
|
598
|
+
return rows.reverse().map(r => this.deserializeTransaction(r));
|
|
599
|
+
}
|
|
600
|
+
async listAllTransactions(limit = 10000) {
|
|
601
|
+
const rows = this.db.prepare('SELECT * FROM wallet_transactions ORDER BY timestamp DESC LIMIT ?').all(Math.min(limit, 10000));
|
|
602
|
+
return rows.map(r => this.deserializeTransaction(r));
|
|
603
|
+
}
|
|
604
|
+
async deleteTransactions(gaii) {
|
|
605
|
+
const result = this.db.prepare('DELETE FROM wallet_transactions WHERE gaii = ?').run(gaii);
|
|
606
|
+
return result.changes;
|
|
607
|
+
}
|
|
608
|
+
deserializeTransaction(row) {
|
|
609
|
+
const record = {
|
|
610
|
+
id: row.id,
|
|
611
|
+
gaii: row.gaii,
|
|
612
|
+
type: row.type,
|
|
613
|
+
amount: row.amount,
|
|
614
|
+
timestamp: row.timestamp,
|
|
615
|
+
};
|
|
616
|
+
if (row.counterpartyGaii)
|
|
617
|
+
record.counterpartyGaii = row.counterpartyGaii;
|
|
618
|
+
if (row.trackingCode)
|
|
619
|
+
record.trackingCode = row.trackingCode;
|
|
620
|
+
return record;
|
|
621
|
+
}
|
|
622
|
+
// ══════════════════════════════════════════════════════════
|
|
623
|
+
// ── Boards ──
|
|
624
|
+
// ══════════════════════════════════════════════════════════
|
|
625
|
+
async createBoard(board) {
|
|
626
|
+
this.db.prepare(`INSERT INTO boards (id, name, description, visibility, ownerGaii, allowedGaiis, createdAt, semantic)
|
|
627
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`).run(board.id, board.name, board.description ?? null, board.visibility, board.ownerGaii, JSON.stringify(board.allowedGaiis), board.createdAt, board.semantic ? JSON.stringify(board.semantic) : null);
|
|
628
|
+
return board;
|
|
629
|
+
}
|
|
630
|
+
async getBoard(id) {
|
|
631
|
+
const row = this.db.prepare('SELECT * FROM boards WHERE id = ?').get(id);
|
|
632
|
+
return row ? this.deserializeBoard(row) : null;
|
|
633
|
+
}
|
|
634
|
+
async listBoards(opts) {
|
|
635
|
+
let sql = 'SELECT * FROM boards WHERE 1=1';
|
|
636
|
+
const params = [];
|
|
637
|
+
if (opts?.visibility) {
|
|
638
|
+
sql += ' AND visibility = ?';
|
|
639
|
+
params.push(opts.visibility);
|
|
640
|
+
}
|
|
641
|
+
if (opts?.ownerGaii) {
|
|
642
|
+
sql += ' AND ownerGaii = ?';
|
|
643
|
+
params.push(opts.ownerGaii);
|
|
644
|
+
}
|
|
645
|
+
const rows = this.db.prepare(sql).all(...params);
|
|
646
|
+
return rows.map(r => this.deserializeBoard(r));
|
|
647
|
+
}
|
|
648
|
+
async updateBoardVisibility(id, visibility) {
|
|
649
|
+
const result = this.db.prepare('UPDATE boards SET visibility = ? WHERE id = ?').run(visibility, id);
|
|
650
|
+
if (result.changes === 0)
|
|
651
|
+
return null;
|
|
652
|
+
return this.getBoard(id);
|
|
653
|
+
}
|
|
654
|
+
async updateBoardMembers(id, allowedGaiis) {
|
|
655
|
+
const result = this.db.prepare('UPDATE boards SET allowedGaiis = ? WHERE id = ?').run(JSON.stringify(allowedGaiis), id);
|
|
656
|
+
if (result.changes === 0)
|
|
657
|
+
return null;
|
|
658
|
+
return this.getBoard(id);
|
|
659
|
+
}
|
|
660
|
+
async deleteBoard(id) {
|
|
661
|
+
// Delete all posts in the board
|
|
662
|
+
this.db.prepare('DELETE FROM board_posts WHERE boardId = ?').run(id);
|
|
663
|
+
const result = this.db.prepare('DELETE FROM boards WHERE id = ?').run(id);
|
|
664
|
+
return result.changes > 0;
|
|
665
|
+
}
|
|
666
|
+
async createPost(post) {
|
|
667
|
+
this.db.prepare(`INSERT INTO board_posts (boardId, id, authorGaii, title, body, category, tags, ttlExpiresAt, reactions, replyTo, createdAt, semantic)
|
|
668
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(post.boardId, post.id, post.authorGaii, post.title, post.body, post.category ?? null, JSON.stringify(post.tags), post.ttlExpiresAt ?? null, JSON.stringify(post.reactions), post.replyTo ?? null, post.createdAt, post.semantic ? JSON.stringify(post.semantic) : null);
|
|
669
|
+
return post;
|
|
670
|
+
}
|
|
671
|
+
async getPost(boardId, postId) {
|
|
672
|
+
const row = this.db.prepare('SELECT * FROM board_posts WHERE boardId = ? AND id = ?').get(boardId, postId);
|
|
673
|
+
return row ? this.deserializePost(row) : null;
|
|
674
|
+
}
|
|
675
|
+
async listPosts(boardId, opts) {
|
|
676
|
+
const limit = opts?.limit ?? 20;
|
|
677
|
+
const now = Date.now();
|
|
678
|
+
let sql = 'SELECT * FROM board_posts WHERE boardId = ? AND replyTo IS NULL';
|
|
679
|
+
const params = [boardId];
|
|
680
|
+
if (opts?.category) {
|
|
681
|
+
sql += ' AND category = ?';
|
|
682
|
+
params.push(opts.category);
|
|
683
|
+
}
|
|
684
|
+
sql += ' ORDER BY createdAt DESC';
|
|
685
|
+
const rows = this.db.prepare(sql).all(...params);
|
|
686
|
+
let results = [];
|
|
687
|
+
for (const row of rows) {
|
|
688
|
+
const post = this.deserializePost(row);
|
|
689
|
+
if (post.ttlExpiresAt && new Date(post.ttlExpiresAt).getTime() < now) {
|
|
690
|
+
this.db.prepare('DELETE FROM board_posts WHERE boardId = ? AND id = ?').run(post.boardId, post.id);
|
|
691
|
+
continue;
|
|
692
|
+
}
|
|
693
|
+
results.push(post);
|
|
694
|
+
}
|
|
695
|
+
if (opts?.cursor) {
|
|
696
|
+
const idx = results.findIndex(p => p.id === opts.cursor);
|
|
697
|
+
if (idx >= 0)
|
|
698
|
+
results = results.slice(idx + 1);
|
|
699
|
+
}
|
|
700
|
+
return results.slice(0, limit);
|
|
701
|
+
}
|
|
702
|
+
async deletePost(boardId, postId) {
|
|
703
|
+
const result = this.db.prepare('DELETE FROM board_posts WHERE boardId = ? AND id = ?').run(boardId, postId);
|
|
704
|
+
return result.changes > 0;
|
|
705
|
+
}
|
|
706
|
+
async addReaction(boardId, postId, emoji, gaii) {
|
|
707
|
+
const row = this.db.prepare('SELECT reactions FROM board_posts WHERE boardId = ? AND id = ?').get(boardId, postId);
|
|
708
|
+
if (!row)
|
|
709
|
+
return false;
|
|
710
|
+
const reactions = JSON.parse(row.reactions);
|
|
711
|
+
if (!reactions[emoji])
|
|
712
|
+
reactions[emoji] = [];
|
|
713
|
+
if (!reactions[emoji].includes(gaii))
|
|
714
|
+
reactions[emoji].push(gaii);
|
|
715
|
+
this.db.prepare('UPDATE board_posts SET reactions = ? WHERE boardId = ? AND id = ?').run(JSON.stringify(reactions), boardId, postId);
|
|
716
|
+
return true;
|
|
717
|
+
}
|
|
718
|
+
deserializeBoard(row) {
|
|
719
|
+
const record = {
|
|
720
|
+
id: row.id,
|
|
721
|
+
name: row.name,
|
|
722
|
+
visibility: row.visibility,
|
|
723
|
+
ownerGaii: row.ownerGaii,
|
|
724
|
+
allowedGaiis: JSON.parse(row.allowedGaiis),
|
|
725
|
+
createdAt: row.createdAt,
|
|
726
|
+
};
|
|
727
|
+
if (row.description)
|
|
728
|
+
record.description = row.description;
|
|
729
|
+
if (row.semantic)
|
|
730
|
+
record.semantic = JSON.parse(row.semantic);
|
|
731
|
+
return record;
|
|
732
|
+
}
|
|
733
|
+
deserializePost(row) {
|
|
734
|
+
const record = {
|
|
735
|
+
id: row.id,
|
|
736
|
+
boardId: row.boardId,
|
|
737
|
+
authorGaii: row.authorGaii,
|
|
738
|
+
title: row.title,
|
|
739
|
+
body: row.body,
|
|
740
|
+
tags: JSON.parse(row.tags),
|
|
741
|
+
reactions: JSON.parse(row.reactions),
|
|
742
|
+
createdAt: row.createdAt,
|
|
743
|
+
};
|
|
744
|
+
if (row.category)
|
|
745
|
+
record.category = row.category;
|
|
746
|
+
if (row.ttlExpiresAt)
|
|
747
|
+
record.ttlExpiresAt = row.ttlExpiresAt;
|
|
748
|
+
if (row.replyTo)
|
|
749
|
+
record.replyTo = row.replyTo;
|
|
750
|
+
if (row.semantic)
|
|
751
|
+
record.semantic = JSON.parse(row.semantic);
|
|
752
|
+
return record;
|
|
753
|
+
}
|
|
754
|
+
// ══════════════════════════════════════════════════════════
|
|
755
|
+
// ── Board Subscriptions ──
|
|
756
|
+
// ══════════════════════════════════════════════════════════
|
|
757
|
+
async createBoardSubscription(sub) {
|
|
758
|
+
this.db.prepare(`INSERT OR REPLACE INTO board_subscriptions (id, boardId, gaii, callbackUrl, filters, createdAt)
|
|
759
|
+
VALUES (?, ?, ?, ?, ?, ?)`).run(sub.id, sub.boardId, sub.gaii, sub.callbackUrl ?? null, sub.filters ? JSON.stringify(sub.filters) : null, sub.createdAt);
|
|
760
|
+
return sub;
|
|
761
|
+
}
|
|
762
|
+
async getBoardSubscription(boardId, gaii) {
|
|
763
|
+
const row = this.db.prepare('SELECT * FROM board_subscriptions WHERE boardId = ? AND gaii = ?').get(boardId, gaii);
|
|
764
|
+
return row ? this.deserializeBoardSubscription(row) : null;
|
|
765
|
+
}
|
|
766
|
+
async listBoardSubscriptions(boardId) {
|
|
767
|
+
const rows = this.db.prepare('SELECT * FROM board_subscriptions WHERE boardId = ?').all(boardId);
|
|
768
|
+
return rows.map(r => this.deserializeBoardSubscription(r));
|
|
769
|
+
}
|
|
770
|
+
async listSubscriptionsByAgent(gaii) {
|
|
771
|
+
const rows = this.db.prepare('SELECT * FROM board_subscriptions WHERE gaii = ?').all(gaii);
|
|
772
|
+
return rows.map(r => this.deserializeBoardSubscription(r));
|
|
773
|
+
}
|
|
774
|
+
async deleteBoardSubscription(boardId, gaii) {
|
|
775
|
+
const result = this.db.prepare('DELETE FROM board_subscriptions WHERE boardId = ? AND gaii = ?').run(boardId, gaii);
|
|
776
|
+
return result.changes > 0;
|
|
777
|
+
}
|
|
778
|
+
deserializeBoardSubscription(row) {
|
|
779
|
+
const record = {
|
|
780
|
+
id: row.id,
|
|
781
|
+
boardId: row.boardId,
|
|
782
|
+
gaii: row.gaii,
|
|
783
|
+
createdAt: row.createdAt,
|
|
784
|
+
};
|
|
785
|
+
if (row.callbackUrl)
|
|
786
|
+
record.callbackUrl = row.callbackUrl;
|
|
787
|
+
if (row.filters)
|
|
788
|
+
record.filters = JSON.parse(row.filters);
|
|
789
|
+
return record;
|
|
790
|
+
}
|
|
791
|
+
// ══════════════════════════════════════════════════════════
|
|
792
|
+
// ── OTK (One-Time Keys) ──
|
|
793
|
+
// ══════════════════════════════════════════════════════════
|
|
794
|
+
async createOtk(otk) {
|
|
795
|
+
this.db.prepare(`INSERT INTO otks (key, ownerGaii, action, params, expiresAt, initial, used, usedAt, sessionId, createdAt)
|
|
796
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(otk.key, otk.ownerGaii, otk.action, JSON.stringify(otk.params), otk.expiresAt, otk.initial ? 1 : 0, otk.used ? 1 : 0, otk.usedAt, otk.sessionId, otk.createdAt);
|
|
797
|
+
return otk;
|
|
798
|
+
}
|
|
799
|
+
async getOtk(key) {
|
|
800
|
+
const row = this.db.prepare('SELECT * FROM otks WHERE key = ?').get(key);
|
|
801
|
+
return row ? this.deserializeOtk(row) : null;
|
|
802
|
+
}
|
|
803
|
+
async consumeOtk(key, graceMs = 60_000) {
|
|
804
|
+
const otk = await this.getOtk(key);
|
|
805
|
+
if (!otk)
|
|
806
|
+
return null;
|
|
807
|
+
// Initial OTK: timer hasn't started yet -- activate on first use
|
|
808
|
+
if (otk.initial && !otk.used) {
|
|
809
|
+
otk.used = true;
|
|
810
|
+
otk.usedAt = new Date().toISOString();
|
|
811
|
+
otk.expiresAt = new Date(Date.now() + graceMs).toISOString();
|
|
812
|
+
this.db.prepare('UPDATE otks SET used = 1, usedAt = ?, expiresAt = ? WHERE key = ?').run(otk.usedAt, otk.expiresAt, key);
|
|
813
|
+
return otk;
|
|
814
|
+
}
|
|
815
|
+
if (new Date(otk.expiresAt) < new Date()) {
|
|
816
|
+
this.db.prepare('DELETE FROM otks WHERE key = ?').run(key);
|
|
817
|
+
return null;
|
|
818
|
+
}
|
|
819
|
+
// Configurable post-use window: allow re-use within graceMs of first use
|
|
820
|
+
if (otk.used && otk.usedAt) {
|
|
821
|
+
const usedAt = new Date(otk.usedAt).getTime();
|
|
822
|
+
if (Date.now() - usedAt > graceMs) {
|
|
823
|
+
this.db.prepare('DELETE FROM otks WHERE key = ?').run(key);
|
|
824
|
+
return null;
|
|
825
|
+
}
|
|
826
|
+
return otk; // still within grace window
|
|
827
|
+
}
|
|
828
|
+
otk.used = true;
|
|
829
|
+
otk.usedAt = new Date().toISOString();
|
|
830
|
+
this.db.prepare('UPDATE otks SET used = 1, usedAt = ? WHERE key = ?').run(otk.usedAt, key);
|
|
831
|
+
return otk;
|
|
832
|
+
}
|
|
833
|
+
async listOtksBySession(sessionId) {
|
|
834
|
+
const rows = this.db.prepare('SELECT * FROM otks WHERE sessionId = ?').all(sessionId);
|
|
835
|
+
return rows.map(r => this.deserializeOtk(r));
|
|
836
|
+
}
|
|
837
|
+
async expireSessionOtks(sessionId) {
|
|
838
|
+
const result = this.db.prepare('DELETE FROM otks WHERE sessionId = ?').run(sessionId);
|
|
839
|
+
return result.changes;
|
|
840
|
+
}
|
|
841
|
+
deserializeOtk(row) {
|
|
842
|
+
return {
|
|
843
|
+
key: row.key,
|
|
844
|
+
ownerGaii: row.ownerGaii,
|
|
845
|
+
action: row.action,
|
|
846
|
+
params: JSON.parse(row.params),
|
|
847
|
+
expiresAt: row.expiresAt,
|
|
848
|
+
initial: row.initial === 1,
|
|
849
|
+
used: row.used === 1,
|
|
850
|
+
usedAt: row.usedAt ?? null,
|
|
851
|
+
sessionId: row.sessionId ?? null,
|
|
852
|
+
createdAt: row.createdAt,
|
|
853
|
+
};
|
|
854
|
+
}
|
|
855
|
+
// ══════════════════════════════════════════════════════════
|
|
856
|
+
// ── Node Key ──
|
|
857
|
+
// ══════════════════════════════════════════════════════════
|
|
858
|
+
async setNodeKey(publicKey, privateKey) {
|
|
859
|
+
this.db.prepare(`INSERT OR REPLACE INTO node_key (id, publicKey, privateKey) VALUES (1, ?, ?)`).run(publicKey, privateKey);
|
|
860
|
+
}
|
|
861
|
+
async getNodeKey() {
|
|
862
|
+
const row = this.db.prepare('SELECT * FROM node_key WHERE id = 1').get();
|
|
863
|
+
if (!row)
|
|
864
|
+
return null;
|
|
865
|
+
return { publicKey: row.publicKey, privateKey: row.privateKey };
|
|
866
|
+
}
|
|
867
|
+
// ══════════════════════════════════════════════════════════
|
|
868
|
+
// ── Disputes ──
|
|
869
|
+
// ══════════════════════════════════════════════════════════
|
|
870
|
+
async createDispute(dispute) {
|
|
871
|
+
this.db.prepare(`INSERT INTO disputes (id, trackingCode, status, openedBy, reason, ruling, createdAt, updatedAt)
|
|
872
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`).run(dispute.id, dispute.trackingCode, dispute.status, dispute.openedBy, dispute.reason, dispute.ruling ? JSON.stringify(dispute.ruling) : null, dispute.createdAt, dispute.updatedAt);
|
|
873
|
+
return dispute;
|
|
874
|
+
}
|
|
875
|
+
async getDispute(id) {
|
|
876
|
+
const row = this.db.prepare('SELECT * FROM disputes WHERE id = ?').get(id);
|
|
877
|
+
return row ? this.deserializeDispute(row) : null;
|
|
878
|
+
}
|
|
879
|
+
async getDisputeByTrackingCode(tc) {
|
|
880
|
+
const row = this.db.prepare('SELECT * FROM disputes WHERE trackingCode = ?').get(tc);
|
|
881
|
+
return row ? this.deserializeDispute(row) : null;
|
|
882
|
+
}
|
|
883
|
+
async updateDispute(id, updates) {
|
|
884
|
+
const existing = await this.getDispute(id);
|
|
885
|
+
if (!existing)
|
|
886
|
+
return null;
|
|
887
|
+
const updated = { ...existing, ...updates };
|
|
888
|
+
this.db.prepare(`UPDATE disputes SET trackingCode = ?, status = ?, openedBy = ?, reason = ?, ruling = ?,
|
|
889
|
+
createdAt = ?, updatedAt = ? WHERE id = ?`).run(updated.trackingCode, updated.status, updated.openedBy, updated.reason, updated.ruling ? JSON.stringify(updated.ruling) : null, updated.createdAt, updated.updatedAt, id);
|
|
890
|
+
return updated;
|
|
891
|
+
}
|
|
892
|
+
async addDisputeAuditEntry(disputeId, entry) {
|
|
893
|
+
this.db.prepare(`INSERT INTO dispute_audit (disputeId, sequence, event, actor, timestamp, data, hash, previousHash)
|
|
894
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`).run(disputeId, entry.sequence, entry.event, entry.actor, entry.timestamp, JSON.stringify(entry.data), entry.hash, entry.previousHash);
|
|
895
|
+
return entry;
|
|
896
|
+
}
|
|
897
|
+
async getDisputeAuditLog(disputeId) {
|
|
898
|
+
const rows = this.db.prepare('SELECT * FROM dispute_audit WHERE disputeId = ? ORDER BY sequence ASC').all(disputeId);
|
|
899
|
+
return rows.map(r => ({
|
|
900
|
+
sequence: r.sequence,
|
|
901
|
+
event: r.event,
|
|
902
|
+
actor: r.actor,
|
|
903
|
+
timestamp: r.timestamp,
|
|
904
|
+
data: JSON.parse(r.data),
|
|
905
|
+
hash: r.hash,
|
|
906
|
+
previousHash: r.previousHash,
|
|
907
|
+
}));
|
|
908
|
+
}
|
|
909
|
+
async listDisputesByProvider(gaii) {
|
|
910
|
+
// Need to join with work to find by provider
|
|
911
|
+
const rows = this.db.prepare(`SELECT d.* FROM disputes d
|
|
912
|
+
INNER JOIN work w ON d.trackingCode = w.trackingCode
|
|
913
|
+
WHERE w.providerGaii = ?`).all(gaii);
|
|
914
|
+
return rows.map(r => this.deserializeDispute(r));
|
|
915
|
+
}
|
|
916
|
+
async listAllDisputes(limit = 10000) {
|
|
917
|
+
const rows = this.db.prepare('SELECT * FROM disputes ORDER BY createdAt DESC LIMIT ?').all(Math.min(limit, 10000));
|
|
918
|
+
return rows.map(r => this.deserializeDispute(r));
|
|
919
|
+
}
|
|
920
|
+
deserializeDispute(row) {
|
|
921
|
+
const record = {
|
|
922
|
+
id: row.id,
|
|
923
|
+
trackingCode: row.trackingCode,
|
|
924
|
+
status: row.status,
|
|
925
|
+
openedBy: row.openedBy,
|
|
926
|
+
reason: row.reason,
|
|
927
|
+
createdAt: row.createdAt,
|
|
928
|
+
updatedAt: row.updatedAt,
|
|
929
|
+
};
|
|
930
|
+
if (row.ruling)
|
|
931
|
+
record.ruling = JSON.parse(row.ruling);
|
|
932
|
+
return record;
|
|
933
|
+
}
|
|
934
|
+
// ══════════════════════════════════════════════════════════
|
|
935
|
+
// ── Micro-Memory ──
|
|
936
|
+
// ══════════════════════════════════════════════════════════
|
|
937
|
+
async setMicroMemory(record) {
|
|
938
|
+
this.db.prepare(`INSERT OR REPLACE INTO micro_memory (gaii, setName, entries, visibility, accessCode, updatedAt)
|
|
939
|
+
VALUES (?, ?, ?, ?, ?, ?)`).run(record.gaii, record.set, JSON.stringify(record.entries), record.visibility, record.accessCode ?? null, record.updatedAt);
|
|
940
|
+
return record;
|
|
941
|
+
}
|
|
942
|
+
async getMicroMemory(gaii, set) {
|
|
943
|
+
const row = this.db.prepare('SELECT * FROM micro_memory WHERE gaii = ? AND setName = ?').get(gaii, set);
|
|
944
|
+
return row ? this.deserializeMicroMemory(row) : null;
|
|
945
|
+
}
|
|
946
|
+
async listMicroMemorySets(gaii) {
|
|
947
|
+
const rows = this.db.prepare('SELECT * FROM micro_memory WHERE gaii = ?').all(gaii);
|
|
948
|
+
return rows.map(r => this.deserializeMicroMemory(r));
|
|
949
|
+
}
|
|
950
|
+
async deleteMicroMemory(gaii, set) {
|
|
951
|
+
const result = this.db.prepare('DELETE FROM micro_memory WHERE gaii = ? AND setName = ?').run(gaii, set);
|
|
952
|
+
return result.changes > 0;
|
|
953
|
+
}
|
|
954
|
+
async deleteMicroMemoryEntry(gaii, set, key) {
|
|
955
|
+
const record = await this.getMicroMemory(gaii, set);
|
|
956
|
+
if (!record || !(key in record.entries))
|
|
957
|
+
return false;
|
|
958
|
+
delete record.entries[key];
|
|
959
|
+
this.db.prepare('UPDATE micro_memory SET entries = ? WHERE gaii = ? AND setName = ?').run(JSON.stringify(record.entries), gaii, set);
|
|
960
|
+
return true;
|
|
961
|
+
}
|
|
962
|
+
async findMicroMemoryByAccessCode(set, accessCode) {
|
|
963
|
+
const row = this.db.prepare(`SELECT * FROM micro_memory WHERE setName = ? AND accessCode = ? AND (visibility = 'shared_read' OR visibility = 'shared_write')`).get(set, accessCode);
|
|
964
|
+
return row ? this.deserializeMicroMemory(row) : null;
|
|
965
|
+
}
|
|
966
|
+
deserializeMicroMemory(row) {
|
|
967
|
+
const record = {
|
|
968
|
+
gaii: row.gaii,
|
|
969
|
+
set: row.setName,
|
|
970
|
+
entries: JSON.parse(row.entries),
|
|
971
|
+
visibility: row.visibility,
|
|
972
|
+
updatedAt: row.updatedAt,
|
|
973
|
+
};
|
|
974
|
+
if (row.accessCode)
|
|
975
|
+
record.accessCode = row.accessCode;
|
|
976
|
+
return record;
|
|
977
|
+
}
|
|
978
|
+
// ══════════════════════════════════════════════════════════
|
|
979
|
+
// ── Storage (Binary Files) ──
|
|
980
|
+
// ══════════════════════════════════════════════════════════
|
|
981
|
+
async createStorageFile(file) {
|
|
982
|
+
this.db.prepare(`INSERT OR REPLACE INTO storage_files (ownerGaii, key, visibility, mimeType, size, data, accessCode, tags, createdAt)
|
|
983
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(file.ownerGaii, file.key, file.visibility, file.mimeType, file.size, file.data, file.accessCode ?? null, JSON.stringify(file.tags || []), file.createdAt);
|
|
984
|
+
return file;
|
|
985
|
+
}
|
|
986
|
+
async getStorageFile(ownerGaii, key) {
|
|
987
|
+
const row = this.db.prepare('SELECT * FROM storage_files WHERE ownerGaii = ? AND key = ?').get(ownerGaii, key);
|
|
988
|
+
if (!row)
|
|
989
|
+
return null;
|
|
990
|
+
const record = {
|
|
991
|
+
key: row.key,
|
|
992
|
+
ownerGaii: row.ownerGaii,
|
|
993
|
+
visibility: row.visibility,
|
|
994
|
+
mimeType: row.mimeType,
|
|
995
|
+
size: row.size,
|
|
996
|
+
data: row.data,
|
|
997
|
+
tags: row.tags ? JSON.parse(row.tags) : [],
|
|
998
|
+
createdAt: row.createdAt,
|
|
999
|
+
};
|
|
1000
|
+
if (row.accessCode)
|
|
1001
|
+
record.accessCode = row.accessCode;
|
|
1002
|
+
return record;
|
|
1003
|
+
}
|
|
1004
|
+
async listStorageFiles(ownerGaii) {
|
|
1005
|
+
const rows = this.db.prepare('SELECT * FROM storage_files WHERE ownerGaii = ?').all(ownerGaii);
|
|
1006
|
+
return rows.map(r => {
|
|
1007
|
+
const record = {
|
|
1008
|
+
key: r.key,
|
|
1009
|
+
ownerGaii: r.ownerGaii,
|
|
1010
|
+
visibility: r.visibility,
|
|
1011
|
+
mimeType: r.mimeType,
|
|
1012
|
+
size: r.size,
|
|
1013
|
+
data: r.data,
|
|
1014
|
+
tags: r.tags ? JSON.parse(r.tags) : [],
|
|
1015
|
+
createdAt: r.createdAt,
|
|
1016
|
+
};
|
|
1017
|
+
if (r.accessCode)
|
|
1018
|
+
record.accessCode = r.accessCode;
|
|
1019
|
+
return record;
|
|
1020
|
+
});
|
|
1021
|
+
}
|
|
1022
|
+
async deleteStorageFile(ownerGaii, key) {
|
|
1023
|
+
const result = this.db.prepare('DELETE FROM storage_files WHERE ownerGaii = ? AND key = ?').run(ownerGaii, key);
|
|
1024
|
+
return result.changes > 0;
|
|
1025
|
+
}
|
|
1026
|
+
async updateFileTagsByKey(ownerGaii, key, tags) {
|
|
1027
|
+
const result = this.db.prepare('UPDATE storage_files SET tags = ? WHERE ownerGaii = ? AND key = ?').run(JSON.stringify(tags), ownerGaii, key);
|
|
1028
|
+
if (result.changes === 0)
|
|
1029
|
+
return null;
|
|
1030
|
+
return this.getStorageFile(ownerGaii, key);
|
|
1031
|
+
}
|
|
1032
|
+
async updateFileVisibility(ownerGaii, key, visibility) {
|
|
1033
|
+
const result = this.db.prepare('UPDATE storage_files SET visibility = ? WHERE ownerGaii = ? AND key = ?').run(visibility, ownerGaii, key);
|
|
1034
|
+
if (result.changes === 0)
|
|
1035
|
+
return null;
|
|
1036
|
+
return this.getStorageFile(ownerGaii, key);
|
|
1037
|
+
}
|
|
1038
|
+
// ══════════════════════════════════════════════════════════
|
|
1039
|
+
// ── Peering Requests ──
|
|
1040
|
+
// ══════════════════════════════════════════════════════════
|
|
1041
|
+
async createPeeringRequest(req) {
|
|
1042
|
+
this.db.prepare(`INSERT INTO peering_requests (id, fromNodeUrl, fromNodeId, toNodeId, targetUrl, publicKey, message, status, createdAt, updatedAt)
|
|
1043
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(req.id, req.fromNodeUrl, req.fromNodeId ?? null, req.toNodeId ?? null, req.targetUrl ?? null, req.publicKey ?? null, req.message ?? null, req.status, req.createdAt, req.updatedAt);
|
|
1044
|
+
return req;
|
|
1045
|
+
}
|
|
1046
|
+
async getPeeringRequest(id) {
|
|
1047
|
+
const row = this.db.prepare('SELECT * FROM peering_requests WHERE id = ?').get(id);
|
|
1048
|
+
return row ? this.deserializePeeringRequest(row) : null;
|
|
1049
|
+
}
|
|
1050
|
+
async listPeeringRequests(status) {
|
|
1051
|
+
let sql = 'SELECT * FROM peering_requests';
|
|
1052
|
+
const params = [];
|
|
1053
|
+
if (status) {
|
|
1054
|
+
sql += ' WHERE status = ?';
|
|
1055
|
+
params.push(status);
|
|
1056
|
+
}
|
|
1057
|
+
const rows = this.db.prepare(sql).all(...params);
|
|
1058
|
+
return rows.map(r => this.deserializePeeringRequest(r));
|
|
1059
|
+
}
|
|
1060
|
+
async updatePeeringRequest(id, updates) {
|
|
1061
|
+
const existing = await this.getPeeringRequest(id);
|
|
1062
|
+
if (!existing)
|
|
1063
|
+
return null;
|
|
1064
|
+
const updated = { ...existing, ...updates };
|
|
1065
|
+
this.db.prepare(`UPDATE peering_requests SET fromNodeUrl = ?, fromNodeId = ?, toNodeId = ?, targetUrl = ?,
|
|
1066
|
+
publicKey = ?, message = ?, status = ?, createdAt = ?, updatedAt = ? WHERE id = ?`).run(updated.fromNodeUrl, updated.fromNodeId ?? null, updated.toNodeId ?? null, updated.targetUrl ?? null, updated.publicKey ?? null, updated.message ?? null, updated.status, updated.createdAt, updated.updatedAt, id);
|
|
1067
|
+
return updated;
|
|
1068
|
+
}
|
|
1069
|
+
deserializePeeringRequest(row) {
|
|
1070
|
+
const record = {
|
|
1071
|
+
id: row.id,
|
|
1072
|
+
fromNodeUrl: row.fromNodeUrl,
|
|
1073
|
+
status: row.status,
|
|
1074
|
+
createdAt: row.createdAt,
|
|
1075
|
+
updatedAt: row.updatedAt,
|
|
1076
|
+
};
|
|
1077
|
+
if (row.fromNodeId)
|
|
1078
|
+
record.fromNodeId = row.fromNodeId;
|
|
1079
|
+
if (row.toNodeId)
|
|
1080
|
+
record.toNodeId = row.toNodeId;
|
|
1081
|
+
if (row.targetUrl)
|
|
1082
|
+
record.targetUrl = row.targetUrl;
|
|
1083
|
+
if (row.publicKey)
|
|
1084
|
+
record.publicKey = row.publicKey;
|
|
1085
|
+
if (row.message)
|
|
1086
|
+
record.message = row.message;
|
|
1087
|
+
return record;
|
|
1088
|
+
}
|
|
1089
|
+
// ══════════════════════════════════════════════════════════
|
|
1090
|
+
// ── Chunked Uploads (in-memory, same as MongoDB adapter) ──
|
|
1091
|
+
// ══════════════════════════════════════════════════════════
|
|
1092
|
+
async createChunkedUpload(record) {
|
|
1093
|
+
this.chunkedUploads.set(record.uploadId, record);
|
|
1094
|
+
return record;
|
|
1095
|
+
}
|
|
1096
|
+
async getChunkedUpload(uploadId) {
|
|
1097
|
+
const record = this.chunkedUploads.get(uploadId) ?? null;
|
|
1098
|
+
if (record && new Date(record.expiresAt).getTime() < Date.now()) {
|
|
1099
|
+
this.chunkedUploads.delete(uploadId);
|
|
1100
|
+
return null;
|
|
1101
|
+
}
|
|
1102
|
+
return record;
|
|
1103
|
+
}
|
|
1104
|
+
async addChunk(uploadId, chunkIndex, data) {
|
|
1105
|
+
const record = this.chunkedUploads.get(uploadId);
|
|
1106
|
+
if (!record)
|
|
1107
|
+
return false;
|
|
1108
|
+
if (new Date(record.expiresAt).getTime() < Date.now()) {
|
|
1109
|
+
this.chunkedUploads.delete(uploadId);
|
|
1110
|
+
return false;
|
|
1111
|
+
}
|
|
1112
|
+
record.receivedChunks.set(chunkIndex, data);
|
|
1113
|
+
return true;
|
|
1114
|
+
}
|
|
1115
|
+
async deleteChunkedUpload(uploadId) {
|
|
1116
|
+
return this.chunkedUploads.delete(uploadId);
|
|
1117
|
+
}
|
|
1118
|
+
// ══════════════════════════════════════════════════════════
|
|
1119
|
+
// ── GHII (Global Human Identity Identifier) ──
|
|
1120
|
+
// ══════════════════════════════════════════════════════════
|
|
1121
|
+
async createGHII(record) {
|
|
1122
|
+
try {
|
|
1123
|
+
this.db.prepare(`INSERT INTO ghiis (ghii, username, nodeId, displayName, bio, avatar, locale, passwordHash,
|
|
1124
|
+
verificationLevel, ownerName, createdAt, updatedAt, totpSecret, totpEnabled, totpBackupCodes,
|
|
1125
|
+
totpLastUsedAt, totpLastUsedCode, totpFailedAttempts, totpLockedUntil, semantic, emailHash,
|
|
1126
|
+
emailVerifiedAt, verificationMethod, magicLinkEnabled, notificationEmail, lastLoginAt,
|
|
1127
|
+
loginCount, verifiedAttributes, verificationIssuer, verificationCredentialHash, ftnVerified,
|
|
1128
|
+
trustScore, morselBalance, allowedOrigins)
|
|
1129
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(record.ghii, record.username, record.nodeId, record.displayName, record.bio ?? null, record.avatar ?? null, record.locale ?? null, record.passwordHash ?? null, record.verificationLevel, record.ownerName, record.createdAt, record.updatedAt, record.totpSecret ?? null, record.totpEnabled ? 1 : 0, record.totpBackupCodes ? JSON.stringify(record.totpBackupCodes) : null, record.totpLastUsedAt ?? null, record.totpLastUsedCode ?? null, record.totpFailedAttempts ?? 0, record.totpLockedUntil ?? null, record.semantic ? JSON.stringify(record.semantic) : null, record.emailHash ?? null, record.emailVerifiedAt ?? null, record.verificationMethod ?? null, record.magicLinkEnabled ? 1 : 0, record.notificationEmail ?? null, record.lastLoginAt ?? null, record.loginCount ?? 0, record.verifiedAttributes ? JSON.stringify(record.verifiedAttributes) : null, record.verificationIssuer ?? null, record.verificationCredentialHash ?? null, record.ftnVerified ? 1 : 0, record.trustScore ?? null, record.morselBalance ?? null, record.allowedOrigins ? JSON.stringify(record.allowedOrigins) : null);
|
|
1130
|
+
return record;
|
|
1131
|
+
}
|
|
1132
|
+
catch (err) {
|
|
1133
|
+
if (err instanceof Error && err.message?.includes('UNIQUE constraint failed'))
|
|
1134
|
+
throw new Error('GHII_TAKEN', { cause: err });
|
|
1135
|
+
throw err;
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
async getGHII(ghii) {
|
|
1139
|
+
const row = this.db.prepare('SELECT * FROM ghiis WHERE ghii = ?').get(ghii);
|
|
1140
|
+
return row ? this.deserializeGHII(row) : null;
|
|
1141
|
+
}
|
|
1142
|
+
async getGHIIByOwner(ownerName) {
|
|
1143
|
+
const row = this.db.prepare('SELECT * FROM ghiis WHERE ownerName = ?').get(ownerName);
|
|
1144
|
+
return row ? this.deserializeGHII(row) : null;
|
|
1145
|
+
}
|
|
1146
|
+
async getGHIIByEmailHash(emailHash) {
|
|
1147
|
+
const row = this.db.prepare('SELECT * FROM ghiis WHERE emailHash = ?').get(emailHash);
|
|
1148
|
+
return row ? this.deserializeGHII(row) : null;
|
|
1149
|
+
}
|
|
1150
|
+
async updateGHII(ghii, updates) {
|
|
1151
|
+
const existing = await this.getGHII(ghii);
|
|
1152
|
+
if (!existing)
|
|
1153
|
+
return null;
|
|
1154
|
+
const updated = { ...existing, ...updates, updatedAt: new Date().toISOString() };
|
|
1155
|
+
this.db.prepare(`UPDATE ghiis SET username = ?, nodeId = ?, displayName = ?, bio = ?, avatar = ?, locale = ?,
|
|
1156
|
+
passwordHash = ?, verificationLevel = ?, ownerName = ?, createdAt = ?, updatedAt = ?,
|
|
1157
|
+
totpSecret = ?, totpEnabled = ?, totpBackupCodes = ?, totpLastUsedAt = ?,
|
|
1158
|
+
totpLastUsedCode = ?, totpFailedAttempts = ?, totpLockedUntil = ?, semantic = ?,
|
|
1159
|
+
emailHash = ?, emailVerifiedAt = ?, verificationMethod = ?, magicLinkEnabled = ?,
|
|
1160
|
+
notificationEmail = ?, lastLoginAt = ?, loginCount = ?, verifiedAttributes = ?,
|
|
1161
|
+
verificationIssuer = ?, verificationCredentialHash = ?, ftnVerified = ?,
|
|
1162
|
+
trustScore = ?, morselBalance = ?, allowedOrigins = ?
|
|
1163
|
+
WHERE ghii = ?`).run(updated.username, updated.nodeId, updated.displayName, updated.bio ?? null, updated.avatar ?? null, updated.locale ?? null, updated.passwordHash ?? null, updated.verificationLevel, updated.ownerName, updated.createdAt, updated.updatedAt, updated.totpSecret ?? null, updated.totpEnabled ? 1 : 0, updated.totpBackupCodes ? JSON.stringify(updated.totpBackupCodes) : null, updated.totpLastUsedAt ?? null, updated.totpLastUsedCode ?? null, updated.totpFailedAttempts ?? 0, updated.totpLockedUntil ?? null, updated.semantic ? JSON.stringify(updated.semantic) : null, updated.emailHash ?? null, updated.emailVerifiedAt ?? null, updated.verificationMethod ?? null, updated.magicLinkEnabled ? 1 : 0, updated.notificationEmail ?? null, updated.lastLoginAt ?? null, updated.loginCount ?? 0, updated.verifiedAttributes ? JSON.stringify(updated.verifiedAttributes) : null, updated.verificationIssuer ?? null, updated.verificationCredentialHash ?? null, updated.ftnVerified ? 1 : 0, updated.trustScore ?? null, updated.morselBalance ?? null, updated.allowedOrigins ? JSON.stringify(updated.allowedOrigins) : null, ghii);
|
|
1164
|
+
return updated;
|
|
1165
|
+
}
|
|
1166
|
+
async listGHIIs(opts) {
|
|
1167
|
+
const rows = this.db.prepare('SELECT * FROM ghiis').all();
|
|
1168
|
+
let results = rows.map(r => this.deserializeGHII(r));
|
|
1169
|
+
if (opts?.q) {
|
|
1170
|
+
const q = opts.q.toLowerCase();
|
|
1171
|
+
results = results.filter(r => r.username.toLowerCase().includes(q) ||
|
|
1172
|
+
r.displayName.toLowerCase().includes(q) ||
|
|
1173
|
+
(r.bio?.toLowerCase().includes(q) ?? false));
|
|
1174
|
+
}
|
|
1175
|
+
if (opts?.level !== undefined) {
|
|
1176
|
+
results = results.filter(r => r.verificationLevel >= opts.level);
|
|
1177
|
+
}
|
|
1178
|
+
return results;
|
|
1179
|
+
}
|
|
1180
|
+
async deleteGHII(ghii) {
|
|
1181
|
+
const result = this.db.prepare('DELETE FROM ghiis WHERE ghii = ?').run(ghii);
|
|
1182
|
+
return result.changes > 0;
|
|
1183
|
+
}
|
|
1184
|
+
deserializeGHII(row) {
|
|
1185
|
+
const record = {
|
|
1186
|
+
username: row.username,
|
|
1187
|
+
nodeId: row.nodeId,
|
|
1188
|
+
ghii: row.ghii,
|
|
1189
|
+
displayName: row.displayName,
|
|
1190
|
+
verificationLevel: row.verificationLevel,
|
|
1191
|
+
ownerName: row.ownerName,
|
|
1192
|
+
createdAt: row.createdAt,
|
|
1193
|
+
updatedAt: row.updatedAt,
|
|
1194
|
+
totpEnabled: row.totpEnabled === 1,
|
|
1195
|
+
};
|
|
1196
|
+
if (row.bio)
|
|
1197
|
+
record.bio = row.bio;
|
|
1198
|
+
if (row.avatar)
|
|
1199
|
+
record.avatar = row.avatar;
|
|
1200
|
+
if (row.locale)
|
|
1201
|
+
record.locale = row.locale;
|
|
1202
|
+
if (row.passwordHash)
|
|
1203
|
+
record.passwordHash = row.passwordHash;
|
|
1204
|
+
if (row.totpSecret)
|
|
1205
|
+
record.totpSecret = row.totpSecret;
|
|
1206
|
+
if (row.totpBackupCodes)
|
|
1207
|
+
record.totpBackupCodes = JSON.parse(row.totpBackupCodes);
|
|
1208
|
+
if (row.totpLastUsedAt)
|
|
1209
|
+
record.totpLastUsedAt = row.totpLastUsedAt;
|
|
1210
|
+
if (row.totpLastUsedCode)
|
|
1211
|
+
record.totpLastUsedCode = row.totpLastUsedCode;
|
|
1212
|
+
if (row.totpFailedAttempts)
|
|
1213
|
+
record.totpFailedAttempts = row.totpFailedAttempts;
|
|
1214
|
+
if (row.totpLockedUntil)
|
|
1215
|
+
record.totpLockedUntil = row.totpLockedUntil;
|
|
1216
|
+
if (row.semantic)
|
|
1217
|
+
record.semantic = JSON.parse(row.semantic);
|
|
1218
|
+
if (row.emailHash)
|
|
1219
|
+
record.emailHash = row.emailHash;
|
|
1220
|
+
if (row.emailVerifiedAt)
|
|
1221
|
+
record.emailVerifiedAt = row.emailVerifiedAt;
|
|
1222
|
+
if (row.verificationMethod)
|
|
1223
|
+
record.verificationMethod = row.verificationMethod;
|
|
1224
|
+
if (row.magicLinkEnabled)
|
|
1225
|
+
record.magicLinkEnabled = row.magicLinkEnabled === 1;
|
|
1226
|
+
if (row.notificationEmail)
|
|
1227
|
+
record.notificationEmail = row.notificationEmail;
|
|
1228
|
+
if (row.lastLoginAt)
|
|
1229
|
+
record.lastLoginAt = row.lastLoginAt;
|
|
1230
|
+
if (row.loginCount)
|
|
1231
|
+
record.loginCount = row.loginCount;
|
|
1232
|
+
if (row.verifiedAttributes)
|
|
1233
|
+
record.verifiedAttributes = JSON.parse(row.verifiedAttributes);
|
|
1234
|
+
if (row.verificationIssuer)
|
|
1235
|
+
record.verificationIssuer = row.verificationIssuer;
|
|
1236
|
+
if (row.verificationCredentialHash)
|
|
1237
|
+
record.verificationCredentialHash = row.verificationCredentialHash;
|
|
1238
|
+
if (row.ftnVerified)
|
|
1239
|
+
record.ftnVerified = row.ftnVerified === 1;
|
|
1240
|
+
if (row.trustScore !== null && row.trustScore !== undefined)
|
|
1241
|
+
record.trustScore = row.trustScore;
|
|
1242
|
+
if (row.morselBalance !== null && row.morselBalance !== undefined)
|
|
1243
|
+
record.morselBalance = row.morselBalance;
|
|
1244
|
+
if (row.allowedOrigins)
|
|
1245
|
+
record.allowedOrigins = JSON.parse(row.allowedOrigins);
|
|
1246
|
+
return record;
|
|
1247
|
+
}
|
|
1248
|
+
// ══════════════════════════════════════════════════════════
|
|
1249
|
+
// ── Chat Instances ──
|
|
1250
|
+
// ══════════════════════════════════════════════════════════
|
|
1251
|
+
async createChatInstance(record) {
|
|
1252
|
+
try {
|
|
1253
|
+
this.db.prepare(`INSERT INTO chat_instances (id, platform, appName, ownerName, ghii, nodeId, isAnonymous, createdAt, lastSeen, agentGaii, mcpClientId)
|
|
1254
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(record.id, record.platform, record.appName, record.ownerName, record.ghii, record.nodeId, record.isAnonymous ? 1 : 0, record.createdAt, record.lastSeen, record.agentGaii ?? null, record.mcpClientId ?? null);
|
|
1255
|
+
return record;
|
|
1256
|
+
}
|
|
1257
|
+
catch (err) {
|
|
1258
|
+
if (err instanceof Error && err.message?.includes('UNIQUE constraint failed'))
|
|
1259
|
+
throw new Error('CHAT_INSTANCE_EXISTS', { cause: err });
|
|
1260
|
+
throw err;
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
async getChatInstance(id) {
|
|
1264
|
+
const row = this.db.prepare('SELECT * FROM chat_instances WHERE id = ?').get(id);
|
|
1265
|
+
return row ? this.deserializeChatInstance(row) : null;
|
|
1266
|
+
}
|
|
1267
|
+
async listChatInstances(opts) {
|
|
1268
|
+
let sql = 'SELECT * FROM chat_instances WHERE 1=1';
|
|
1269
|
+
const params = [];
|
|
1270
|
+
if (opts?.ownerName) {
|
|
1271
|
+
sql += ' AND ownerName = ?';
|
|
1272
|
+
params.push(opts.ownerName);
|
|
1273
|
+
}
|
|
1274
|
+
if (opts?.platform) {
|
|
1275
|
+
sql += ' AND platform = ?';
|
|
1276
|
+
params.push(opts.platform);
|
|
1277
|
+
}
|
|
1278
|
+
if (opts?.ghii) {
|
|
1279
|
+
sql += ' AND ghii = ?';
|
|
1280
|
+
params.push(opts.ghii);
|
|
1281
|
+
}
|
|
1282
|
+
const rows = this.db.prepare(sql).all(...params);
|
|
1283
|
+
return rows.map(r => this.deserializeChatInstance(r));
|
|
1284
|
+
}
|
|
1285
|
+
async updateChatInstance(id, updates) {
|
|
1286
|
+
const existing = await this.getChatInstance(id);
|
|
1287
|
+
if (!existing)
|
|
1288
|
+
return null;
|
|
1289
|
+
const updated = { ...existing, ...updates };
|
|
1290
|
+
this.db.prepare(`UPDATE chat_instances SET platform = ?, appName = ?, ownerName = ?, ghii = ?,
|
|
1291
|
+
nodeId = ?, isAnonymous = ?, createdAt = ?, lastSeen = ?, agentGaii = ?, mcpClientId = ? WHERE id = ?`).run(updated.platform, updated.appName, updated.ownerName, updated.ghii, updated.nodeId, updated.isAnonymous ? 1 : 0, updated.createdAt, updated.lastSeen, updated.agentGaii ?? null, updated.mcpClientId ?? null, id);
|
|
1292
|
+
return updated;
|
|
1293
|
+
}
|
|
1294
|
+
async deleteChatInstance(id) {
|
|
1295
|
+
const result = this.db.prepare('DELETE FROM chat_instances WHERE id = ?').run(id);
|
|
1296
|
+
return result.changes > 0;
|
|
1297
|
+
}
|
|
1298
|
+
deserializeChatInstance(row) {
|
|
1299
|
+
return {
|
|
1300
|
+
id: row.id,
|
|
1301
|
+
platform: row.platform,
|
|
1302
|
+
appName: row.appName,
|
|
1303
|
+
ownerName: row.ownerName,
|
|
1304
|
+
ghii: row.ghii,
|
|
1305
|
+
nodeId: row.nodeId,
|
|
1306
|
+
isAnonymous: row.isAnonymous === 1,
|
|
1307
|
+
createdAt: row.createdAt,
|
|
1308
|
+
lastSeen: row.lastSeen,
|
|
1309
|
+
agentGaii: row.agentGaii || undefined,
|
|
1310
|
+
mcpClientId: row.mcpClientId || undefined,
|
|
1311
|
+
};
|
|
1312
|
+
}
|
|
1313
|
+
// ══════════════════════════════════════════════════════════
|
|
1314
|
+
// ── Email Verifications ──
|
|
1315
|
+
// ══════════════════════════════════════════════════════════
|
|
1316
|
+
async createEmailVerification(record) {
|
|
1317
|
+
this.db.prepare(`INSERT INTO email_verifications (id, ownerName, emailHash, code, purpose, status, attempts, expiresAt, createdAt, verifiedAt)
|
|
1318
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(record.id, record.ownerName, record.emailHash, record.code, record.purpose, record.status, record.attempts, record.expiresAt, record.createdAt, record.verifiedAt);
|
|
1319
|
+
return record;
|
|
1320
|
+
}
|
|
1321
|
+
async getEmailVerification(id) {
|
|
1322
|
+
const row = this.db.prepare('SELECT * FROM email_verifications WHERE id = ?').get(id);
|
|
1323
|
+
return row ? this.deserializeEmailVerification(row) : null;
|
|
1324
|
+
}
|
|
1325
|
+
async getActiveEmailVerification(ownerName, purpose) {
|
|
1326
|
+
const now = new Date().toISOString();
|
|
1327
|
+
const row = this.db.prepare(`SELECT * FROM email_verifications WHERE ownerName = ? AND purpose = ? AND status = 'pending' AND expiresAt > ? LIMIT 1`).get(ownerName, purpose, now);
|
|
1328
|
+
return row ? this.deserializeEmailVerification(row) : null;
|
|
1329
|
+
}
|
|
1330
|
+
async updateEmailVerification(id, updates) {
|
|
1331
|
+
const existing = await this.getEmailVerification(id);
|
|
1332
|
+
if (!existing)
|
|
1333
|
+
return null;
|
|
1334
|
+
const updated = { ...existing, ...updates };
|
|
1335
|
+
this.db.prepare(`UPDATE email_verifications SET ownerName = ?, emailHash = ?, code = ?, purpose = ?,
|
|
1336
|
+
status = ?, attempts = ?, expiresAt = ?, createdAt = ?, verifiedAt = ? WHERE id = ?`).run(updated.ownerName, updated.emailHash, updated.code, updated.purpose, updated.status, updated.attempts, updated.expiresAt, updated.createdAt, updated.verifiedAt, id);
|
|
1337
|
+
return updated;
|
|
1338
|
+
}
|
|
1339
|
+
async getEmailVerificationsByOwner(ownerName) {
|
|
1340
|
+
const rows = this.db.prepare('SELECT * FROM email_verifications WHERE ownerName = ?').all(ownerName);
|
|
1341
|
+
return rows.map(r => this.deserializeEmailVerification(r));
|
|
1342
|
+
}
|
|
1343
|
+
async deleteExpiredEmailVerifications() {
|
|
1344
|
+
const now = new Date().toISOString();
|
|
1345
|
+
const result = this.db.prepare(`DELETE FROM email_verifications WHERE status = 'pending' AND expiresAt < ?`).run(now);
|
|
1346
|
+
return result.changes;
|
|
1347
|
+
}
|
|
1348
|
+
deserializeEmailVerification(row) {
|
|
1349
|
+
return {
|
|
1350
|
+
id: row.id,
|
|
1351
|
+
ownerName: row.ownerName,
|
|
1352
|
+
emailHash: row.emailHash,
|
|
1353
|
+
code: row.code,
|
|
1354
|
+
purpose: row.purpose,
|
|
1355
|
+
status: row.status,
|
|
1356
|
+
attempts: row.attempts,
|
|
1357
|
+
expiresAt: row.expiresAt,
|
|
1358
|
+
createdAt: row.createdAt,
|
|
1359
|
+
verifiedAt: row.verifiedAt ?? null,
|
|
1360
|
+
};
|
|
1361
|
+
}
|
|
1362
|
+
// ══════════════════════════════════════════════════════════
|
|
1363
|
+
// ── Personal Nodes ──
|
|
1364
|
+
// ══════════════════════════════════════════════════════════
|
|
1365
|
+
async createPersonalNode(node) {
|
|
1366
|
+
this.db.prepare(`INSERT INTO personal_nodes (nodeId, ownerName, anchorNodeId, publicKey, status, agentGaiis,
|
|
1367
|
+
lastSeen, mailboxQuotaBytes, mailboxUsedBytes, visibility, createdAt, updatedAt, semantic)
|
|
1368
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(node.nodeId, node.ownerName, node.anchorNodeId, node.publicKey, node.status, JSON.stringify(node.agentGaiis), node.lastSeen, node.mailboxQuotaBytes, node.mailboxUsedBytes, node.visibility, node.createdAt, node.updatedAt, node.semantic ? JSON.stringify(node.semantic) : null);
|
|
1369
|
+
return { ...node };
|
|
1370
|
+
}
|
|
1371
|
+
async getPersonalNode(nodeId) {
|
|
1372
|
+
const row = this.db.prepare('SELECT * FROM personal_nodes WHERE nodeId = ?').get(nodeId);
|
|
1373
|
+
return row ? this.deserializePersonalNode(row) : null;
|
|
1374
|
+
}
|
|
1375
|
+
async getPersonalNodeByOwner(ownerName) {
|
|
1376
|
+
const row = this.db.prepare('SELECT * FROM personal_nodes WHERE ownerName = ?').get(ownerName);
|
|
1377
|
+
return row ? this.deserializePersonalNode(row) : null;
|
|
1378
|
+
}
|
|
1379
|
+
async listPersonalNodes(opts) {
|
|
1380
|
+
let sql = 'SELECT * FROM personal_nodes';
|
|
1381
|
+
const params = [];
|
|
1382
|
+
if (opts?.status) {
|
|
1383
|
+
sql += ' WHERE status = ?';
|
|
1384
|
+
params.push(opts.status);
|
|
1385
|
+
}
|
|
1386
|
+
const rows = this.db.prepare(sql).all(...params);
|
|
1387
|
+
return rows.map(r => this.deserializePersonalNode(r));
|
|
1388
|
+
}
|
|
1389
|
+
async updatePersonalNode(nodeId, updates) {
|
|
1390
|
+
const existing = await this.getPersonalNode(nodeId);
|
|
1391
|
+
if (!existing)
|
|
1392
|
+
return null;
|
|
1393
|
+
const updated = { ...existing, ...updates, updatedAt: new Date().toISOString() };
|
|
1394
|
+
this.db.prepare(`UPDATE personal_nodes SET ownerName = ?, anchorNodeId = ?, publicKey = ?, status = ?,
|
|
1395
|
+
agentGaiis = ?, lastSeen = ?, mailboxQuotaBytes = ?, mailboxUsedBytes = ?,
|
|
1396
|
+
visibility = ?, createdAt = ?, updatedAt = ?, semantic = ? WHERE nodeId = ?`).run(updated.ownerName, updated.anchorNodeId, updated.publicKey, updated.status, JSON.stringify(updated.agentGaiis), updated.lastSeen, updated.mailboxQuotaBytes, updated.mailboxUsedBytes, updated.visibility, updated.createdAt, updated.updatedAt, updated.semantic ? JSON.stringify(updated.semantic) : null, nodeId);
|
|
1397
|
+
return { ...updated };
|
|
1398
|
+
}
|
|
1399
|
+
async deletePersonalNode(nodeId) {
|
|
1400
|
+
const result = this.db.prepare('DELETE FROM personal_nodes WHERE nodeId = ?').run(nodeId);
|
|
1401
|
+
return result.changes > 0;
|
|
1402
|
+
}
|
|
1403
|
+
deserializePersonalNode(row) {
|
|
1404
|
+
const record = {
|
|
1405
|
+
nodeId: row.nodeId,
|
|
1406
|
+
ownerName: row.ownerName,
|
|
1407
|
+
anchorNodeId: row.anchorNodeId,
|
|
1408
|
+
publicKey: row.publicKey,
|
|
1409
|
+
status: row.status,
|
|
1410
|
+
agentGaiis: JSON.parse(row.agentGaiis),
|
|
1411
|
+
lastSeen: row.lastSeen,
|
|
1412
|
+
mailboxQuotaBytes: row.mailboxQuotaBytes,
|
|
1413
|
+
mailboxUsedBytes: row.mailboxUsedBytes,
|
|
1414
|
+
visibility: row.visibility,
|
|
1415
|
+
createdAt: row.createdAt,
|
|
1416
|
+
updatedAt: row.updatedAt,
|
|
1417
|
+
};
|
|
1418
|
+
if (row.semantic)
|
|
1419
|
+
record.semantic = JSON.parse(row.semantic);
|
|
1420
|
+
return record;
|
|
1421
|
+
}
|
|
1422
|
+
// ══════════════════════════════════════════════════════════
|
|
1423
|
+
// ── Mailbox ──
|
|
1424
|
+
// ══════════════════════════════════════════════════════════
|
|
1425
|
+
async createMailboxItem(item) {
|
|
1426
|
+
this.db.prepare(`INSERT INTO mailbox_items (id, personalNodeId, type, fromGaii, toGaii, payload, sizeBytes, retentionDays, expiresAt, createdAt)
|
|
1427
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(item.id, item.personalNodeId, item.type, item.fromGaii, item.toGaii, item.payload, item.sizeBytes, item.retentionDays, item.expiresAt, item.createdAt);
|
|
1428
|
+
// Update the personal node's mailbox usage
|
|
1429
|
+
this.db.prepare('UPDATE personal_nodes SET mailboxUsedBytes = mailboxUsedBytes + ? WHERE nodeId = ?').run(item.sizeBytes, item.personalNodeId);
|
|
1430
|
+
return { ...item };
|
|
1431
|
+
}
|
|
1432
|
+
async getMailboxItem(id) {
|
|
1433
|
+
const row = this.db.prepare('SELECT * FROM mailbox_items WHERE id = ?').get(id);
|
|
1434
|
+
return row ? this.deserializeMailboxItem(row) : null;
|
|
1435
|
+
}
|
|
1436
|
+
async listMailboxItems(personalNodeId, opts) {
|
|
1437
|
+
let sql = 'SELECT * FROM mailbox_items WHERE personalNodeId = ?';
|
|
1438
|
+
const params = [personalNodeId];
|
|
1439
|
+
if (opts?.type) {
|
|
1440
|
+
sql += ' AND type = ?';
|
|
1441
|
+
params.push(opts.type);
|
|
1442
|
+
}
|
|
1443
|
+
sql += ' ORDER BY createdAt ASC';
|
|
1444
|
+
if (opts?.limit) {
|
|
1445
|
+
sql += ' LIMIT ?';
|
|
1446
|
+
params.push(opts.limit);
|
|
1447
|
+
}
|
|
1448
|
+
const rows = this.db.prepare(sql).all(...params);
|
|
1449
|
+
return rows.map(r => this.deserializeMailboxItem(r));
|
|
1450
|
+
}
|
|
1451
|
+
async deleteMailboxItem(id) {
|
|
1452
|
+
const item = await this.getMailboxItem(id);
|
|
1453
|
+
if (!item)
|
|
1454
|
+
return false;
|
|
1455
|
+
// Update the personal node's mailbox usage
|
|
1456
|
+
this.db.prepare('UPDATE personal_nodes SET mailboxUsedBytes = MAX(0, mailboxUsedBytes - ?) WHERE nodeId = ?').run(item.sizeBytes, item.personalNodeId);
|
|
1457
|
+
const result = this.db.prepare('DELETE FROM mailbox_items WHERE id = ?').run(id);
|
|
1458
|
+
return result.changes > 0;
|
|
1459
|
+
}
|
|
1460
|
+
async deleteMailboxItemsByNode(personalNodeId) {
|
|
1461
|
+
const result = this.db.prepare('DELETE FROM mailbox_items WHERE personalNodeId = ?').run(personalNodeId);
|
|
1462
|
+
this.db.prepare('UPDATE personal_nodes SET mailboxUsedBytes = 0 WHERE nodeId = ?').run(personalNodeId);
|
|
1463
|
+
return result.changes;
|
|
1464
|
+
}
|
|
1465
|
+
async getMailboxStats(personalNodeId) {
|
|
1466
|
+
const row = this.db.prepare('SELECT COUNT(*) as count, COALESCE(SUM(sizeBytes), 0) as totalBytes FROM mailbox_items WHERE personalNodeId = ?').get(personalNodeId);
|
|
1467
|
+
return {
|
|
1468
|
+
count: row.count,
|
|
1469
|
+
totalBytes: row.totalBytes,
|
|
1470
|
+
};
|
|
1471
|
+
}
|
|
1472
|
+
async cleanExpiredMailboxItems() {
|
|
1473
|
+
const now = new Date().toISOString();
|
|
1474
|
+
// Get expired items to update personal node usage
|
|
1475
|
+
const expiredItems = this.db.prepare('SELECT personalNodeId, sizeBytes FROM mailbox_items WHERE expiresAt < ?').all(now);
|
|
1476
|
+
// Aggregate by personalNodeId
|
|
1477
|
+
const bytesPerNode = new Map();
|
|
1478
|
+
for (const item of expiredItems) {
|
|
1479
|
+
const nodeId = item.personalNodeId;
|
|
1480
|
+
bytesPerNode.set(nodeId, (bytesPerNode.get(nodeId) ?? 0) + item.sizeBytes);
|
|
1481
|
+
}
|
|
1482
|
+
// Delete expired items
|
|
1483
|
+
const result = this.db.prepare('DELETE FROM mailbox_items WHERE expiresAt < ?').run(now);
|
|
1484
|
+
// Update personal node usage
|
|
1485
|
+
for (const [nodeId, bytes] of bytesPerNode) {
|
|
1486
|
+
this.db.prepare('UPDATE personal_nodes SET mailboxUsedBytes = MAX(0, mailboxUsedBytes - ?) WHERE nodeId = ?').run(bytes, nodeId);
|
|
1487
|
+
}
|
|
1488
|
+
return result.changes;
|
|
1489
|
+
}
|
|
1490
|
+
deserializeMailboxItem(row) {
|
|
1491
|
+
return {
|
|
1492
|
+
id: row.id,
|
|
1493
|
+
personalNodeId: row.personalNodeId,
|
|
1494
|
+
type: row.type,
|
|
1495
|
+
fromGaii: row.fromGaii,
|
|
1496
|
+
toGaii: row.toGaii,
|
|
1497
|
+
payload: row.payload,
|
|
1498
|
+
sizeBytes: row.sizeBytes,
|
|
1499
|
+
retentionDays: row.retentionDays,
|
|
1500
|
+
expiresAt: row.expiresAt,
|
|
1501
|
+
createdAt: row.createdAt,
|
|
1502
|
+
};
|
|
1503
|
+
}
|
|
1504
|
+
// ══════════════════════════════════════════════════════════
|
|
1505
|
+
// ── Maintenance Mode ──
|
|
1506
|
+
// ══════════════════════════════════════════════════════════
|
|
1507
|
+
async getMaintenanceMode() {
|
|
1508
|
+
const row = this.db.prepare('SELECT * FROM maintenance WHERE id = 1').get();
|
|
1509
|
+
if (!row) {
|
|
1510
|
+
return { enabled: false, message: '', enabledAt: null, enabledBy: null };
|
|
1511
|
+
}
|
|
1512
|
+
return {
|
|
1513
|
+
enabled: row.enabled === 1,
|
|
1514
|
+
message: row.message,
|
|
1515
|
+
enabledAt: row.enabledAt ?? null,
|
|
1516
|
+
enabledBy: row.enabledBy ?? null,
|
|
1517
|
+
};
|
|
1518
|
+
}
|
|
1519
|
+
async setMaintenanceMode(state) {
|
|
1520
|
+
this.db.prepare(`INSERT OR REPLACE INTO maintenance (id, enabled, message, enabledAt, enabledBy) VALUES (1, ?, ?, ?, ?)`).run(state.enabled ? 1 : 0, state.message, state.enabledAt, state.enabledBy);
|
|
1521
|
+
return state;
|
|
1522
|
+
}
|
|
1523
|
+
// ══════════════════════════════════════════════════════════
|
|
1524
|
+
// ── Consent Layer ──
|
|
1525
|
+
// ══════════════════════════════════════════════════════════
|
|
1526
|
+
async createConsent(record) {
|
|
1527
|
+
this.db.prepare(`INSERT INTO consents (id, ownerGaii, dataPattern, recipient, purpose, scope, expires, status, grantedAt, revokedAt, metadata)
|
|
1528
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(record.id, record.ownerGaii, record.dataPattern, record.recipient, record.purpose, record.scope, record.expires, record.status, record.grantedAt, record.revokedAt, record.metadata ? JSON.stringify(record.metadata) : null);
|
|
1529
|
+
return record;
|
|
1530
|
+
}
|
|
1531
|
+
async getConsent(id) {
|
|
1532
|
+
const row = this.db.prepare('SELECT * FROM consents WHERE id = ?').get(id);
|
|
1533
|
+
return row ? this.deserializeConsent(row) : null;
|
|
1534
|
+
}
|
|
1535
|
+
async listConsents(ownerGaii, opts) {
|
|
1536
|
+
let sql = 'SELECT * FROM consents WHERE ownerGaii = ?';
|
|
1537
|
+
const params = [ownerGaii];
|
|
1538
|
+
if (opts?.status) {
|
|
1539
|
+
sql += ' AND status = ?';
|
|
1540
|
+
params.push(opts.status);
|
|
1541
|
+
}
|
|
1542
|
+
if (opts?.recipient) {
|
|
1543
|
+
sql += ' AND recipient = ?';
|
|
1544
|
+
params.push(opts.recipient);
|
|
1545
|
+
}
|
|
1546
|
+
const rows = this.db.prepare(sql).all(...params);
|
|
1547
|
+
return rows.map(r => this.deserializeConsent(r));
|
|
1548
|
+
}
|
|
1549
|
+
async updateConsent(id, updates) {
|
|
1550
|
+
const existing = await this.getConsent(id);
|
|
1551
|
+
if (!existing)
|
|
1552
|
+
return null;
|
|
1553
|
+
const updated = { ...existing, ...updates };
|
|
1554
|
+
this.db.prepare(`UPDATE consents SET ownerGaii = ?, dataPattern = ?, recipient = ?, purpose = ?,
|
|
1555
|
+
scope = ?, expires = ?, status = ?, grantedAt = ?, revokedAt = ?, metadata = ? WHERE id = ?`).run(updated.ownerGaii, updated.dataPattern, updated.recipient, updated.purpose, updated.scope, updated.expires, updated.status, updated.grantedAt, updated.revokedAt, updated.metadata ? JSON.stringify(updated.metadata) : null, id);
|
|
1556
|
+
return updated;
|
|
1557
|
+
}
|
|
1558
|
+
async deleteConsent(id) {
|
|
1559
|
+
const result = this.db.prepare('DELETE FROM consents WHERE id = ?').run(id);
|
|
1560
|
+
return result.changes > 0;
|
|
1561
|
+
}
|
|
1562
|
+
async findMatchingConsents(ownerGaii, memoryKey, accessorGaii) {
|
|
1563
|
+
const now = new Date().toISOString();
|
|
1564
|
+
const rows = this.db.prepare(`SELECT * FROM consents WHERE ownerGaii = ? AND status = 'active'`).all(ownerGaii);
|
|
1565
|
+
const results = [];
|
|
1566
|
+
for (const row of rows) {
|
|
1567
|
+
const consent = this.deserializeConsent(row);
|
|
1568
|
+
// Check expiration
|
|
1569
|
+
if (consent.expires && consent.expires < now) {
|
|
1570
|
+
this.db.prepare('UPDATE consents SET status = ? WHERE id = ?').run('expired', consent.id);
|
|
1571
|
+
continue;
|
|
1572
|
+
}
|
|
1573
|
+
// Check recipient (supports *, exact GAII, ghii:, domain:, node:)
|
|
1574
|
+
const accessor = parseGaiiLoose(accessorGaii);
|
|
1575
|
+
if (!matchesRecipient(consent.recipient, accessorGaii, accessor.owner, accessor.node))
|
|
1576
|
+
continue;
|
|
1577
|
+
// Check data_pattern (glob match)
|
|
1578
|
+
if (!consentMatchPattern(consent.dataPattern, memoryKey))
|
|
1579
|
+
continue;
|
|
1580
|
+
results.push(consent);
|
|
1581
|
+
}
|
|
1582
|
+
return results;
|
|
1583
|
+
}
|
|
1584
|
+
// Consent Audit
|
|
1585
|
+
async addConsentAuditEntry(entry) {
|
|
1586
|
+
this.db.prepare(`INSERT INTO consent_audit (id, consentId, ownerGaii, accessorGaii, memoryKey, action, timestamp, allowed)
|
|
1587
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`).run(entry.id, entry.consentId, entry.ownerGaii, entry.accessorGaii, entry.memoryKey, entry.action, entry.timestamp, entry.allowed ? 1 : 0);
|
|
1588
|
+
return entry;
|
|
1589
|
+
}
|
|
1590
|
+
async listConsentAudit(ownerGaii, opts) {
|
|
1591
|
+
let sql = 'SELECT * FROM consent_audit WHERE ownerGaii = ?';
|
|
1592
|
+
const params = [ownerGaii];
|
|
1593
|
+
if (opts?.days) {
|
|
1594
|
+
const cutoff = new Date(Date.now() - opts.days * 86400000).toISOString();
|
|
1595
|
+
sql += ' AND timestamp >= ?';
|
|
1596
|
+
params.push(cutoff);
|
|
1597
|
+
}
|
|
1598
|
+
if (opts?.consentId) {
|
|
1599
|
+
sql += ' AND consentId = ?';
|
|
1600
|
+
params.push(opts.consentId);
|
|
1601
|
+
}
|
|
1602
|
+
if (opts?.accessorGaii) {
|
|
1603
|
+
sql += ' AND accessorGaii = ?';
|
|
1604
|
+
params.push(opts.accessorGaii);
|
|
1605
|
+
}
|
|
1606
|
+
const rows = this.db.prepare(sql).all(...params);
|
|
1607
|
+
return rows.map(r => ({
|
|
1608
|
+
id: r.id,
|
|
1609
|
+
consentId: r.consentId,
|
|
1610
|
+
ownerGaii: r.ownerGaii,
|
|
1611
|
+
accessorGaii: r.accessorGaii,
|
|
1612
|
+
memoryKey: r.memoryKey,
|
|
1613
|
+
action: r.action,
|
|
1614
|
+
timestamp: r.timestamp,
|
|
1615
|
+
allowed: r.allowed === 1,
|
|
1616
|
+
}));
|
|
1617
|
+
}
|
|
1618
|
+
deserializeConsent(row) {
|
|
1619
|
+
const record = {
|
|
1620
|
+
id: row.id,
|
|
1621
|
+
ownerGaii: row.ownerGaii,
|
|
1622
|
+
dataPattern: row.dataPattern,
|
|
1623
|
+
recipient: row.recipient,
|
|
1624
|
+
purpose: row.purpose,
|
|
1625
|
+
scope: row.scope,
|
|
1626
|
+
expires: row.expires ?? null,
|
|
1627
|
+
status: row.status,
|
|
1628
|
+
grantedAt: row.grantedAt,
|
|
1629
|
+
revokedAt: row.revokedAt ?? null,
|
|
1630
|
+
};
|
|
1631
|
+
if (row.metadata)
|
|
1632
|
+
record.metadata = JSON.parse(row.metadata);
|
|
1633
|
+
return record;
|
|
1634
|
+
}
|
|
1635
|
+
// ══════════════════════════════════════════════════════════
|
|
1636
|
+
// ── Schema Locking ──
|
|
1637
|
+
// ══════════════════════════════════════════════════════════
|
|
1638
|
+
async setSchema(record) {
|
|
1639
|
+
this.db.prepare(`INSERT OR REPLACE INTO schemas (keyPattern, applyTo, schemaJson, schemaMode, lockedBy, setAt, updatedAt, semanticContext)
|
|
1640
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`).run(record.keyPattern, record.applyTo, JSON.stringify(record.schemaJson), record.schemaMode, record.lockedBy, record.setAt, record.updatedAt, record.semanticContext ? JSON.stringify(record.semanticContext) : null);
|
|
1641
|
+
return record;
|
|
1642
|
+
}
|
|
1643
|
+
async getSchema(keyPattern, applyTo) {
|
|
1644
|
+
if (applyTo) {
|
|
1645
|
+
const row = this.db.prepare('SELECT * FROM schemas WHERE keyPattern = ? AND applyTo = ?').get(keyPattern, applyTo);
|
|
1646
|
+
return row ? this.deserializeSchema(row) : null;
|
|
1647
|
+
}
|
|
1648
|
+
// Try exact first, then prefix
|
|
1649
|
+
const exactRow = this.db.prepare('SELECT * FROM schemas WHERE keyPattern = ? AND applyTo = ?').get(keyPattern, 'exact');
|
|
1650
|
+
if (exactRow)
|
|
1651
|
+
return this.deserializeSchema(exactRow);
|
|
1652
|
+
const prefixRow = this.db.prepare('SELECT * FROM schemas WHERE keyPattern = ? AND applyTo = ?').get(keyPattern, 'prefix');
|
|
1653
|
+
return prefixRow ? this.deserializeSchema(prefixRow) : null;
|
|
1654
|
+
}
|
|
1655
|
+
async deleteSchema(keyPattern) {
|
|
1656
|
+
const result = this.db.prepare('DELETE FROM schemas WHERE keyPattern = ?').run(keyPattern);
|
|
1657
|
+
return result.changes > 0;
|
|
1658
|
+
}
|
|
1659
|
+
async listSchemas(prefix) {
|
|
1660
|
+
let sql = 'SELECT * FROM schemas';
|
|
1661
|
+
const params = [];
|
|
1662
|
+
if (prefix) {
|
|
1663
|
+
sql += ' WHERE keyPattern LIKE ?';
|
|
1664
|
+
params.push(prefix + '%');
|
|
1665
|
+
}
|
|
1666
|
+
const rows = this.db.prepare(sql).all(...params);
|
|
1667
|
+
return rows.map(r => this.deserializeSchema(r));
|
|
1668
|
+
}
|
|
1669
|
+
async findApplicableSchema(memoryKey) {
|
|
1670
|
+
// 1. Exact match -- highest priority
|
|
1671
|
+
const exactRow = this.db.prepare('SELECT * FROM schemas WHERE keyPattern = ? AND applyTo = ?').get(memoryKey, 'exact');
|
|
1672
|
+
if (exactRow)
|
|
1673
|
+
return this.deserializeSchema(exactRow);
|
|
1674
|
+
// 2. Wildcard pattern match -- supports profile.*.interests style
|
|
1675
|
+
const prefixSchemas = this.db.prepare('SELECT * FROM schemas WHERE applyTo = ?').all('prefix');
|
|
1676
|
+
let bestWildcard = null;
|
|
1677
|
+
let bestSegments = 0;
|
|
1678
|
+
for (const row of prefixSchemas) {
|
|
1679
|
+
const record = this.deserializeSchema(row);
|
|
1680
|
+
if (!record.keyPattern.includes('*'))
|
|
1681
|
+
continue;
|
|
1682
|
+
if (matchWildcardPattern(record.keyPattern, memoryKey)) {
|
|
1683
|
+
const segments = record.keyPattern.split('.').length;
|
|
1684
|
+
if (segments > bestSegments) {
|
|
1685
|
+
bestWildcard = record;
|
|
1686
|
+
bestSegments = segments;
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
if (bestWildcard)
|
|
1691
|
+
return bestWildcard;
|
|
1692
|
+
// 3. Simple prefix match -- longest prefix wins
|
|
1693
|
+
const parts = memoryKey.split('.');
|
|
1694
|
+
for (let i = parts.length - 1; i >= 1; i--) {
|
|
1695
|
+
const prefix = parts.slice(0, i).join('.');
|
|
1696
|
+
const prefixRow = this.db.prepare('SELECT * FROM schemas WHERE keyPattern = ? AND applyTo = ?').get(prefix, 'prefix');
|
|
1697
|
+
if (prefixRow)
|
|
1698
|
+
return this.deserializeSchema(prefixRow);
|
|
1699
|
+
}
|
|
1700
|
+
return null;
|
|
1701
|
+
}
|
|
1702
|
+
deserializeSchema(row) {
|
|
1703
|
+
const record = {
|
|
1704
|
+
keyPattern: row.keyPattern,
|
|
1705
|
+
applyTo: row.applyTo,
|
|
1706
|
+
schemaJson: JSON.parse(row.schemaJson),
|
|
1707
|
+
schemaMode: row.schemaMode,
|
|
1708
|
+
lockedBy: row.lockedBy,
|
|
1709
|
+
setAt: row.setAt,
|
|
1710
|
+
updatedAt: row.updatedAt,
|
|
1711
|
+
};
|
|
1712
|
+
if (row.semanticContext)
|
|
1713
|
+
record.semanticContext = JSON.parse(row.semanticContext);
|
|
1714
|
+
return record;
|
|
1715
|
+
}
|
|
1716
|
+
// ══════════════════════════════════════════════════════════
|
|
1717
|
+
// ── CSM (Community Service Manifest) ──
|
|
1718
|
+
// ══════════════════════════════════════════════════════════
|
|
1719
|
+
async createCsm(record) {
|
|
1720
|
+
try {
|
|
1721
|
+
this.db.prepare(`INSERT INTO csms (name, definition, jsonSchemaKey, serviceType, registeredBy, registeredAt, updatedAt, semantic, federate)
|
|
1722
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(record.name, JSON.stringify(record.definition), record.jsonSchemaKey, record.serviceType, record.registeredBy, record.registeredAt, record.updatedAt, record.semantic ? JSON.stringify(record.semantic) : null, record.federate ? 1 : 0);
|
|
1723
|
+
return record;
|
|
1724
|
+
}
|
|
1725
|
+
catch (err) {
|
|
1726
|
+
if (err instanceof Error && err.message?.includes('UNIQUE constraint failed'))
|
|
1727
|
+
throw new Error('CSM_NAME_TAKEN', { cause: err });
|
|
1728
|
+
throw err;
|
|
1729
|
+
}
|
|
1730
|
+
}
|
|
1731
|
+
async getCsm(name) {
|
|
1732
|
+
const row = this.db.prepare('SELECT * FROM csms WHERE name = ?').get(name);
|
|
1733
|
+
return row ? this.deserializeCsm(row) : null;
|
|
1734
|
+
}
|
|
1735
|
+
async listCsms(opts) {
|
|
1736
|
+
let sql = 'SELECT * FROM csms';
|
|
1737
|
+
const params = [];
|
|
1738
|
+
if (opts?.serviceType) {
|
|
1739
|
+
sql += ' WHERE serviceType = ?';
|
|
1740
|
+
params.push(opts.serviceType);
|
|
1741
|
+
}
|
|
1742
|
+
const rows = this.db.prepare(sql).all(...params);
|
|
1743
|
+
return rows.map(r => this.deserializeCsm(r));
|
|
1744
|
+
}
|
|
1745
|
+
async updateCsm(name, updates) {
|
|
1746
|
+
const existing = await this.getCsm(name);
|
|
1747
|
+
if (!existing)
|
|
1748
|
+
return null;
|
|
1749
|
+
const updated = { ...existing, ...updates, name: existing.name };
|
|
1750
|
+
this.db.prepare(`UPDATE csms SET definition = ?, jsonSchemaKey = ?, serviceType = ?, registeredBy = ?,
|
|
1751
|
+
registeredAt = ?, updatedAt = ?, semantic = ?, federate = ? WHERE name = ?`).run(JSON.stringify(updated.definition), updated.jsonSchemaKey, updated.serviceType, updated.registeredBy, updated.registeredAt, updated.updatedAt, updated.semantic ? JSON.stringify(updated.semantic) : null, updated.federate ? 1 : 0, name);
|
|
1752
|
+
return updated;
|
|
1753
|
+
}
|
|
1754
|
+
async deleteCsm(name) {
|
|
1755
|
+
const result = this.db.prepare('DELETE FROM csms WHERE name = ?').run(name);
|
|
1756
|
+
return result.changes > 0;
|
|
1757
|
+
}
|
|
1758
|
+
deserializeCsm(row) {
|
|
1759
|
+
const record = {
|
|
1760
|
+
name: row.name,
|
|
1761
|
+
definition: JSON.parse(row.definition),
|
|
1762
|
+
jsonSchemaKey: row.jsonSchemaKey,
|
|
1763
|
+
serviceType: row.serviceType,
|
|
1764
|
+
registeredBy: row.registeredBy,
|
|
1765
|
+
registeredAt: row.registeredAt,
|
|
1766
|
+
updatedAt: row.updatedAt,
|
|
1767
|
+
};
|
|
1768
|
+
if (row.semantic)
|
|
1769
|
+
record.semantic = JSON.parse(row.semantic);
|
|
1770
|
+
if (row.federate)
|
|
1771
|
+
record.federate = row.federate === 1;
|
|
1772
|
+
return record;
|
|
1773
|
+
}
|
|
1774
|
+
// ══════════════════════════════════════════════════════════
|
|
1775
|
+
// ── MSM (Machine Service Manifest) ──
|
|
1776
|
+
// ══════════════════════════════════════════════════════════
|
|
1777
|
+
async createMsm(record) {
|
|
1778
|
+
try {
|
|
1779
|
+
this.db.prepare(`INSERT INTO msms (name, definition, category, authType, actionsCount, registeredBy, registeredAt, updatedAt, federate)
|
|
1780
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(record.name, JSON.stringify(record.definition), record.category, record.authType, record.actionsCount, record.registeredBy, record.registeredAt, record.updatedAt, record.federate ? 1 : 0);
|
|
1781
|
+
return record;
|
|
1782
|
+
}
|
|
1783
|
+
catch (err) {
|
|
1784
|
+
if (err instanceof Error && err.message?.includes('UNIQUE constraint failed'))
|
|
1785
|
+
throw new Error('MSM_NAME_TAKEN', { cause: err });
|
|
1786
|
+
throw err;
|
|
1787
|
+
}
|
|
1788
|
+
}
|
|
1789
|
+
async getMsm(name) {
|
|
1790
|
+
const row = this.db.prepare('SELECT * FROM msms WHERE name = ?').get(name);
|
|
1791
|
+
return row ? this.deserializeMsm(row) : null;
|
|
1792
|
+
}
|
|
1793
|
+
async listMsms(opts) {
|
|
1794
|
+
let sql = 'SELECT * FROM msms';
|
|
1795
|
+
const params = [];
|
|
1796
|
+
if (opts?.category) {
|
|
1797
|
+
sql += ' WHERE category = ?';
|
|
1798
|
+
params.push(opts.category);
|
|
1799
|
+
}
|
|
1800
|
+
const rows = this.db.prepare(sql).all(...params);
|
|
1801
|
+
return rows.map(r => this.deserializeMsm(r));
|
|
1802
|
+
}
|
|
1803
|
+
async updateMsm(name, updates) {
|
|
1804
|
+
const existing = await this.getMsm(name);
|
|
1805
|
+
if (!existing)
|
|
1806
|
+
return null;
|
|
1807
|
+
const updated = { ...existing, ...updates, name: existing.name, updatedAt: new Date().toISOString() };
|
|
1808
|
+
this.db.prepare(`UPDATE msms SET definition = ?, category = ?, authType = ?, actionsCount = ?,
|
|
1809
|
+
registeredBy = ?, registeredAt = ?, updatedAt = ?, federate = ? WHERE name = ?`).run(JSON.stringify(updated.definition), updated.category, updated.authType, updated.actionsCount, updated.registeredBy, updated.registeredAt, updated.updatedAt, updated.federate ? 1 : 0, name);
|
|
1810
|
+
return updated;
|
|
1811
|
+
}
|
|
1812
|
+
async deleteMsm(name) {
|
|
1813
|
+
const result = this.db.prepare('DELETE FROM msms WHERE name = ?').run(name);
|
|
1814
|
+
return result.changes > 0;
|
|
1815
|
+
}
|
|
1816
|
+
deserializeMsm(row) {
|
|
1817
|
+
const record = {
|
|
1818
|
+
name: row.name,
|
|
1819
|
+
definition: JSON.parse(row.definition),
|
|
1820
|
+
category: row.category,
|
|
1821
|
+
authType: row.authType,
|
|
1822
|
+
actionsCount: row.actionsCount,
|
|
1823
|
+
registeredBy: row.registeredBy,
|
|
1824
|
+
registeredAt: row.registeredAt,
|
|
1825
|
+
updatedAt: row.updatedAt,
|
|
1826
|
+
};
|
|
1827
|
+
if (row.federate)
|
|
1828
|
+
record.federate = row.federate === 1;
|
|
1829
|
+
return record;
|
|
1830
|
+
}
|
|
1831
|
+
// ══════════════════════════════════════════════════════════
|
|
1832
|
+
// ── Flags (Moderation) ──
|
|
1833
|
+
// ══════════════════════════════════════════════════════════
|
|
1834
|
+
async createFlag(record) {
|
|
1835
|
+
this.db.prepare(`INSERT INTO flags (id, targetType, targetId, flaggedBy, reason, description, status, reviewedBy, reviewedAt, createdAt)
|
|
1836
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(record.id, record.targetType, record.targetId, record.flaggedBy, record.reason, record.description ?? null, record.status, record.reviewedBy ?? null, record.reviewedAt ?? null, record.createdAt);
|
|
1837
|
+
return record;
|
|
1838
|
+
}
|
|
1839
|
+
async getFlag(id) {
|
|
1840
|
+
const row = this.db.prepare('SELECT * FROM flags WHERE id = ?').get(id);
|
|
1841
|
+
return row ? this.deserializeFlag(row) : null;
|
|
1842
|
+
}
|
|
1843
|
+
async getFlagsByTarget(targetType, targetId) {
|
|
1844
|
+
const rows = this.db.prepare('SELECT * FROM flags WHERE targetType = ? AND targetId = ?').all(targetType, targetId);
|
|
1845
|
+
return rows.map(r => this.deserializeFlag(r));
|
|
1846
|
+
}
|
|
1847
|
+
async getFlagByUser(targetType, targetId, flaggedBy) {
|
|
1848
|
+
const row = this.db.prepare('SELECT * FROM flags WHERE targetType = ? AND targetId = ? AND flaggedBy = ?').get(targetType, targetId, flaggedBy);
|
|
1849
|
+
return row ? this.deserializeFlag(row) : null;
|
|
1850
|
+
}
|
|
1851
|
+
async getFlagSummary(targetType, targetId) {
|
|
1852
|
+
const rows = this.db.prepare('SELECT * FROM flags WHERE targetType = ? AND targetId = ?').all(targetType, targetId);
|
|
1853
|
+
if (rows.length === 0)
|
|
1854
|
+
return null;
|
|
1855
|
+
const byReason = {};
|
|
1856
|
+
let latestFlag = '';
|
|
1857
|
+
for (const r of rows) {
|
|
1858
|
+
const reason = r.reason;
|
|
1859
|
+
byReason[reason] = (byReason[reason] ?? 0) + 1;
|
|
1860
|
+
if (r.createdAt > latestFlag)
|
|
1861
|
+
latestFlag = r.createdAt;
|
|
1862
|
+
}
|
|
1863
|
+
return {
|
|
1864
|
+
targetType,
|
|
1865
|
+
targetId,
|
|
1866
|
+
totalFlags: rows.length,
|
|
1867
|
+
byReason,
|
|
1868
|
+
latestFlag,
|
|
1869
|
+
};
|
|
1870
|
+
}
|
|
1871
|
+
async updateFlag(id, updates) {
|
|
1872
|
+
const existing = await this.getFlag(id);
|
|
1873
|
+
if (!existing)
|
|
1874
|
+
return null;
|
|
1875
|
+
const updated = { ...existing, ...updates };
|
|
1876
|
+
this.db.prepare(`UPDATE flags SET targetType = ?, targetId = ?, flaggedBy = ?, reason = ?,
|
|
1877
|
+
description = ?, status = ?, reviewedBy = ?, reviewedAt = ?, createdAt = ? WHERE id = ?`).run(updated.targetType, updated.targetId, updated.flaggedBy, updated.reason, updated.description ?? null, updated.status, updated.reviewedBy ?? null, updated.reviewedAt ?? null, updated.createdAt, id);
|
|
1878
|
+
return updated;
|
|
1879
|
+
}
|
|
1880
|
+
async listFlags(opts) {
|
|
1881
|
+
const page = opts?.page ?? 1;
|
|
1882
|
+
const perPage = opts?.perPage ?? 20;
|
|
1883
|
+
let sql = 'SELECT * FROM flags WHERE 1=1';
|
|
1884
|
+
const params = [];
|
|
1885
|
+
if (opts?.status) {
|
|
1886
|
+
sql += ' AND status = ?';
|
|
1887
|
+
params.push(opts.status);
|
|
1888
|
+
}
|
|
1889
|
+
if (opts?.targetType) {
|
|
1890
|
+
sql += ' AND targetType = ?';
|
|
1891
|
+
params.push(opts.targetType);
|
|
1892
|
+
}
|
|
1893
|
+
sql += ' ORDER BY createdAt DESC';
|
|
1894
|
+
sql += ' LIMIT ? OFFSET ?';
|
|
1895
|
+
params.push(perPage, (page - 1) * perPage);
|
|
1896
|
+
const rows = this.db.prepare(sql).all(...params);
|
|
1897
|
+
return rows.map(r => this.deserializeFlag(r));
|
|
1898
|
+
}
|
|
1899
|
+
deserializeFlag(row) {
|
|
1900
|
+
const record = {
|
|
1901
|
+
id: row.id,
|
|
1902
|
+
targetType: row.targetType,
|
|
1903
|
+
targetId: row.targetId,
|
|
1904
|
+
flaggedBy: row.flaggedBy,
|
|
1905
|
+
reason: row.reason,
|
|
1906
|
+
status: row.status,
|
|
1907
|
+
createdAt: row.createdAt,
|
|
1908
|
+
};
|
|
1909
|
+
if (row.description)
|
|
1910
|
+
record.description = row.description;
|
|
1911
|
+
if (row.reviewedBy)
|
|
1912
|
+
record.reviewedBy = row.reviewedBy;
|
|
1913
|
+
if (row.reviewedAt)
|
|
1914
|
+
record.reviewedAt = row.reviewedAt;
|
|
1915
|
+
return record;
|
|
1916
|
+
}
|
|
1917
|
+
// ══════════════════════════════════════════════════════════
|
|
1918
|
+
// ── Matches ──
|
|
1919
|
+
// ══════════════════════════════════════════════════════════
|
|
1920
|
+
async createMatch(record) {
|
|
1921
|
+
this.db.prepare(`INSERT INTO matches (id, profileA, profileB, score, breakdown, status, notifiedAt, respondedAt, expiresAt, createdAt)
|
|
1922
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(record.id, record.profileA, record.profileB, record.score, JSON.stringify(record.breakdown), record.status, record.notifiedAt, record.respondedAt, record.expiresAt, record.createdAt);
|
|
1923
|
+
return record;
|
|
1924
|
+
}
|
|
1925
|
+
async getMatch(id) {
|
|
1926
|
+
const row = this.db.prepare('SELECT * FROM matches WHERE id = ?').get(id);
|
|
1927
|
+
return row ? this.deserializeMatch(row) : null;
|
|
1928
|
+
}
|
|
1929
|
+
async getMatchByPair(profileA, profileB) {
|
|
1930
|
+
const row = this.db.prepare('SELECT * FROM matches WHERE (profileA = ? AND profileB = ?) OR (profileA = ? AND profileB = ?)').get(profileA, profileB, profileB, profileA);
|
|
1931
|
+
return row ? this.deserializeMatch(row) : null;
|
|
1932
|
+
}
|
|
1933
|
+
async listMatchesByProfile(profile, opts) {
|
|
1934
|
+
const page = opts?.page ?? 1;
|
|
1935
|
+
const perPage = opts?.perPage ?? 10;
|
|
1936
|
+
let sql = 'SELECT * FROM matches WHERE (profileA = ? OR profileB = ?)';
|
|
1937
|
+
const params = [profile, profile];
|
|
1938
|
+
if (opts?.status) {
|
|
1939
|
+
sql += ' AND status = ?';
|
|
1940
|
+
params.push(opts.status);
|
|
1941
|
+
}
|
|
1942
|
+
sql += ' ORDER BY createdAt DESC LIMIT ? OFFSET ?';
|
|
1943
|
+
params.push(perPage, (page - 1) * perPage);
|
|
1944
|
+
const rows = this.db.prepare(sql).all(...params);
|
|
1945
|
+
return rows.map(r => this.deserializeMatch(r));
|
|
1946
|
+
}
|
|
1947
|
+
async updateMatch(id, updates) {
|
|
1948
|
+
const existing = await this.getMatch(id);
|
|
1949
|
+
if (!existing)
|
|
1950
|
+
return null;
|
|
1951
|
+
const updated = { ...existing, ...updates };
|
|
1952
|
+
this.db.prepare(`UPDATE matches SET profileA = ?, profileB = ?, score = ?, breakdown = ?,
|
|
1953
|
+
status = ?, notifiedAt = ?, respondedAt = ?, expiresAt = ?, createdAt = ? WHERE id = ?`).run(updated.profileA, updated.profileB, updated.score, JSON.stringify(updated.breakdown), updated.status, updated.notifiedAt, updated.respondedAt, updated.expiresAt, updated.createdAt, id);
|
|
1954
|
+
return updated;
|
|
1955
|
+
}
|
|
1956
|
+
async deleteExpiredMatches() {
|
|
1957
|
+
const now = new Date().toISOString();
|
|
1958
|
+
const result = this.db.prepare(`DELETE FROM matches WHERE expiresAt < ? AND status != 'accepted'`).run(now);
|
|
1959
|
+
return result.changes;
|
|
1960
|
+
}
|
|
1961
|
+
async listAllMatches(limit = 10000) {
|
|
1962
|
+
const rows = this.db.prepare('SELECT * FROM matches ORDER BY createdAt DESC LIMIT ?').all(Math.min(limit, 10000));
|
|
1963
|
+
return rows.map(r => this.deserializeMatch(r));
|
|
1964
|
+
}
|
|
1965
|
+
deserializeMatch(row) {
|
|
1966
|
+
return {
|
|
1967
|
+
id: row.id,
|
|
1968
|
+
profileA: row.profileA,
|
|
1969
|
+
profileB: row.profileB,
|
|
1970
|
+
score: row.score,
|
|
1971
|
+
breakdown: JSON.parse(row.breakdown),
|
|
1972
|
+
status: row.status,
|
|
1973
|
+
notifiedAt: row.notifiedAt ?? null,
|
|
1974
|
+
respondedAt: row.respondedAt ?? null,
|
|
1975
|
+
expiresAt: row.expiresAt,
|
|
1976
|
+
createdAt: row.createdAt,
|
|
1977
|
+
};
|
|
1978
|
+
}
|
|
1979
|
+
// ══════════════════════════════════════════════════════════
|
|
1980
|
+
// ── Organisms ──
|
|
1981
|
+
// ══════════════════════════════════════════════════════════
|
|
1982
|
+
async createOrganism(record) {
|
|
1983
|
+
this.db.prepare(`INSERT INTO organisms (id, name, description, type, location, interests, creatorGhii, admins,
|
|
1984
|
+
members, agentGaiis, boardId, joinPolicy, maxMembers, visibility, moderationConfig,
|
|
1985
|
+
memoryNamespace, semantic, createdAt, updatedAt)
|
|
1986
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(record.id, record.name, record.description, record.type, record.location ? JSON.stringify(record.location) : null, JSON.stringify(record.interests), record.creatorGhii, JSON.stringify(record.admins), JSON.stringify(record.members), JSON.stringify(record.agentGaiis), record.boardId, record.joinPolicy, record.maxMembers, record.visibility, JSON.stringify(record.moderationConfig), record.memoryNamespace, record.semantic ? JSON.stringify(record.semantic) : null, record.createdAt, record.updatedAt);
|
|
1987
|
+
return record;
|
|
1988
|
+
}
|
|
1989
|
+
async getOrganism(id) {
|
|
1990
|
+
const row = this.db.prepare('SELECT * FROM organisms WHERE id = ?').get(id);
|
|
1991
|
+
return row ? this.deserializeOrganism(row) : null;
|
|
1992
|
+
}
|
|
1993
|
+
async listOrganisms(opts) {
|
|
1994
|
+
const page = opts?.page ?? 1;
|
|
1995
|
+
const perPage = opts?.perPage ?? 20;
|
|
1996
|
+
const rows = this.db.prepare('SELECT * FROM organisms ORDER BY createdAt DESC').all();
|
|
1997
|
+
let results = rows.map(r => this.deserializeOrganism(r));
|
|
1998
|
+
if (opts?.type)
|
|
1999
|
+
results = results.filter(o => o.type === opts.type);
|
|
2000
|
+
if (opts?.city)
|
|
2001
|
+
results = results.filter(o => o.location?.city?.toLowerCase() === opts.city.toLowerCase());
|
|
2002
|
+
if (opts?.interest)
|
|
2003
|
+
results = results.filter(o => o.interests.some(i => i.toLowerCase() === opts.interest.toLowerCase()));
|
|
2004
|
+
if (opts?.member)
|
|
2005
|
+
results = results.filter(o => o.members.includes(opts.member));
|
|
2006
|
+
if (opts?.visibility)
|
|
2007
|
+
results = results.filter(o => o.visibility === opts.visibility);
|
|
2008
|
+
const start = (page - 1) * perPage;
|
|
2009
|
+
return results.slice(start, start + perPage);
|
|
2010
|
+
}
|
|
2011
|
+
async updateOrganism(id, updates) {
|
|
2012
|
+
const existing = await this.getOrganism(id);
|
|
2013
|
+
if (!existing)
|
|
2014
|
+
return null;
|
|
2015
|
+
const updated = { ...existing, ...updates, id: existing.id };
|
|
2016
|
+
this.db.prepare(`UPDATE organisms SET name = ?, description = ?, type = ?, location = ?, interests = ?,
|
|
2017
|
+
creatorGhii = ?, admins = ?, members = ?, agentGaiis = ?, boardId = ?,
|
|
2018
|
+
joinPolicy = ?, maxMembers = ?, visibility = ?, moderationConfig = ?,
|
|
2019
|
+
memoryNamespace = ?, semantic = ?, createdAt = ?, updatedAt = ? WHERE id = ?`).run(updated.name, updated.description, updated.type, updated.location ? JSON.stringify(updated.location) : null, JSON.stringify(updated.interests), updated.creatorGhii, JSON.stringify(updated.admins), JSON.stringify(updated.members), JSON.stringify(updated.agentGaiis), updated.boardId, updated.joinPolicy, updated.maxMembers, updated.visibility, JSON.stringify(updated.moderationConfig), updated.memoryNamespace, updated.semantic ? JSON.stringify(updated.semantic) : null, updated.createdAt, updated.updatedAt, id);
|
|
2020
|
+
return updated;
|
|
2021
|
+
}
|
|
2022
|
+
async deleteOrganism(id) {
|
|
2023
|
+
const txn = this.db.transaction(() => {
|
|
2024
|
+
// Get the organism to find its boardId and memoryNamespace
|
|
2025
|
+
const org = this.db.prepare('SELECT boardId, memoryNamespace FROM organisms WHERE id = ?').get(id);
|
|
2026
|
+
// Cascade: delete memberships and join requests
|
|
2027
|
+
this.db.prepare('DELETE FROM organism_memberships WHERE organismId = ?').run(id);
|
|
2028
|
+
this.db.prepare('DELETE FROM join_requests WHERE organismId = ?').run(id);
|
|
2029
|
+
// Cascade: delete organism reputation
|
|
2030
|
+
this.db.prepare('DELETE FROM organism_reputations WHERE organismId = ?').run(id);
|
|
2031
|
+
if (org) {
|
|
2032
|
+
// Cascade: delete the organism's board and its posts/subscriptions
|
|
2033
|
+
this.db.prepare('DELETE FROM board_posts WHERE boardId = ?').run(org.boardId);
|
|
2034
|
+
this.db.prepare('DELETE FROM board_subscriptions WHERE boardId = ?').run(org.boardId);
|
|
2035
|
+
this.db.prepare('DELETE FROM boards WHERE id = ?').run(org.boardId);
|
|
2036
|
+
// Cascade: delete memory entries under the organism's namespace
|
|
2037
|
+
this.db.prepare('DELETE FROM memory WHERE ownerGaii = ?').run(org.memoryNamespace);
|
|
2038
|
+
}
|
|
2039
|
+
const result = this.db.prepare('DELETE FROM organisms WHERE id = ?').run(id);
|
|
2040
|
+
return result.changes > 0;
|
|
2041
|
+
});
|
|
2042
|
+
return txn();
|
|
2043
|
+
}
|
|
2044
|
+
deserializeOrganism(row) {
|
|
2045
|
+
const record = {
|
|
2046
|
+
id: row.id,
|
|
2047
|
+
name: row.name,
|
|
2048
|
+
description: row.description,
|
|
2049
|
+
type: row.type,
|
|
2050
|
+
interests: JSON.parse(row.interests),
|
|
2051
|
+
creatorGhii: row.creatorGhii,
|
|
2052
|
+
admins: JSON.parse(row.admins),
|
|
2053
|
+
members: JSON.parse(row.members),
|
|
2054
|
+
agentGaiis: JSON.parse(row.agentGaiis),
|
|
2055
|
+
boardId: row.boardId,
|
|
2056
|
+
joinPolicy: row.joinPolicy,
|
|
2057
|
+
maxMembers: row.maxMembers,
|
|
2058
|
+
visibility: row.visibility,
|
|
2059
|
+
moderationConfig: JSON.parse(row.moderationConfig),
|
|
2060
|
+
memoryNamespace: row.memoryNamespace,
|
|
2061
|
+
createdAt: row.createdAt,
|
|
2062
|
+
updatedAt: row.updatedAt,
|
|
2063
|
+
};
|
|
2064
|
+
if (row.location)
|
|
2065
|
+
record.location = JSON.parse(row.location);
|
|
2066
|
+
if (row.semantic)
|
|
2067
|
+
record.semantic = JSON.parse(row.semantic);
|
|
2068
|
+
return record;
|
|
2069
|
+
}
|
|
2070
|
+
// ══════════════════════════════════════════════════════════
|
|
2071
|
+
// ── Memberships ──
|
|
2072
|
+
// ══════════════════════════════════════════════════════════
|
|
2073
|
+
async createMembership(record) {
|
|
2074
|
+
this.db.prepare(`INSERT INTO organism_memberships (id, organismId, ghii, role, status, joinedAt, invitedBy)
|
|
2075
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`).run(record.id, record.organismId, record.ghii, record.role, record.status, record.joinedAt, record.invitedBy ?? null);
|
|
2076
|
+
return record;
|
|
2077
|
+
}
|
|
2078
|
+
async getMembership(organismId, ghii) {
|
|
2079
|
+
const row = this.db.prepare('SELECT * FROM organism_memberships WHERE organismId = ? AND ghii = ?').get(organismId, ghii);
|
|
2080
|
+
return row ? this.deserializeMembership(row) : null;
|
|
2081
|
+
}
|
|
2082
|
+
async listMembers(organismId, opts) {
|
|
2083
|
+
let sql = 'SELECT * FROM organism_memberships WHERE organismId = ?';
|
|
2084
|
+
const params = [organismId];
|
|
2085
|
+
if (opts?.role) {
|
|
2086
|
+
sql += ' AND role = ?';
|
|
2087
|
+
params.push(opts.role);
|
|
2088
|
+
}
|
|
2089
|
+
if (opts?.status) {
|
|
2090
|
+
sql += ' AND status = ?';
|
|
2091
|
+
params.push(opts.status);
|
|
2092
|
+
}
|
|
2093
|
+
const rows = this.db.prepare(sql).all(...params);
|
|
2094
|
+
return rows.map(r => this.deserializeMembership(r));
|
|
2095
|
+
}
|
|
2096
|
+
async listMembershipsByGhii(ghii) {
|
|
2097
|
+
const rows = this.db.prepare('SELECT * FROM organism_memberships WHERE ghii = ?').all(ghii);
|
|
2098
|
+
return rows.map(r => this.deserializeMembership(r));
|
|
2099
|
+
}
|
|
2100
|
+
async updateMembership(id, updates) {
|
|
2101
|
+
const row = this.db.prepare('SELECT * FROM organism_memberships WHERE id = ?').get(id);
|
|
2102
|
+
if (!row)
|
|
2103
|
+
return null;
|
|
2104
|
+
const existing = this.deserializeMembership(row);
|
|
2105
|
+
const updated = { ...existing, ...updates, id: existing.id };
|
|
2106
|
+
this.db.prepare(`UPDATE organism_memberships SET organismId = ?, ghii = ?, role = ?, status = ?, joinedAt = ?, invitedBy = ? WHERE id = ?`).run(updated.organismId, updated.ghii, updated.role, updated.status, updated.joinedAt, updated.invitedBy ?? null, id);
|
|
2107
|
+
return updated;
|
|
2108
|
+
}
|
|
2109
|
+
async deleteMembership(id) {
|
|
2110
|
+
const result = this.db.prepare('DELETE FROM organism_memberships WHERE id = ?').run(id);
|
|
2111
|
+
return result.changes > 0;
|
|
2112
|
+
}
|
|
2113
|
+
deserializeMembership(row) {
|
|
2114
|
+
const record = {
|
|
2115
|
+
id: row.id,
|
|
2116
|
+
organismId: row.organismId,
|
|
2117
|
+
ghii: row.ghii,
|
|
2118
|
+
role: row.role,
|
|
2119
|
+
status: row.status,
|
|
2120
|
+
joinedAt: row.joinedAt,
|
|
2121
|
+
};
|
|
2122
|
+
if (row.invitedBy)
|
|
2123
|
+
record.invitedBy = row.invitedBy;
|
|
2124
|
+
return record;
|
|
2125
|
+
}
|
|
2126
|
+
// ══════════════════════════════════════════════════════════
|
|
2127
|
+
// ── Join Requests ──
|
|
2128
|
+
// ══════════════════════════════════════════════════════════
|
|
2129
|
+
async createJoinRequest(record) {
|
|
2130
|
+
this.db.prepare(`INSERT INTO join_requests (id, organismId, ghii, message, status, reviewedBy, createdAt, reviewedAt)
|
|
2131
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`).run(record.id, record.organismId, record.ghii, record.message ?? null, record.status, record.reviewedBy ?? null, record.createdAt, record.reviewedAt ?? null);
|
|
2132
|
+
return record;
|
|
2133
|
+
}
|
|
2134
|
+
async getJoinRequest(id) {
|
|
2135
|
+
const row = this.db.prepare('SELECT * FROM join_requests WHERE id = ?').get(id);
|
|
2136
|
+
return row ? this.deserializeJoinRequest(row) : null;
|
|
2137
|
+
}
|
|
2138
|
+
async listJoinRequests(organismId, opts) {
|
|
2139
|
+
let sql = 'SELECT * FROM join_requests WHERE organismId = ?';
|
|
2140
|
+
const params = [organismId];
|
|
2141
|
+
if (opts?.status) {
|
|
2142
|
+
sql += ' AND status = ?';
|
|
2143
|
+
params.push(opts.status);
|
|
2144
|
+
}
|
|
2145
|
+
const rows = this.db.prepare(sql).all(...params);
|
|
2146
|
+
return rows.map(r => this.deserializeJoinRequest(r));
|
|
2147
|
+
}
|
|
2148
|
+
async updateJoinRequest(id, updates) {
|
|
2149
|
+
const existing = await this.getJoinRequest(id);
|
|
2150
|
+
if (!existing)
|
|
2151
|
+
return null;
|
|
2152
|
+
const updated = { ...existing, ...updates, id: existing.id };
|
|
2153
|
+
this.db.prepare(`UPDATE join_requests SET organismId = ?, ghii = ?, message = ?, status = ?,
|
|
2154
|
+
reviewedBy = ?, createdAt = ?, reviewedAt = ? WHERE id = ?`).run(updated.organismId, updated.ghii, updated.message ?? null, updated.status, updated.reviewedBy ?? null, updated.createdAt, updated.reviewedAt ?? null, id);
|
|
2155
|
+
return updated;
|
|
2156
|
+
}
|
|
2157
|
+
deserializeJoinRequest(row) {
|
|
2158
|
+
const record = {
|
|
2159
|
+
id: row.id,
|
|
2160
|
+
organismId: row.organismId,
|
|
2161
|
+
ghii: row.ghii,
|
|
2162
|
+
status: row.status,
|
|
2163
|
+
createdAt: row.createdAt,
|
|
2164
|
+
};
|
|
2165
|
+
if (row.message)
|
|
2166
|
+
record.message = row.message;
|
|
2167
|
+
if (row.reviewedBy)
|
|
2168
|
+
record.reviewedBy = row.reviewedBy;
|
|
2169
|
+
if (row.reviewedAt)
|
|
2170
|
+
record.reviewedAt = row.reviewedAt;
|
|
2171
|
+
return record;
|
|
2172
|
+
}
|
|
2173
|
+
// ══════════════════════════════════════════════════════════
|
|
2174
|
+
// ── Appeals ──
|
|
2175
|
+
// ══════════════════════════════════════════════════════════
|
|
2176
|
+
async createAppeal(record) {
|
|
2177
|
+
this.db.prepare(`INSERT INTO appeals (id, flagId, appealedBy, reason, status, reviewedBy, reviewNote, createdAt, reviewedAt)
|
|
2178
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(record.id, record.flagId, record.appealedBy, record.reason, record.status, record.reviewedBy ?? null, record.reviewNote ?? null, record.createdAt, record.reviewedAt ?? null);
|
|
2179
|
+
return record;
|
|
2180
|
+
}
|
|
2181
|
+
async getAppeal(id) {
|
|
2182
|
+
const row = this.db.prepare('SELECT * FROM appeals WHERE id = ?').get(id);
|
|
2183
|
+
return row ? this.deserializeAppeal(row) : null;
|
|
2184
|
+
}
|
|
2185
|
+
async getAppealByFlagId(flagId) {
|
|
2186
|
+
const row = this.db.prepare('SELECT * FROM appeals WHERE flagId = ?').get(flagId);
|
|
2187
|
+
return row ? this.deserializeAppeal(row) : null;
|
|
2188
|
+
}
|
|
2189
|
+
async listAppeals(opts) {
|
|
2190
|
+
const page = opts?.page ?? 1;
|
|
2191
|
+
const perPage = opts?.perPage ?? 20;
|
|
2192
|
+
let sql = 'SELECT * FROM appeals WHERE 1=1';
|
|
2193
|
+
const params = [];
|
|
2194
|
+
if (opts?.status) {
|
|
2195
|
+
sql += ' AND status = ?';
|
|
2196
|
+
params.push(opts.status);
|
|
2197
|
+
}
|
|
2198
|
+
sql += ' ORDER BY createdAt DESC LIMIT ? OFFSET ?';
|
|
2199
|
+
params.push(perPage, (page - 1) * perPage);
|
|
2200
|
+
const rows = this.db.prepare(sql).all(...params);
|
|
2201
|
+
return rows.map(r => this.deserializeAppeal(r));
|
|
2202
|
+
}
|
|
2203
|
+
async updateAppeal(id, updates) {
|
|
2204
|
+
const existing = await this.getAppeal(id);
|
|
2205
|
+
if (!existing)
|
|
2206
|
+
return null;
|
|
2207
|
+
const updated = { ...existing, ...updates, id: existing.id };
|
|
2208
|
+
this.db.prepare(`UPDATE appeals SET flagId = ?, appealedBy = ?, reason = ?, status = ?,
|
|
2209
|
+
reviewedBy = ?, reviewNote = ?, createdAt = ?, reviewedAt = ? WHERE id = ?`).run(updated.flagId, updated.appealedBy, updated.reason, updated.status, updated.reviewedBy ?? null, updated.reviewNote ?? null, updated.createdAt, updated.reviewedAt ?? null, id);
|
|
2210
|
+
return updated;
|
|
2211
|
+
}
|
|
2212
|
+
deserializeAppeal(row) {
|
|
2213
|
+
const record = {
|
|
2214
|
+
id: row.id,
|
|
2215
|
+
flagId: row.flagId,
|
|
2216
|
+
appealedBy: row.appealedBy,
|
|
2217
|
+
reason: row.reason,
|
|
2218
|
+
status: row.status,
|
|
2219
|
+
createdAt: row.createdAt,
|
|
2220
|
+
};
|
|
2221
|
+
if (row.reviewedBy)
|
|
2222
|
+
record.reviewedBy = row.reviewedBy;
|
|
2223
|
+
if (row.reviewNote)
|
|
2224
|
+
record.reviewNote = row.reviewNote;
|
|
2225
|
+
if (row.reviewedAt)
|
|
2226
|
+
record.reviewedAt = row.reviewedAt;
|
|
2227
|
+
return record;
|
|
2228
|
+
}
|
|
2229
|
+
// ══════════════════════════════════════════════════════════
|
|
2230
|
+
// ── Marketplace ──
|
|
2231
|
+
// ══════════════════════════════════════════════════════════
|
|
2232
|
+
async createListing(record) {
|
|
2233
|
+
this.db.prepare(`INSERT INTO listings (id, ownerName, sellerGhii, title, description, category, priceMorsels,
|
|
2234
|
+
condition, availability, location, tags, images, status, memoryKey, flagCount, createdAt, updatedAt, semantic)
|
|
2235
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(record.id, record.ownerName, record.sellerGhii, record.title, record.description, record.category, record.priceMorsels, record.condition ?? null, record.availability ?? null, record.location ? JSON.stringify(record.location) : null, record.tags ? JSON.stringify(record.tags) : null, record.images ? JSON.stringify(record.images) : null, record.status, record.memoryKey, record.flagCount, record.createdAt, record.updatedAt, record.semantic ? JSON.stringify(record.semantic) : null);
|
|
2236
|
+
return record;
|
|
2237
|
+
}
|
|
2238
|
+
async getListing(id) {
|
|
2239
|
+
const row = this.db.prepare('SELECT * FROM listings WHERE id = ?').get(id);
|
|
2240
|
+
return row ? this.deserializeListing(row) : null;
|
|
2241
|
+
}
|
|
2242
|
+
async listListings(opts) {
|
|
2243
|
+
const page = opts?.page ?? 1;
|
|
2244
|
+
const perPage = opts?.perPage ?? 20;
|
|
2245
|
+
const rows = this.db.prepare('SELECT * FROM listings ORDER BY createdAt DESC').all();
|
|
2246
|
+
let results = rows.map(r => this.deserializeListing(r));
|
|
2247
|
+
if (opts?.category)
|
|
2248
|
+
results = results.filter(l => l.category === opts.category);
|
|
2249
|
+
if (opts?.city)
|
|
2250
|
+
results = results.filter(l => l.location?.city?.toLowerCase() === opts.city.toLowerCase());
|
|
2251
|
+
if (opts?.minPrice !== undefined)
|
|
2252
|
+
results = results.filter(l => l.priceMorsels >= opts.minPrice);
|
|
2253
|
+
if (opts?.maxPrice !== undefined)
|
|
2254
|
+
results = results.filter(l => l.priceMorsels <= opts.maxPrice);
|
|
2255
|
+
if (opts?.status)
|
|
2256
|
+
results = results.filter(l => l.status === opts.status);
|
|
2257
|
+
if (opts?.sellerOwner)
|
|
2258
|
+
results = results.filter(l => l.ownerName === opts.sellerOwner);
|
|
2259
|
+
const start = (page - 1) * perPage;
|
|
2260
|
+
return results.slice(start, start + perPage);
|
|
2261
|
+
}
|
|
2262
|
+
async updateListing(id, updates) {
|
|
2263
|
+
const existing = await this.getListing(id);
|
|
2264
|
+
if (!existing)
|
|
2265
|
+
return null;
|
|
2266
|
+
const updated = { ...existing, ...updates, id: existing.id };
|
|
2267
|
+
this.db.prepare(`UPDATE listings SET ownerName = ?, sellerGhii = ?, title = ?, description = ?,
|
|
2268
|
+
category = ?, priceMorsels = ?, condition = ?, availability = ?, location = ?,
|
|
2269
|
+
tags = ?, images = ?, status = ?, memoryKey = ?, flagCount = ?,
|
|
2270
|
+
createdAt = ?, updatedAt = ?, semantic = ? WHERE id = ?`).run(updated.ownerName, updated.sellerGhii, updated.title, updated.description, updated.category, updated.priceMorsels, updated.condition ?? null, updated.availability ?? null, updated.location ? JSON.stringify(updated.location) : null, updated.tags ? JSON.stringify(updated.tags) : null, updated.images ? JSON.stringify(updated.images) : null, updated.status, updated.memoryKey, updated.flagCount, updated.createdAt, updated.updatedAt, updated.semantic ? JSON.stringify(updated.semantic) : null, id);
|
|
2271
|
+
return updated;
|
|
2272
|
+
}
|
|
2273
|
+
async deleteListing(id) {
|
|
2274
|
+
const result = this.db.prepare('DELETE FROM listings WHERE id = ?').run(id);
|
|
2275
|
+
return result.changes > 0;
|
|
2276
|
+
}
|
|
2277
|
+
async createPurchase(record) {
|
|
2278
|
+
this.db.prepare(`INSERT INTO purchases (id, listingId, buyerOwner, sellerOwner, priceMorsels,
|
|
2279
|
+
transactionFeeMorsels, totalCostMorsels, status, rating, trackingCode, createdAt, completedAt)
|
|
2280
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(record.id, record.listingId, record.buyerOwner, record.sellerOwner, record.priceMorsels, record.transactionFeeMorsels, record.totalCostMorsels, record.status, record.rating ? JSON.stringify(record.rating) : null, record.trackingCode, record.createdAt, record.completedAt ?? null);
|
|
2281
|
+
return record;
|
|
2282
|
+
}
|
|
2283
|
+
async getPurchase(id) {
|
|
2284
|
+
const row = this.db.prepare('SELECT * FROM purchases WHERE id = ?').get(id);
|
|
2285
|
+
return row ? this.deserializePurchase(row) : null;
|
|
2286
|
+
}
|
|
2287
|
+
async listPurchasesByBuyer(buyerOwner) {
|
|
2288
|
+
const rows = this.db.prepare('SELECT * FROM purchases WHERE buyerOwner = ? ORDER BY createdAt DESC').all(buyerOwner);
|
|
2289
|
+
return rows.map(r => this.deserializePurchase(r));
|
|
2290
|
+
}
|
|
2291
|
+
async listPurchasesBySeller(sellerOwner) {
|
|
2292
|
+
const rows = this.db.prepare('SELECT * FROM purchases WHERE sellerOwner = ? ORDER BY createdAt DESC').all(sellerOwner);
|
|
2293
|
+
return rows.map(r => this.deserializePurchase(r));
|
|
2294
|
+
}
|
|
2295
|
+
async updatePurchase(id, updates) {
|
|
2296
|
+
const existing = await this.getPurchase(id);
|
|
2297
|
+
if (!existing)
|
|
2298
|
+
return null;
|
|
2299
|
+
const updated = { ...existing, ...updates, id: existing.id };
|
|
2300
|
+
this.db.prepare(`UPDATE purchases SET listingId = ?, buyerOwner = ?, sellerOwner = ?, priceMorsels = ?,
|
|
2301
|
+
transactionFeeMorsels = ?, totalCostMorsels = ?, status = ?, rating = ?,
|
|
2302
|
+
trackingCode = ?, createdAt = ?, completedAt = ? WHERE id = ?`).run(updated.listingId, updated.buyerOwner, updated.sellerOwner, updated.priceMorsels, updated.transactionFeeMorsels, updated.totalCostMorsels, updated.status, updated.rating ? JSON.stringify(updated.rating) : null, updated.trackingCode, updated.createdAt, updated.completedAt ?? null, id);
|
|
2303
|
+
return updated;
|
|
2304
|
+
}
|
|
2305
|
+
deserializeListing(row) {
|
|
2306
|
+
const record = {
|
|
2307
|
+
id: row.id,
|
|
2308
|
+
ownerName: row.ownerName,
|
|
2309
|
+
sellerGhii: row.sellerGhii,
|
|
2310
|
+
title: row.title,
|
|
2311
|
+
description: row.description,
|
|
2312
|
+
category: row.category,
|
|
2313
|
+
priceMorsels: row.priceMorsels,
|
|
2314
|
+
status: row.status,
|
|
2315
|
+
memoryKey: row.memoryKey,
|
|
2316
|
+
flagCount: row.flagCount,
|
|
2317
|
+
createdAt: row.createdAt,
|
|
2318
|
+
updatedAt: row.updatedAt,
|
|
2319
|
+
};
|
|
2320
|
+
if (row.condition)
|
|
2321
|
+
record.condition = row.condition;
|
|
2322
|
+
if (row.availability)
|
|
2323
|
+
record.availability = row.availability;
|
|
2324
|
+
if (row.location)
|
|
2325
|
+
record.location = JSON.parse(row.location);
|
|
2326
|
+
if (row.tags)
|
|
2327
|
+
record.tags = JSON.parse(row.tags);
|
|
2328
|
+
if (row.images)
|
|
2329
|
+
record.images = JSON.parse(row.images);
|
|
2330
|
+
if (row.semantic)
|
|
2331
|
+
record.semantic = JSON.parse(row.semantic);
|
|
2332
|
+
return record;
|
|
2333
|
+
}
|
|
2334
|
+
deserializePurchase(row) {
|
|
2335
|
+
const record = {
|
|
2336
|
+
id: row.id,
|
|
2337
|
+
listingId: row.listingId,
|
|
2338
|
+
buyerOwner: row.buyerOwner,
|
|
2339
|
+
sellerOwner: row.sellerOwner,
|
|
2340
|
+
priceMorsels: row.priceMorsels,
|
|
2341
|
+
transactionFeeMorsels: row.transactionFeeMorsels,
|
|
2342
|
+
totalCostMorsels: row.totalCostMorsels,
|
|
2343
|
+
status: row.status,
|
|
2344
|
+
trackingCode: row.trackingCode,
|
|
2345
|
+
createdAt: row.createdAt,
|
|
2346
|
+
};
|
|
2347
|
+
if (row.rating)
|
|
2348
|
+
record.rating = JSON.parse(row.rating);
|
|
2349
|
+
if (row.completedAt)
|
|
2350
|
+
record.completedAt = row.completedAt;
|
|
2351
|
+
return record;
|
|
2352
|
+
}
|
|
2353
|
+
// ══════════════════════════════════════════════════════════
|
|
2354
|
+
// ── Push Subscriptions ──
|
|
2355
|
+
// ══════════════════════════════════════════════════════════
|
|
2356
|
+
async createPushSubscription(record) {
|
|
2357
|
+
this.db.prepare(`INSERT OR REPLACE INTO push_subscriptions (ownerName, endpoint, keys, createdAt, lastUsedAt)
|
|
2358
|
+
VALUES (?, ?, ?, ?, ?)`).run(record.ownerName, record.endpoint, JSON.stringify(record.keys), record.createdAt, record.lastUsedAt);
|
|
2359
|
+
return record;
|
|
2360
|
+
}
|
|
2361
|
+
async getPushSubscription(ownerName) {
|
|
2362
|
+
const row = this.db.prepare('SELECT * FROM push_subscriptions WHERE ownerName = ?').get(ownerName);
|
|
2363
|
+
if (!row)
|
|
2364
|
+
return null;
|
|
2365
|
+
return {
|
|
2366
|
+
ownerName: row.ownerName,
|
|
2367
|
+
endpoint: row.endpoint,
|
|
2368
|
+
keys: JSON.parse(row.keys),
|
|
2369
|
+
createdAt: row.createdAt,
|
|
2370
|
+
lastUsedAt: row.lastUsedAt,
|
|
2371
|
+
};
|
|
2372
|
+
}
|
|
2373
|
+
async deletePushSubscription(ownerName) {
|
|
2374
|
+
const result = this.db.prepare('DELETE FROM push_subscriptions WHERE ownerName = ?').run(ownerName);
|
|
2375
|
+
return result.changes > 0;
|
|
2376
|
+
}
|
|
2377
|
+
async listPushSubscriptions() {
|
|
2378
|
+
const rows = this.db.prepare('SELECT * FROM push_subscriptions').all();
|
|
2379
|
+
return rows.map(r => ({
|
|
2380
|
+
ownerName: r.ownerName,
|
|
2381
|
+
endpoint: r.endpoint,
|
|
2382
|
+
keys: JSON.parse(r.keys),
|
|
2383
|
+
createdAt: r.createdAt,
|
|
2384
|
+
lastUsedAt: r.lastUsedAt,
|
|
2385
|
+
}));
|
|
2386
|
+
}
|
|
2387
|
+
// ══════════════════════════════════════════════════════════
|
|
2388
|
+
// ── Trusted Issuers ──
|
|
2389
|
+
// ══════════════════════════════════════════════════════════
|
|
2390
|
+
async createTrustedIssuer(record) {
|
|
2391
|
+
this.db.prepare(`INSERT INTO trusted_issuers (id, name, url, publicKey, type, trusted, addedBy, createdAt)
|
|
2392
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`).run(record.id, record.name, record.url, record.publicKey, record.type, record.trusted ? 1 : 0, record.addedBy, record.createdAt);
|
|
2393
|
+
return record;
|
|
2394
|
+
}
|
|
2395
|
+
async getTrustedIssuer(id) {
|
|
2396
|
+
const row = this.db.prepare('SELECT * FROM trusted_issuers WHERE id = ?').get(id);
|
|
2397
|
+
return row ? this.deserializeTrustedIssuer(row) : null;
|
|
2398
|
+
}
|
|
2399
|
+
async getTrustedIssuerByUrl(url) {
|
|
2400
|
+
const row = this.db.prepare('SELECT * FROM trusted_issuers WHERE url = ?').get(url);
|
|
2401
|
+
return row ? this.deserializeTrustedIssuer(row) : null;
|
|
2402
|
+
}
|
|
2403
|
+
async listTrustedIssuers(opts) {
|
|
2404
|
+
let sql = 'SELECT * FROM trusted_issuers';
|
|
2405
|
+
const params = [];
|
|
2406
|
+
if (opts?.type) {
|
|
2407
|
+
sql += ' WHERE type = ?';
|
|
2408
|
+
params.push(opts.type);
|
|
2409
|
+
}
|
|
2410
|
+
const rows = this.db.prepare(sql).all(...params);
|
|
2411
|
+
return rows.map(r => this.deserializeTrustedIssuer(r));
|
|
2412
|
+
}
|
|
2413
|
+
async deleteTrustedIssuer(id) {
|
|
2414
|
+
const result = this.db.prepare('DELETE FROM trusted_issuers WHERE id = ?').run(id);
|
|
2415
|
+
return result.changes > 0;
|
|
2416
|
+
}
|
|
2417
|
+
deserializeTrustedIssuer(row) {
|
|
2418
|
+
return {
|
|
2419
|
+
id: row.id,
|
|
2420
|
+
name: row.name,
|
|
2421
|
+
url: row.url,
|
|
2422
|
+
publicKey: row.publicKey,
|
|
2423
|
+
type: row.type,
|
|
2424
|
+
trusted: row.trusted === 1,
|
|
2425
|
+
addedBy: row.addedBy,
|
|
2426
|
+
createdAt: row.createdAt,
|
|
2427
|
+
};
|
|
2428
|
+
}
|
|
2429
|
+
// ══════════════════════════════════════════════════════════
|
|
2430
|
+
// ── Genesis Peers ──
|
|
2431
|
+
// ══════════════════════════════════════════════════════════
|
|
2432
|
+
async createGenesisPeer(record) {
|
|
2433
|
+
this.db.prepare(`INSERT INTO genesis_peers (id, genesisNodeId, genesisUrl, publicKey, status, lastSyncAt, catalogueHash, createdAt, updatedAt)
|
|
2434
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(record.id, record.genesisNodeId, record.genesisUrl, record.publicKey, record.status, record.lastSyncAt, record.catalogueHash, record.createdAt, record.updatedAt);
|
|
2435
|
+
return record;
|
|
2436
|
+
}
|
|
2437
|
+
async getGenesisPeer(id) {
|
|
2438
|
+
const row = this.db.prepare('SELECT * FROM genesis_peers WHERE id = ?').get(id);
|
|
2439
|
+
return row ? this.deserializeGenesisPeer(row) : null;
|
|
2440
|
+
}
|
|
2441
|
+
async getGenesisPeerByNodeId(nodeId) {
|
|
2442
|
+
const row = this.db.prepare('SELECT * FROM genesis_peers WHERE genesisNodeId = ?').get(nodeId);
|
|
2443
|
+
return row ? this.deserializeGenesisPeer(row) : null;
|
|
2444
|
+
}
|
|
2445
|
+
async listGenesisPeers(opts) {
|
|
2446
|
+
let sql = 'SELECT * FROM genesis_peers';
|
|
2447
|
+
const params = [];
|
|
2448
|
+
if (opts?.status) {
|
|
2449
|
+
sql += ' WHERE status = ?';
|
|
2450
|
+
params.push(opts.status);
|
|
2451
|
+
}
|
|
2452
|
+
const rows = this.db.prepare(sql).all(...params);
|
|
2453
|
+
return rows.map(r => this.deserializeGenesisPeer(r));
|
|
2454
|
+
}
|
|
2455
|
+
async updateGenesisPeer(id, updates) {
|
|
2456
|
+
const existing = await this.getGenesisPeer(id);
|
|
2457
|
+
if (!existing)
|
|
2458
|
+
return null;
|
|
2459
|
+
const updated = { ...existing, ...updates };
|
|
2460
|
+
this.db.prepare(`UPDATE genesis_peers SET genesisNodeId = ?, genesisUrl = ?, publicKey = ?, status = ?,
|
|
2461
|
+
lastSyncAt = ?, catalogueHash = ?, createdAt = ?, updatedAt = ? WHERE id = ?`).run(updated.genesisNodeId, updated.genesisUrl, updated.publicKey, updated.status, updated.lastSyncAt, updated.catalogueHash, updated.createdAt, updated.updatedAt, id);
|
|
2462
|
+
return updated;
|
|
2463
|
+
}
|
|
2464
|
+
async deleteGenesisPeer(id) {
|
|
2465
|
+
const result = this.db.prepare('DELETE FROM genesis_peers WHERE id = ?').run(id);
|
|
2466
|
+
return result.changes > 0;
|
|
2467
|
+
}
|
|
2468
|
+
deserializeGenesisPeer(row) {
|
|
2469
|
+
return {
|
|
2470
|
+
id: row.id,
|
|
2471
|
+
genesisNodeId: row.genesisNodeId,
|
|
2472
|
+
genesisUrl: row.genesisUrl,
|
|
2473
|
+
publicKey: row.publicKey,
|
|
2474
|
+
status: row.status,
|
|
2475
|
+
lastSyncAt: row.lastSyncAt,
|
|
2476
|
+
catalogueHash: row.catalogueHash,
|
|
2477
|
+
createdAt: row.createdAt,
|
|
2478
|
+
updatedAt: row.updatedAt,
|
|
2479
|
+
};
|
|
2480
|
+
}
|
|
2481
|
+
// ══════════════════════════════════════════════════════════
|
|
2482
|
+
// ── Organism Reputation ──
|
|
2483
|
+
// ══════════════════════════════════════════════════════════
|
|
2484
|
+
async setOrganismReputation(record) {
|
|
2485
|
+
this.db.prepare(`INSERT OR REPLACE INTO organism_reputations (organismId, score, breakdown, calculatedAt)
|
|
2486
|
+
VALUES (?, ?, ?, ?)`).run(record.organismId, record.score, JSON.stringify(record.breakdown), record.calculatedAt);
|
|
2487
|
+
return record;
|
|
2488
|
+
}
|
|
2489
|
+
async getOrganismReputation(organismId) {
|
|
2490
|
+
const row = this.db.prepare('SELECT * FROM organism_reputations WHERE organismId = ?').get(organismId);
|
|
2491
|
+
if (!row)
|
|
2492
|
+
return null;
|
|
2493
|
+
return {
|
|
2494
|
+
organismId: row.organismId,
|
|
2495
|
+
score: row.score,
|
|
2496
|
+
breakdown: JSON.parse(row.breakdown),
|
|
2497
|
+
calculatedAt: row.calculatedAt,
|
|
2498
|
+
};
|
|
2499
|
+
}
|
|
2500
|
+
// ══════════════════════════════════════════════════════════
|
|
2501
|
+
// ── Realtime Rooms ──
|
|
2502
|
+
// ══════════════════════════════════════════════════════════
|
|
2503
|
+
async createRealtimeRoom(room) {
|
|
2504
|
+
this.db.prepare(`INSERT INTO realtime_rooms (id, appType, name, createdBy, maxPeers, isPublic, tags, peerCount, createdAt, lastActivityAt)
|
|
2505
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(room.id, room.appType, room.name, room.createdBy, room.maxPeers, room.isPublic ? 1 : 0, JSON.stringify(room.tags), room.peerCount, room.createdAt, room.lastActivityAt);
|
|
2506
|
+
return room;
|
|
2507
|
+
}
|
|
2508
|
+
async getRealtimeRoom(id) {
|
|
2509
|
+
const row = this.db.prepare('SELECT * FROM realtime_rooms WHERE id = ?').get(id);
|
|
2510
|
+
return row ? this.deserializeRealtimeRoom(row) : null;
|
|
2511
|
+
}
|
|
2512
|
+
async listRealtimeRooms(filter) {
|
|
2513
|
+
let sql = 'SELECT * FROM realtime_rooms WHERE 1=1';
|
|
2514
|
+
const params = [];
|
|
2515
|
+
if (filter?.appType) {
|
|
2516
|
+
sql += ' AND appType = ?';
|
|
2517
|
+
params.push(filter.appType);
|
|
2518
|
+
}
|
|
2519
|
+
if (filter?.isPublic !== undefined) {
|
|
2520
|
+
sql += ' AND isPublic = ?';
|
|
2521
|
+
params.push(filter.isPublic ? 1 : 0);
|
|
2522
|
+
}
|
|
2523
|
+
const rows = this.db.prepare(sql).all(...params);
|
|
2524
|
+
return rows.map(r => this.deserializeRealtimeRoom(r));
|
|
2525
|
+
}
|
|
2526
|
+
async updateRealtimeRoom(id, updates) {
|
|
2527
|
+
const existing = await this.getRealtimeRoom(id);
|
|
2528
|
+
if (!existing)
|
|
2529
|
+
return null;
|
|
2530
|
+
const updated = { ...existing, ...updates };
|
|
2531
|
+
this.db.prepare(`UPDATE realtime_rooms SET appType = ?, name = ?, createdBy = ?, maxPeers = ?,
|
|
2532
|
+
isPublic = ?, tags = ?, peerCount = ?, createdAt = ?, lastActivityAt = ? WHERE id = ?`).run(updated.appType, updated.name, updated.createdBy, updated.maxPeers, updated.isPublic ? 1 : 0, JSON.stringify(updated.tags), updated.peerCount, updated.createdAt, updated.lastActivityAt, id);
|
|
2533
|
+
return updated;
|
|
2534
|
+
}
|
|
2535
|
+
async deleteRealtimeRoom(id) {
|
|
2536
|
+
const result = this.db.prepare('DELETE FROM realtime_rooms WHERE id = ?').run(id);
|
|
2537
|
+
return result.changes > 0;
|
|
2538
|
+
}
|
|
2539
|
+
deserializeRealtimeRoom(row) {
|
|
2540
|
+
return {
|
|
2541
|
+
id: row.id,
|
|
2542
|
+
appType: row.appType,
|
|
2543
|
+
name: row.name,
|
|
2544
|
+
createdBy: row.createdBy,
|
|
2545
|
+
maxPeers: row.maxPeers,
|
|
2546
|
+
isPublic: row.isPublic === 1,
|
|
2547
|
+
tags: JSON.parse(row.tags),
|
|
2548
|
+
peerCount: row.peerCount,
|
|
2549
|
+
createdAt: row.createdAt,
|
|
2550
|
+
lastActivityAt: row.lastActivityAt,
|
|
2551
|
+
};
|
|
2552
|
+
}
|
|
2553
|
+
// ══════════════════════════════════════════════════════════
|
|
2554
|
+
// ── Site Change Log ──
|
|
2555
|
+
// ══════════════════════════════════════════════════════════
|
|
2556
|
+
async addSiteChangeLog(entry) {
|
|
2557
|
+
this.db.prepare(`INSERT INTO site_changelog (id, action, summary, changedBy, changedAt) VALUES (?, ?, ?, ?, ?)`).run(entry.id, entry.action, entry.summary, entry.changedBy, entry.changedAt);
|
|
2558
|
+
// Keep at most 200 entries (delete oldest beyond 200)
|
|
2559
|
+
this.db.prepare(`DELETE FROM site_changelog WHERE id NOT IN (SELECT id FROM site_changelog ORDER BY changedAt DESC LIMIT 200)`).run();
|
|
2560
|
+
return entry;
|
|
2561
|
+
}
|
|
2562
|
+
async listSiteChangeLog(limit, cursor) {
|
|
2563
|
+
const sql = 'SELECT * FROM site_changelog ORDER BY changedAt DESC';
|
|
2564
|
+
const params = [];
|
|
2565
|
+
const allRows = this.db.prepare(sql).all(...params);
|
|
2566
|
+
let entries = allRows.map(r => ({
|
|
2567
|
+
id: r.id,
|
|
2568
|
+
action: r.action,
|
|
2569
|
+
summary: r.summary,
|
|
2570
|
+
changedBy: r.changedBy,
|
|
2571
|
+
changedAt: r.changedAt,
|
|
2572
|
+
}));
|
|
2573
|
+
if (cursor) {
|
|
2574
|
+
const idx = entries.findIndex(e => e.id === cursor);
|
|
2575
|
+
if (idx >= 0)
|
|
2576
|
+
entries = entries.slice(idx + 1);
|
|
2577
|
+
}
|
|
2578
|
+
return entries.slice(0, limit);
|
|
2579
|
+
}
|
|
2580
|
+
// ══════════════════════════════════════════════════════════
|
|
2581
|
+
// ── Extensions ──
|
|
2582
|
+
// ══════════════════════════════════════════════════════════
|
|
2583
|
+
async createExtension(record) {
|
|
2584
|
+
try {
|
|
2585
|
+
this.db.prepare(`INSERT INTO extensions (name, version, description, author, status, requiredApis,
|
|
2586
|
+
actions, config, limits, federation, instances, installedBy, installedAt, activatedAt)
|
|
2587
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(record.name, record.version, record.description, record.author, record.status, JSON.stringify(record.requiredApis), JSON.stringify(record.actions), JSON.stringify(record.config), JSON.stringify(record.limits), JSON.stringify(record.federation), record.instances ? JSON.stringify(record.instances) : null, record.installedBy, record.installedAt, record.activatedAt ?? null);
|
|
2588
|
+
return record;
|
|
2589
|
+
}
|
|
2590
|
+
catch (err) {
|
|
2591
|
+
if (err instanceof Error && err.message?.includes('UNIQUE constraint failed')) {
|
|
2592
|
+
throw new Error(`Extension "${record.name}" already exists`, { cause: err });
|
|
2593
|
+
}
|
|
2594
|
+
throw err;
|
|
2595
|
+
}
|
|
2596
|
+
}
|
|
2597
|
+
async getExtension(name) {
|
|
2598
|
+
const row = this.db.prepare('SELECT * FROM extensions WHERE name = ?').get(name);
|
|
2599
|
+
return row ? this.deserializeExtension(row) : null;
|
|
2600
|
+
}
|
|
2601
|
+
async listExtensions(opts) {
|
|
2602
|
+
let sql = 'SELECT * FROM extensions';
|
|
2603
|
+
const params = [];
|
|
2604
|
+
if (opts?.status) {
|
|
2605
|
+
sql += ' WHERE status = ?';
|
|
2606
|
+
params.push(opts.status);
|
|
2607
|
+
}
|
|
2608
|
+
const rows = this.db.prepare(sql).all(...params);
|
|
2609
|
+
return rows.map(r => this.deserializeExtension(r));
|
|
2610
|
+
}
|
|
2611
|
+
async updateExtension(name, updates) {
|
|
2612
|
+
const existing = await this.getExtension(name);
|
|
2613
|
+
if (!existing)
|
|
2614
|
+
return null;
|
|
2615
|
+
const updated = { ...existing, ...updates };
|
|
2616
|
+
this.db.prepare(`UPDATE extensions SET version = ?, description = ?, author = ?, status = ?,
|
|
2617
|
+
requiredApis = ?, actions = ?, config = ?, limits = ?, federation = ?,
|
|
2618
|
+
instances = ?, installedBy = ?, installedAt = ?, activatedAt = ? WHERE name = ?`).run(updated.version, updated.description, updated.author, updated.status, JSON.stringify(updated.requiredApis), JSON.stringify(updated.actions), JSON.stringify(updated.config), JSON.stringify(updated.limits), JSON.stringify(updated.federation), updated.instances ? JSON.stringify(updated.instances) : null, updated.installedBy, updated.installedAt, updated.activatedAt ?? null, name);
|
|
2619
|
+
return updated;
|
|
2620
|
+
}
|
|
2621
|
+
async deleteExtension(name) {
|
|
2622
|
+
const result = this.db.prepare('DELETE FROM extensions WHERE name = ?').run(name);
|
|
2623
|
+
return result.changes > 0;
|
|
2624
|
+
}
|
|
2625
|
+
deserializeExtension(row) {
|
|
2626
|
+
const record = {
|
|
2627
|
+
name: row.name,
|
|
2628
|
+
version: row.version,
|
|
2629
|
+
description: row.description,
|
|
2630
|
+
author: row.author,
|
|
2631
|
+
status: row.status,
|
|
2632
|
+
requiredApis: JSON.parse(row.requiredApis),
|
|
2633
|
+
actions: JSON.parse(row.actions),
|
|
2634
|
+
config: JSON.parse(row.config),
|
|
2635
|
+
limits: JSON.parse(row.limits),
|
|
2636
|
+
federation: JSON.parse(row.federation),
|
|
2637
|
+
installedBy: row.installedBy,
|
|
2638
|
+
installedAt: row.installedAt,
|
|
2639
|
+
};
|
|
2640
|
+
if (row.activatedAt)
|
|
2641
|
+
record.activatedAt = row.activatedAt;
|
|
2642
|
+
if (row.instances)
|
|
2643
|
+
record.instances = JSON.parse(row.instances);
|
|
2644
|
+
return record;
|
|
2645
|
+
}
|
|
2646
|
+
// ══════════════════════════════════════════════════════════
|
|
2647
|
+
// ── Escrow Holds ──
|
|
2648
|
+
// ══════════════════════════════════════════════════════════
|
|
2649
|
+
async createEscrowHold(record) {
|
|
2650
|
+
this.db.prepare(`INSERT INTO escrow_holds (holdId, fromGaii, amount, reason, status, extensionName, createdAt, releasedAt, releasedTo)
|
|
2651
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(record.holdId, record.fromGaii, record.amount, record.reason, record.status, record.extensionName, record.createdAt, record.releasedAt ?? null, record.releasedTo ?? null);
|
|
2652
|
+
return record;
|
|
2653
|
+
}
|
|
2654
|
+
async getEscrowHold(holdId) {
|
|
2655
|
+
const row = this.db.prepare('SELECT * FROM escrow_holds WHERE holdId = ?').get(holdId);
|
|
2656
|
+
return row ? this.deserializeEscrowHold(row) : null;
|
|
2657
|
+
}
|
|
2658
|
+
async listEscrowHolds(fromGaii, opts) {
|
|
2659
|
+
let sql = 'SELECT * FROM escrow_holds WHERE fromGaii = ?';
|
|
2660
|
+
const params = [fromGaii];
|
|
2661
|
+
if (opts?.status) {
|
|
2662
|
+
sql += ' AND status = ?';
|
|
2663
|
+
params.push(opts.status);
|
|
2664
|
+
}
|
|
2665
|
+
const rows = this.db.prepare(sql).all(...params);
|
|
2666
|
+
return rows.map(r => this.deserializeEscrowHold(r));
|
|
2667
|
+
}
|
|
2668
|
+
async releaseEscrowHold(holdId, toGaii) {
|
|
2669
|
+
const hold = await this.getEscrowHold(holdId);
|
|
2670
|
+
if (!hold)
|
|
2671
|
+
return null;
|
|
2672
|
+
if (hold.status !== 'held')
|
|
2673
|
+
return null;
|
|
2674
|
+
const updated = {
|
|
2675
|
+
...hold,
|
|
2676
|
+
status: 'released',
|
|
2677
|
+
releasedTo: toGaii,
|
|
2678
|
+
releasedAt: new Date().toISOString(),
|
|
2679
|
+
};
|
|
2680
|
+
this.db.prepare('UPDATE escrow_holds SET status = ?, releasedTo = ?, releasedAt = ? WHERE holdId = ?').run(updated.status, updated.releasedTo, updated.releasedAt, holdId);
|
|
2681
|
+
return updated;
|
|
2682
|
+
}
|
|
2683
|
+
async refundEscrowHold(holdId) {
|
|
2684
|
+
const hold = await this.getEscrowHold(holdId);
|
|
2685
|
+
if (!hold)
|
|
2686
|
+
return null;
|
|
2687
|
+
if (hold.status !== 'held')
|
|
2688
|
+
return null;
|
|
2689
|
+
const updated = {
|
|
2690
|
+
...hold,
|
|
2691
|
+
status: 'refunded',
|
|
2692
|
+
releasedAt: new Date().toISOString(),
|
|
2693
|
+
};
|
|
2694
|
+
this.db.prepare('UPDATE escrow_holds SET status = ?, releasedAt = ? WHERE holdId = ?').run(updated.status, updated.releasedAt, holdId);
|
|
2695
|
+
return updated;
|
|
2696
|
+
}
|
|
2697
|
+
deserializeEscrowHold(row) {
|
|
2698
|
+
const record = {
|
|
2699
|
+
holdId: row.holdId,
|
|
2700
|
+
fromGaii: row.fromGaii,
|
|
2701
|
+
amount: row.amount,
|
|
2702
|
+
reason: row.reason,
|
|
2703
|
+
status: row.status,
|
|
2704
|
+
extensionName: row.extensionName,
|
|
2705
|
+
createdAt: row.createdAt,
|
|
2706
|
+
};
|
|
2707
|
+
if (row.releasedAt)
|
|
2708
|
+
record.releasedAt = row.releasedAt;
|
|
2709
|
+
if (row.releasedTo)
|
|
2710
|
+
record.releasedTo = row.releasedTo;
|
|
2711
|
+
return record;
|
|
2712
|
+
}
|
|
2713
|
+
// ── Cortex Extensions ──────────────────────────────────────────
|
|
2714
|
+
async createCortexExtension(record) {
|
|
2715
|
+
const existing = this.db.prepare('SELECT name FROM cortex_extensions WHERE name = ?').get(record.name);
|
|
2716
|
+
if (existing)
|
|
2717
|
+
throw new Error(`Cortex extension "${record.name}" already exists`);
|
|
2718
|
+
this.db.prepare(`INSERT INTO cortex_extensions (name, namespace, shortName, apiVersion, version, description, author, license, tags, labels, aimeatCompat, status, visibility, installedAt, activatedAt, installedBy, manifest, components, activationArtifacts) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(record.name, record.namespace, record.shortName, record.apiVersion, record.version, record.description, record.author, record.license ?? null, JSON.stringify(record.tags), JSON.stringify(record.labels), record.aimeatCompat ?? null, record.status, record.visibility ?? 'private', record.installedAt, record.activatedAt ?? null, record.installedBy, record.manifest, JSON.stringify(record.components), JSON.stringify(record.activationArtifacts));
|
|
2719
|
+
return record;
|
|
2720
|
+
}
|
|
2721
|
+
async getCortexExtension(name) {
|
|
2722
|
+
const row = this.db.prepare('SELECT * FROM cortex_extensions WHERE name = ?').get(name);
|
|
2723
|
+
return row ? this.deserializeCortexExtension(row) : null;
|
|
2724
|
+
}
|
|
2725
|
+
async listCortexExtensions(opts) {
|
|
2726
|
+
let sql = 'SELECT * FROM cortex_extensions WHERE 1=1';
|
|
2727
|
+
const params = [];
|
|
2728
|
+
if (opts?.status) {
|
|
2729
|
+
sql += ' AND status = ?';
|
|
2730
|
+
params.push(opts.status);
|
|
2731
|
+
}
|
|
2732
|
+
if (opts?.namespace) {
|
|
2733
|
+
sql += ' AND namespace = ?';
|
|
2734
|
+
params.push(opts.namespace);
|
|
2735
|
+
}
|
|
2736
|
+
if (opts?.visibility) {
|
|
2737
|
+
sql += ' AND visibility = ?';
|
|
2738
|
+
params.push(opts.visibility);
|
|
2739
|
+
}
|
|
2740
|
+
if (opts?.installedBy) {
|
|
2741
|
+
sql += ' AND installedBy = ?';
|
|
2742
|
+
params.push(opts.installedBy);
|
|
2743
|
+
}
|
|
2744
|
+
const rows = this.db.prepare(sql).all(...params);
|
|
2745
|
+
return rows.map(r => this.deserializeCortexExtension(r));
|
|
2746
|
+
}
|
|
2747
|
+
async updateCortexExtension(name, updates) {
|
|
2748
|
+
const existing = await this.getCortexExtension(name);
|
|
2749
|
+
if (!existing)
|
|
2750
|
+
return null;
|
|
2751
|
+
const merged = { ...existing, ...updates };
|
|
2752
|
+
this.db.prepare(`UPDATE cortex_extensions SET namespace=?, shortName=?, apiVersion=?, version=?, description=?, author=?, license=?, tags=?, labels=?, aimeatCompat=?, status=?, visibility=?, installedAt=?, activatedAt=?, installedBy=?, manifest=?, components=?, activationArtifacts=? WHERE name=?`).run(merged.namespace, merged.shortName, merged.apiVersion, merged.version, merged.description, merged.author, merged.license ?? null, JSON.stringify(merged.tags), JSON.stringify(merged.labels), merged.aimeatCompat ?? null, merged.status, merged.visibility ?? 'private', merged.installedAt, merged.activatedAt ?? null, merged.installedBy, merged.manifest, JSON.stringify(merged.components), JSON.stringify(merged.activationArtifacts), name);
|
|
2753
|
+
return merged;
|
|
2754
|
+
}
|
|
2755
|
+
async deleteCortexExtension(name) {
|
|
2756
|
+
const result = this.db.prepare('DELETE FROM cortex_extensions WHERE name = ?').run(name);
|
|
2757
|
+
if (result.changes > 0) {
|
|
2758
|
+
this.db.prepare('DELETE FROM cortex_lib_files WHERE extName = ?').run(name);
|
|
2759
|
+
}
|
|
2760
|
+
return result.changes > 0;
|
|
2761
|
+
}
|
|
2762
|
+
async setCortexLibFile(extName, libName, content) {
|
|
2763
|
+
this.db.prepare('INSERT OR REPLACE INTO cortex_lib_files (extName, libName, content) VALUES (?, ?, ?)').run(extName, libName, content);
|
|
2764
|
+
}
|
|
2765
|
+
async getCortexLibFile(extName, libName) {
|
|
2766
|
+
const row = this.db.prepare('SELECT content FROM cortex_lib_files WHERE extName = ? AND libName = ?').get(extName, libName);
|
|
2767
|
+
return row?.content ?? null;
|
|
2768
|
+
}
|
|
2769
|
+
async deleteCortexLibFile(extName, libName) {
|
|
2770
|
+
const result = this.db.prepare('DELETE FROM cortex_lib_files WHERE extName = ? AND libName = ?').run(extName, libName);
|
|
2771
|
+
return result.changes > 0;
|
|
2772
|
+
}
|
|
2773
|
+
deserializeCortexExtension(row) {
|
|
2774
|
+
return {
|
|
2775
|
+
name: row.name,
|
|
2776
|
+
namespace: row.namespace,
|
|
2777
|
+
shortName: row.shortName,
|
|
2778
|
+
apiVersion: row.apiVersion,
|
|
2779
|
+
version: row.version,
|
|
2780
|
+
description: row.description,
|
|
2781
|
+
author: row.author,
|
|
2782
|
+
license: row.license,
|
|
2783
|
+
tags: JSON.parse(row.tags || '[]'),
|
|
2784
|
+
labels: JSON.parse(row.labels || '{}'),
|
|
2785
|
+
aimeatCompat: row.aimeatCompat,
|
|
2786
|
+
status: row.status,
|
|
2787
|
+
visibility: row.visibility === 'public' ? 'public' : 'private',
|
|
2788
|
+
installedAt: row.installedAt,
|
|
2789
|
+
activatedAt: row.activatedAt,
|
|
2790
|
+
installedBy: row.installedBy,
|
|
2791
|
+
manifest: row.manifest,
|
|
2792
|
+
components: JSON.parse(row.components || '[]'),
|
|
2793
|
+
activationArtifacts: JSON.parse(row.activationArtifacts || '{}'),
|
|
2794
|
+
};
|
|
2795
|
+
}
|
|
2796
|
+
// ══════════════════════════════════════════════════════════
|
|
2797
|
+
// ── Personal Push Subscriptions (REQ-007) ──
|
|
2798
|
+
// ══════════════════════════════════════════════════════════
|
|
2799
|
+
async createPersonalPushSubscription(record) {
|
|
2800
|
+
this.db.prepare(`INSERT INTO personal_push_subscriptions (id, personalNodeId, ownerName, endpoint, keys, failureCount, createdAt, lastUsedAt)
|
|
2801
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`).run(record.id, record.personalNodeId, record.ownerName, record.endpoint, JSON.stringify(record.keys), record.failureCount, record.createdAt, record.lastUsedAt);
|
|
2802
|
+
return record;
|
|
2803
|
+
}
|
|
2804
|
+
async getPersonalPushSubscription(id) {
|
|
2805
|
+
const row = this.db.prepare('SELECT * FROM personal_push_subscriptions WHERE id = ?').get(id);
|
|
2806
|
+
return row ? this.deserializePersonalPushSubscription(row) : null;
|
|
2807
|
+
}
|
|
2808
|
+
async listPersonalPushSubscriptions(personalNodeId) {
|
|
2809
|
+
const rows = this.db.prepare('SELECT * FROM personal_push_subscriptions WHERE personalNodeId = ?').all(personalNodeId);
|
|
2810
|
+
return rows.map(r => this.deserializePersonalPushSubscription(r));
|
|
2811
|
+
}
|
|
2812
|
+
async updatePersonalPushSubscription(id, updates) {
|
|
2813
|
+
const existing = await this.getPersonalPushSubscription(id);
|
|
2814
|
+
if (!existing)
|
|
2815
|
+
return false;
|
|
2816
|
+
const merged = { ...existing, ...updates };
|
|
2817
|
+
this.db.prepare(`UPDATE personal_push_subscriptions
|
|
2818
|
+
SET personalNodeId = ?, ownerName = ?, endpoint = ?, keys = ?, failureCount = ?, createdAt = ?, lastUsedAt = ?
|
|
2819
|
+
WHERE id = ?`).run(merged.personalNodeId, merged.ownerName, merged.endpoint, JSON.stringify(merged.keys), merged.failureCount, merged.createdAt, merged.lastUsedAt, id);
|
|
2820
|
+
return true;
|
|
2821
|
+
}
|
|
2822
|
+
async deletePersonalPushSubscription(id) {
|
|
2823
|
+
const result = this.db.prepare('DELETE FROM personal_push_subscriptions WHERE id = ?').run(id);
|
|
2824
|
+
return result.changes > 0;
|
|
2825
|
+
}
|
|
2826
|
+
async deletePersonalPushSubscriptionsByNode(personalNodeId) {
|
|
2827
|
+
const result = this.db.prepare('DELETE FROM personal_push_subscriptions WHERE personalNodeId = ?').run(personalNodeId);
|
|
2828
|
+
return result.changes;
|
|
2829
|
+
}
|
|
2830
|
+
async countPersonalPushSubscriptions(personalNodeId) {
|
|
2831
|
+
const row = this.db.prepare('SELECT COUNT(*) as cnt FROM personal_push_subscriptions WHERE personalNodeId = ?').get(personalNodeId);
|
|
2832
|
+
return row.cnt ?? 0;
|
|
2833
|
+
}
|
|
2834
|
+
deserializePersonalPushSubscription(row) {
|
|
2835
|
+
return {
|
|
2836
|
+
id: row.id,
|
|
2837
|
+
personalNodeId: row.personalNodeId,
|
|
2838
|
+
ownerName: row.ownerName,
|
|
2839
|
+
endpoint: row.endpoint,
|
|
2840
|
+
keys: JSON.parse(row.keys),
|
|
2841
|
+
failureCount: row.failureCount,
|
|
2842
|
+
createdAt: row.createdAt,
|
|
2843
|
+
lastUsedAt: row.lastUsedAt ?? null,
|
|
2844
|
+
};
|
|
2845
|
+
}
|
|
2846
|
+
// ══════════════════════════════════════════════════════════
|
|
2847
|
+
// ── Notification Preferences (REQ-007) ──
|
|
2848
|
+
// ══════════════════════════════════════════════════════════
|
|
2849
|
+
async getNotificationPreferences(personalNodeId) {
|
|
2850
|
+
const row = this.db.prepare('SELECT * FROM notification_preferences WHERE personalNodeId = ?').get(personalNodeId);
|
|
2851
|
+
return row ? this.deserializeNotificationPreferences(row) : null;
|
|
2852
|
+
}
|
|
2853
|
+
async upsertNotificationPreferences(prefs) {
|
|
2854
|
+
this.db.prepare(`INSERT INTO notification_preferences (personalNodeId, enabled, channels, notifyTypes, cooldownMinutes, quietHoursUtc, email)
|
|
2855
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
2856
|
+
ON CONFLICT(personalNodeId) DO UPDATE SET
|
|
2857
|
+
enabled = excluded.enabled,
|
|
2858
|
+
channels = excluded.channels,
|
|
2859
|
+
notifyTypes = excluded.notifyTypes,
|
|
2860
|
+
cooldownMinutes = excluded.cooldownMinutes,
|
|
2861
|
+
quietHoursUtc = excluded.quietHoursUtc,
|
|
2862
|
+
email = excluded.email`).run(prefs.personalNodeId, prefs.enabled ? 1 : 0, JSON.stringify(prefs.channels), JSON.stringify(prefs.notifyTypes), prefs.cooldownMinutes, prefs.quietHoursUtc ? JSON.stringify(prefs.quietHoursUtc) : null, prefs.email);
|
|
2863
|
+
return prefs;
|
|
2864
|
+
}
|
|
2865
|
+
async deleteNotificationPreferences(personalNodeId) {
|
|
2866
|
+
const result = this.db.prepare('DELETE FROM notification_preferences WHERE personalNodeId = ?').run(personalNodeId);
|
|
2867
|
+
return result.changes > 0;
|
|
2868
|
+
}
|
|
2869
|
+
deserializeNotificationPreferences(row) {
|
|
2870
|
+
return {
|
|
2871
|
+
personalNodeId: row.personalNodeId,
|
|
2872
|
+
enabled: row.enabled === 1,
|
|
2873
|
+
channels: JSON.parse(row.channels),
|
|
2874
|
+
notifyTypes: JSON.parse(row.notifyTypes),
|
|
2875
|
+
cooldownMinutes: row.cooldownMinutes,
|
|
2876
|
+
quietHoursUtc: row.quietHoursUtc ? JSON.parse(row.quietHoursUtc) : null,
|
|
2877
|
+
email: row.email ?? null,
|
|
2878
|
+
};
|
|
2879
|
+
}
|
|
2880
|
+
// ══════════════════════════════════════════════════════════
|
|
2881
|
+
// ── Notification Templates (Phase 3.2) ──
|
|
2882
|
+
// ══════════════════════════════════════════════════════════
|
|
2883
|
+
async getNotificationTemplate(id, locale) {
|
|
2884
|
+
const row = this.db.prepare('SELECT * FROM notification_templates WHERE id = ? AND locale = ?').get(id, locale);
|
|
2885
|
+
return row ? this.deserializeNotificationTemplate(row) : null;
|
|
2886
|
+
}
|
|
2887
|
+
async upsertNotificationTemplate(record) {
|
|
2888
|
+
this.db.prepare(`
|
|
2889
|
+
INSERT INTO notification_templates (id, locale, fields, placeholders, updatedAt, updatedBy)
|
|
2890
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
2891
|
+
ON CONFLICT(id, locale) DO UPDATE SET fields = excluded.fields, placeholders = excluded.placeholders, updatedAt = excluded.updatedAt, updatedBy = excluded.updatedBy
|
|
2892
|
+
`).run(record.id, record.locale, JSON.stringify(record.fields), JSON.stringify(record.placeholders), record.updatedAt, record.updatedBy);
|
|
2893
|
+
return record;
|
|
2894
|
+
}
|
|
2895
|
+
async listNotificationTemplates() {
|
|
2896
|
+
const rows = this.db.prepare('SELECT * FROM notification_templates ORDER BY id, locale').all();
|
|
2897
|
+
return rows.map(r => this.deserializeNotificationTemplate(r));
|
|
2898
|
+
}
|
|
2899
|
+
async deleteAllNotificationTemplates() {
|
|
2900
|
+
this.db.prepare('DELETE FROM notification_templates').run();
|
|
2901
|
+
}
|
|
2902
|
+
deserializeNotificationTemplate(row) {
|
|
2903
|
+
return {
|
|
2904
|
+
id: row.id,
|
|
2905
|
+
locale: row.locale,
|
|
2906
|
+
fields: JSON.parse(row.fields),
|
|
2907
|
+
placeholders: JSON.parse(row.placeholders),
|
|
2908
|
+
updatedAt: row.updatedAt,
|
|
2909
|
+
updatedBy: row.updatedBy,
|
|
2910
|
+
};
|
|
2911
|
+
}
|
|
2912
|
+
// ══════════════════════════════════════════════════════════
|
|
2913
|
+
// ── Sessions (P3-7: Server-Side Session Tracking) ──
|
|
2914
|
+
// ══════════════════════════════════════════════════════════
|
|
2915
|
+
async createSession(session) {
|
|
2916
|
+
this.db.prepare('INSERT INTO sessions (sessionId, gaii, owner, issuedAt, expiresAt, revoked) VALUES (?, ?, ?, ?, ?, 0)').run(session.sessionId, session.gaii, session.owner, session.issuedAt, session.expiresAt);
|
|
2917
|
+
}
|
|
2918
|
+
async listActiveSessions(owner) {
|
|
2919
|
+
return this.db.prepare('SELECT sessionId, gaii, owner, issuedAt, expiresAt, revoked FROM sessions WHERE owner = ? AND revoked = 0 ORDER BY issuedAt DESC').all(owner);
|
|
2920
|
+
}
|
|
2921
|
+
async revokeSession(sessionId) {
|
|
2922
|
+
const result = this.db.prepare('UPDATE sessions SET revoked = 1 WHERE sessionId = ? AND revoked = 0').run(sessionId);
|
|
2923
|
+
return result.changes > 0;
|
|
2924
|
+
}
|
|
2925
|
+
async revokeAllSessions(owner) {
|
|
2926
|
+
const result = this.db.prepare('UPDATE sessions SET revoked = 1 WHERE owner = ? AND revoked = 0').run(owner);
|
|
2927
|
+
return result.changes;
|
|
2928
|
+
}
|
|
2929
|
+
async isSessionRevoked(sessionId) {
|
|
2930
|
+
const row = this.db.prepare('SELECT revoked FROM sessions WHERE sessionId = ?').get(sessionId);
|
|
2931
|
+
if (!row)
|
|
2932
|
+
return false; // session not tracked = not revoked
|
|
2933
|
+
return row.revoked === 1;
|
|
2934
|
+
}
|
|
2935
|
+
// ══════════════════════════════════════════════════════════
|
|
2936
|
+
// ── Token Revocation ──
|
|
2937
|
+
// ══════════════════════════════════════════════════════════
|
|
2938
|
+
async revokeToken(tokenHash, expiresAt) {
|
|
2939
|
+
this.db.prepare('INSERT OR REPLACE INTO revoked_tokens (token_hash, expires_at) VALUES (?, ?)').run(tokenHash, expiresAt);
|
|
2940
|
+
}
|
|
2941
|
+
async isTokenRevoked(tokenHash) {
|
|
2942
|
+
const row = this.db.prepare('SELECT 1 FROM revoked_tokens WHERE token_hash = ?').get(tokenHash);
|
|
2943
|
+
return !!row;
|
|
2944
|
+
}
|
|
2945
|
+
async cleanExpiredRevocations() {
|
|
2946
|
+
const result = this.db.prepare('DELETE FROM revoked_tokens WHERE expires_at < ?').run(Math.floor(Date.now() / 1000));
|
|
2947
|
+
return result.changes;
|
|
2948
|
+
}
|
|
2949
|
+
// ══════════════════════════════════════════════════════════
|
|
2950
|
+
// ── App Catalog ──
|
|
2951
|
+
// ══════════════════════════════════════════════════════════
|
|
2952
|
+
async createApp(record) {
|
|
2953
|
+
this.db.prepare(`INSERT INTO apps (ownerGaii, ownerName, filename, versionNumber, manifest, mimeType, size, data, accessCode, createdAt)
|
|
2954
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(record.ownerGaii, record.ownerName, record.filename, record.versionNumber, JSON.stringify(record.manifest), record.mimeType, record.size, record.data, record.accessCode ?? null, record.createdAt);
|
|
2955
|
+
return record;
|
|
2956
|
+
}
|
|
2957
|
+
async getApp(ownerGaii, filename, version) {
|
|
2958
|
+
let row;
|
|
2959
|
+
if (version !== undefined) {
|
|
2960
|
+
row = this.db.prepare('SELECT * FROM apps WHERE ownerGaii = ? AND filename = ? AND versionNumber = ?')
|
|
2961
|
+
.get(ownerGaii, filename, version);
|
|
2962
|
+
}
|
|
2963
|
+
else {
|
|
2964
|
+
row = this.db.prepare('SELECT * FROM apps WHERE ownerGaii = ? AND filename = ? ORDER BY versionNumber DESC LIMIT 1')
|
|
2965
|
+
.get(ownerGaii, filename);
|
|
2966
|
+
}
|
|
2967
|
+
return row ? this.deserializeApp(row) : null;
|
|
2968
|
+
}
|
|
2969
|
+
async getAppByOwnerName(ownerName, filename, version) {
|
|
2970
|
+
let row;
|
|
2971
|
+
if (version !== undefined) {
|
|
2972
|
+
row = this.db.prepare('SELECT * FROM apps WHERE ownerName = ? AND filename = ? AND versionNumber = ?')
|
|
2973
|
+
.get(ownerName, filename, version);
|
|
2974
|
+
}
|
|
2975
|
+
else {
|
|
2976
|
+
row = this.db.prepare('SELECT * FROM apps WHERE ownerName = ? AND filename = ? ORDER BY versionNumber DESC LIMIT 1')
|
|
2977
|
+
.get(ownerName, filename);
|
|
2978
|
+
}
|
|
2979
|
+
return row ? this.deserializeApp(row) : null;
|
|
2980
|
+
}
|
|
2981
|
+
async listApps(opts) {
|
|
2982
|
+
// Get latest version of each app
|
|
2983
|
+
let query = `SELECT a.* FROM apps a
|
|
2984
|
+
INNER JOIN (SELECT ownerGaii, filename, MAX(versionNumber) as maxVer FROM apps GROUP BY ownerGaii, filename) latest
|
|
2985
|
+
ON a.ownerGaii = latest.ownerGaii AND a.filename = latest.filename AND a.versionNumber = latest.maxVer`;
|
|
2986
|
+
const conditions = [];
|
|
2987
|
+
const params = [];
|
|
2988
|
+
if (opts?.ownerGaii) {
|
|
2989
|
+
conditions.push(`a.ownerGaii = ?`);
|
|
2990
|
+
params.push(opts.ownerGaii);
|
|
2991
|
+
}
|
|
2992
|
+
if (opts?.category) {
|
|
2993
|
+
conditions.push(`json_extract(a.manifest, '$.category') = ?`);
|
|
2994
|
+
params.push(opts.category);
|
|
2995
|
+
}
|
|
2996
|
+
if (opts?.tag) {
|
|
2997
|
+
conditions.push(`a.manifest LIKE ?`);
|
|
2998
|
+
params.push(`%"${opts.tag}"%`);
|
|
2999
|
+
}
|
|
3000
|
+
if (opts?.q) {
|
|
3001
|
+
conditions.push(`(a.filename LIKE ? OR json_extract(a.manifest, '$.name') LIKE ? OR json_extract(a.manifest, '$.description') LIKE ?)`);
|
|
3002
|
+
const like = `%${opts.q}%`;
|
|
3003
|
+
params.push(like, like, like);
|
|
3004
|
+
}
|
|
3005
|
+
if (opts?.freeOnly) {
|
|
3006
|
+
conditions.push(`(json_extract(a.manifest, '$.priceMorsels') IS NULL OR json_extract(a.manifest, '$.priceMorsels') = 0)`);
|
|
3007
|
+
}
|
|
3008
|
+
if (conditions.length > 0) {
|
|
3009
|
+
query += ' WHERE ' + conditions.join(' AND ');
|
|
3010
|
+
}
|
|
3011
|
+
// Count total before pagination
|
|
3012
|
+
const countQuery = query.replace('SELECT a.*', 'SELECT COUNT(*) as cnt');
|
|
3013
|
+
const countRow = this.db.prepare(countQuery).get(...params);
|
|
3014
|
+
const total = countRow.cnt;
|
|
3015
|
+
// Sort
|
|
3016
|
+
if (opts?.sort === 'popular') {
|
|
3017
|
+
query += ` ORDER BY (SELECT COALESCE(d.downloads, 0) FROM app_downloads d WHERE d.ownerGaii = a.ownerGaii AND d.filename = a.filename) DESC, a.createdAt DESC`;
|
|
3018
|
+
}
|
|
3019
|
+
else {
|
|
3020
|
+
query += ' ORDER BY a.createdAt DESC';
|
|
3021
|
+
}
|
|
3022
|
+
// Pagination
|
|
3023
|
+
const limit = opts?.limit ?? 50;
|
|
3024
|
+
const offset = opts?.offset ?? 0;
|
|
3025
|
+
query += ' LIMIT ? OFFSET ?';
|
|
3026
|
+
params.push(limit, offset);
|
|
3027
|
+
const rows = this.db.prepare(query).all(...params);
|
|
3028
|
+
return { apps: rows.map(r => this.deserializeApp(r)), total };
|
|
3029
|
+
}
|
|
3030
|
+
async listAppVersions(ownerGaii, filename) {
|
|
3031
|
+
const rows = this.db.prepare('SELECT * FROM apps WHERE ownerGaii = ? AND filename = ? ORDER BY versionNumber DESC')
|
|
3032
|
+
.all(ownerGaii, filename);
|
|
3033
|
+
return rows.map(r => this.deserializeApp(r));
|
|
3034
|
+
}
|
|
3035
|
+
async getLatestVersionNumber(ownerGaii, filename) {
|
|
3036
|
+
const row = this.db.prepare('SELECT MAX(versionNumber) as maxVer FROM apps WHERE ownerGaii = ? AND filename = ?')
|
|
3037
|
+
.get(ownerGaii, filename);
|
|
3038
|
+
return row?.maxVer ?? 0;
|
|
3039
|
+
}
|
|
3040
|
+
async deleteApp(ownerGaii, filename, version) {
|
|
3041
|
+
if (version !== undefined) {
|
|
3042
|
+
const result = this.db.prepare('DELETE FROM apps WHERE ownerGaii = ? AND filename = ? AND versionNumber = ?')
|
|
3043
|
+
.run(ownerGaii, filename, version);
|
|
3044
|
+
return result.changes > 0;
|
|
3045
|
+
}
|
|
3046
|
+
// Delete all versions
|
|
3047
|
+
const result = this.db.prepare('DELETE FROM apps WHERE ownerGaii = ? AND filename = ?')
|
|
3048
|
+
.run(ownerGaii, filename);
|
|
3049
|
+
// Also delete download counter
|
|
3050
|
+
this.db.prepare('DELETE FROM app_downloads WHERE ownerGaii = ? AND filename = ?')
|
|
3051
|
+
.run(ownerGaii, filename);
|
|
3052
|
+
return result.changes > 0;
|
|
3053
|
+
}
|
|
3054
|
+
async updateAppAccessCode(ownerGaii, filename, accessCode) {
|
|
3055
|
+
// Update access code on all versions
|
|
3056
|
+
const result = this.db.prepare('UPDATE apps SET accessCode = ? WHERE ownerGaii = ? AND filename = ?')
|
|
3057
|
+
.run(accessCode ?? null, ownerGaii, filename);
|
|
3058
|
+
return result.changes > 0;
|
|
3059
|
+
}
|
|
3060
|
+
async getAppDownloads(ownerGaii, filename) {
|
|
3061
|
+
const row = this.db.prepare('SELECT downloads FROM app_downloads WHERE ownerGaii = ? AND filename = ?')
|
|
3062
|
+
.get(ownerGaii, filename);
|
|
3063
|
+
return row?.downloads ?? 0;
|
|
3064
|
+
}
|
|
3065
|
+
async incrementAppDownloads(ownerGaii, filename) {
|
|
3066
|
+
this.db.prepare(`INSERT INTO app_downloads (ownerGaii, filename, downloads) VALUES (?, ?, 1)
|
|
3067
|
+
ON CONFLICT(ownerGaii, filename) DO UPDATE SET downloads = downloads + 1`).run(ownerGaii, filename);
|
|
3068
|
+
}
|
|
3069
|
+
deserializeApp(row) {
|
|
3070
|
+
const record = {
|
|
3071
|
+
ownerGaii: row.ownerGaii,
|
|
3072
|
+
ownerName: row.ownerName,
|
|
3073
|
+
filename: row.filename,
|
|
3074
|
+
versionNumber: row.versionNumber,
|
|
3075
|
+
manifest: JSON.parse(row.manifest || '{}'),
|
|
3076
|
+
mimeType: row.mimeType,
|
|
3077
|
+
size: row.size,
|
|
3078
|
+
data: row.data,
|
|
3079
|
+
createdAt: row.createdAt,
|
|
3080
|
+
};
|
|
3081
|
+
if (row.accessCode)
|
|
3082
|
+
record.accessCode = row.accessCode;
|
|
3083
|
+
return record;
|
|
3084
|
+
}
|
|
3085
|
+
// ── App Marketplace (purchase receipts) ──
|
|
3086
|
+
async createAppPurchase(record) {
|
|
3087
|
+
this.db.prepare(`INSERT INTO app_purchases (transactionId, buyerGaii, buyerOwner, sellerGaii, sellerOwner, appFilename, appName, appVersionNumber, licenseType, priceMorsels, transactionFeeMorsels, purchasedAt, appContent, appManifest, appScreenshot, signature, nodeId, nodePublicKey) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(record.transactionId, record.buyerGaii, record.buyerOwner, record.sellerGaii, record.sellerOwner, record.appFilename, record.appName, record.appVersionNumber, record.licenseType, record.priceMorsels, record.transactionFeeMorsels, record.purchasedAt, record.appContent, JSON.stringify(record.appManifest), record.appScreenshot ?? null, record.signature, record.nodeId, record.nodePublicKey);
|
|
3088
|
+
return record;
|
|
3089
|
+
}
|
|
3090
|
+
async getAppPurchase(transactionId) {
|
|
3091
|
+
const row = this.db.prepare('SELECT * FROM app_purchases WHERE transactionId = ?').get(transactionId);
|
|
3092
|
+
return row ? this.deserializeAppPurchase(row) : null;
|
|
3093
|
+
}
|
|
3094
|
+
async listAppPurchasesByBuyer(buyerGaii) {
|
|
3095
|
+
const rows = this.db.prepare('SELECT * FROM app_purchases WHERE buyerGaii = ? ORDER BY purchasedAt DESC').all(buyerGaii);
|
|
3096
|
+
return rows.map(r => this.deserializeAppPurchase(r));
|
|
3097
|
+
}
|
|
3098
|
+
async listAppPurchasesBySeller(sellerGaii) {
|
|
3099
|
+
const rows = this.db.prepare('SELECT * FROM app_purchases WHERE sellerGaii = ? ORDER BY purchasedAt DESC').all(sellerGaii);
|
|
3100
|
+
return rows.map(r => this.deserializeAppPurchase(r));
|
|
3101
|
+
}
|
|
3102
|
+
async hasValidLicense(buyerGaii, sellerGaii, filename, licenseType) {
|
|
3103
|
+
// Lifetime license: any purchase of this app grants access to all versions
|
|
3104
|
+
const lifetime = this.db.prepare('SELECT 1 FROM app_purchases WHERE buyerGaii = ? AND sellerGaii = ? AND appFilename = ? AND licenseType = ? LIMIT 1').get(buyerGaii, sellerGaii, filename, 'lifetime');
|
|
3105
|
+
if (lifetime)
|
|
3106
|
+
return true;
|
|
3107
|
+
// Single license: buyer has at least one purchase of this app (version-specific check done at download)
|
|
3108
|
+
if (!licenseType || licenseType === 'single') {
|
|
3109
|
+
const single = this.db.prepare('SELECT 1 FROM app_purchases WHERE buyerGaii = ? AND sellerGaii = ? AND appFilename = ? LIMIT 1').get(buyerGaii, sellerGaii, filename);
|
|
3110
|
+
return !!single;
|
|
3111
|
+
}
|
|
3112
|
+
return false;
|
|
3113
|
+
}
|
|
3114
|
+
deserializeAppPurchase(row) {
|
|
3115
|
+
const record = {
|
|
3116
|
+
transactionId: row.transactionId,
|
|
3117
|
+
buyerGaii: row.buyerGaii,
|
|
3118
|
+
buyerOwner: row.buyerOwner,
|
|
3119
|
+
sellerGaii: row.sellerGaii,
|
|
3120
|
+
sellerOwner: row.sellerOwner,
|
|
3121
|
+
appFilename: row.appFilename,
|
|
3122
|
+
appName: row.appName,
|
|
3123
|
+
appVersionNumber: row.appVersionNumber,
|
|
3124
|
+
licenseType: row.licenseType,
|
|
3125
|
+
priceMorsels: row.priceMorsels,
|
|
3126
|
+
transactionFeeMorsels: row.transactionFeeMorsels,
|
|
3127
|
+
purchasedAt: row.purchasedAt,
|
|
3128
|
+
appContent: row.appContent,
|
|
3129
|
+
appManifest: JSON.parse(row.appManifest || '{}'),
|
|
3130
|
+
signature: row.signature,
|
|
3131
|
+
nodeId: row.nodeId,
|
|
3132
|
+
nodePublicKey: row.nodePublicKey,
|
|
3133
|
+
};
|
|
3134
|
+
if (row.appScreenshot)
|
|
3135
|
+
record.appScreenshot = row.appScreenshot;
|
|
3136
|
+
return record;
|
|
3137
|
+
}
|
|
3138
|
+
// ══════════════════════════════════════════════════════════
|
|
3139
|
+
// ── Config Persistence ──
|
|
3140
|
+
// ══════════════════════════════════════════════════════════
|
|
3141
|
+
supportsConfigPersistence() {
|
|
3142
|
+
// In-memory SQLite (:memory:) does not persist across restarts
|
|
3143
|
+
return this.db.name !== ':memory:';
|
|
3144
|
+
}
|
|
3145
|
+
async getConfigValue(key) {
|
|
3146
|
+
const row = this.db.prepare('SELECT value FROM system_settings WHERE key = ?').get(`config:${key}`);
|
|
3147
|
+
return row?.value ?? null;
|
|
3148
|
+
}
|
|
3149
|
+
async setConfigValue(key, value) {
|
|
3150
|
+
this.db.prepare(`
|
|
3151
|
+
INSERT INTO system_settings (key, value, updatedAt) VALUES (?, ?, datetime('now'))
|
|
3152
|
+
ON CONFLICT(key) DO UPDATE SET value = excluded.value, updatedAt = datetime('now')
|
|
3153
|
+
`).run(`config:${key}`, value);
|
|
3154
|
+
}
|
|
3155
|
+
async deleteConfigValue(key) {
|
|
3156
|
+
this.db.prepare('DELETE FROM system_settings WHERE key = ?').run(`config:${key}`);
|
|
3157
|
+
}
|
|
3158
|
+
async getAllConfigValues() {
|
|
3159
|
+
const rows = this.db.prepare("SELECT key, value FROM system_settings WHERE key LIKE 'config:%'").all();
|
|
3160
|
+
const result = {};
|
|
3161
|
+
for (const r of rows)
|
|
3162
|
+
result[r.key.replace('config:', '')] = r.value;
|
|
3163
|
+
return result;
|
|
3164
|
+
}
|
|
3165
|
+
// ══════════════════════════════════════════════════════════
|
|
3166
|
+
// ── Knowledge: Memory Links ──
|
|
3167
|
+
// ══════════════════════════════════════════════════════════
|
|
3168
|
+
async createLink(record) {
|
|
3169
|
+
this.db.prepare(`
|
|
3170
|
+
INSERT INTO knowledge_links (source, target, relation, description, linked_at, linked_by)
|
|
3171
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
3172
|
+
ON CONFLICT(source, target) DO UPDATE SET
|
|
3173
|
+
relation = excluded.relation, description = excluded.description,
|
|
3174
|
+
linked_at = excluded.linked_at, linked_by = excluded.linked_by
|
|
3175
|
+
`).run(record.source, record.target, record.relation, record.description, record.linked_at, record.linked_by);
|
|
3176
|
+
return record;
|
|
3177
|
+
}
|
|
3178
|
+
async getLink(source, target) {
|
|
3179
|
+
const row = this.db.prepare('SELECT * FROM knowledge_links WHERE source = ? AND target = ?').get(source, target);
|
|
3180
|
+
return row ?? null;
|
|
3181
|
+
}
|
|
3182
|
+
async listLinks(key, opts) {
|
|
3183
|
+
const dir = opts?.direction ?? 'both';
|
|
3184
|
+
let sql;
|
|
3185
|
+
const params = [];
|
|
3186
|
+
if (dir === 'outgoing') {
|
|
3187
|
+
sql = 'SELECT * FROM knowledge_links WHERE source = ?';
|
|
3188
|
+
params.push(key);
|
|
3189
|
+
}
|
|
3190
|
+
else if (dir === 'incoming') {
|
|
3191
|
+
sql = 'SELECT * FROM knowledge_links WHERE target = ?';
|
|
3192
|
+
params.push(key);
|
|
3193
|
+
}
|
|
3194
|
+
else {
|
|
3195
|
+
sql = 'SELECT * FROM knowledge_links WHERE source = ? OR target = ?';
|
|
3196
|
+
params.push(key, key);
|
|
3197
|
+
}
|
|
3198
|
+
if (opts?.relation) {
|
|
3199
|
+
sql += ' AND relation = ?';
|
|
3200
|
+
params.push(opts.relation);
|
|
3201
|
+
}
|
|
3202
|
+
return this.db.prepare(sql).all(...params);
|
|
3203
|
+
}
|
|
3204
|
+
async deleteLink(source, target) {
|
|
3205
|
+
const result = this.db.prepare('DELETE FROM knowledge_links WHERE source = ? AND target = ?').run(source, target);
|
|
3206
|
+
return result.changes > 0;
|
|
3207
|
+
}
|
|
3208
|
+
async findBrokenLinks(ownerGaii) {
|
|
3209
|
+
const links = this.db.prepare('SELECT * FROM knowledge_links WHERE linked_by = ?').all(ownerGaii);
|
|
3210
|
+
const broken = [];
|
|
3211
|
+
for (const link of links) {
|
|
3212
|
+
const sourceExists = await this.getMemory(ownerGaii, link.source);
|
|
3213
|
+
const targetExists = await this.getMemory(ownerGaii, link.target);
|
|
3214
|
+
if (!sourceExists || !targetExists)
|
|
3215
|
+
broken.push(link);
|
|
3216
|
+
}
|
|
3217
|
+
return broken;
|
|
3218
|
+
}
|
|
3219
|
+
// ══════════════════════════════════════════════════════════
|
|
3220
|
+
// ── Knowledge: Operator Reviews ──
|
|
3221
|
+
// ══════════════════════════════════════════════════════════
|
|
3222
|
+
async createReview(record) {
|
|
3223
|
+
this.db.prepare(`
|
|
3224
|
+
INSERT INTO knowledge_reviews (id, packageId, operatorGaii, reason, customText, action, timestamp)
|
|
3225
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
3226
|
+
`).run(record.id, record.packageId, record.operatorGaii, record.reason, record.customText ?? null, record.action, record.timestamp);
|
|
3227
|
+
return record;
|
|
3228
|
+
}
|
|
3229
|
+
async listReviews(packageId) {
|
|
3230
|
+
return this.db.prepare('SELECT * FROM knowledge_reviews WHERE packageId = ? ORDER BY timestamp ASC').all(packageId);
|
|
3231
|
+
}
|
|
3232
|
+
async listAllReviews(opts) {
|
|
3233
|
+
const page = opts?.page ?? 1;
|
|
3234
|
+
const perPage = opts?.perPage ?? 20;
|
|
3235
|
+
const offset = (page - 1) * perPage;
|
|
3236
|
+
return this.db.prepare('SELECT * FROM knowledge_reviews ORDER BY timestamp DESC LIMIT ? OFFSET ?').all(perPage, offset);
|
|
3237
|
+
}
|
|
3238
|
+
// ══════════════════════════════════════════════════════════
|
|
3239
|
+
// ── Scheduled Jobs ──
|
|
3240
|
+
// ══════════════════════════════════════════════════════════
|
|
3241
|
+
async createScheduledJob(record) {
|
|
3242
|
+
try {
|
|
3243
|
+
this.db.prepare(`INSERT INTO scheduled_jobs (id, name, type, extensionName, instanceId, actionId,
|
|
3244
|
+
coreHandler, cron, enabled, input, lastRunAt, lastRunResult, lastRunError,
|
|
3245
|
+
lastRunDurationMs, nextRunAt, createdBy, createdAt, updatedAt)
|
|
3246
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(record.id, record.name, record.type, record.extensionName ?? null, record.instanceId ?? null, record.actionId ?? null, record.coreHandler ?? null, record.cron, record.enabled ? 1 : 0, record.input ? JSON.stringify(record.input) : null, record.lastRunAt ?? null, record.lastRunResult ?? null, record.lastRunError ?? null, record.lastRunDurationMs ?? null, record.nextRunAt ?? null, record.createdBy, record.createdAt, record.updatedAt);
|
|
3247
|
+
return record;
|
|
3248
|
+
}
|
|
3249
|
+
catch (err) {
|
|
3250
|
+
if (err instanceof Error && err.message?.includes('UNIQUE constraint failed')) {
|
|
3251
|
+
throw new Error(`Scheduled job "${record.id}" already exists`, { cause: err });
|
|
3252
|
+
}
|
|
3253
|
+
throw err;
|
|
3254
|
+
}
|
|
3255
|
+
}
|
|
3256
|
+
async getScheduledJob(id) {
|
|
3257
|
+
const row = this.db.prepare('SELECT * FROM scheduled_jobs WHERE id = ?').get(id);
|
|
3258
|
+
return row ? this.deserializeScheduledJob(row) : null;
|
|
3259
|
+
}
|
|
3260
|
+
async listScheduledJobs(filter) {
|
|
3261
|
+
let sql = 'SELECT * FROM scheduled_jobs';
|
|
3262
|
+
const conditions = [];
|
|
3263
|
+
const params = [];
|
|
3264
|
+
if (filter?.type) {
|
|
3265
|
+
conditions.push('type = ?');
|
|
3266
|
+
params.push(filter.type);
|
|
3267
|
+
}
|
|
3268
|
+
if (filter?.extensionName) {
|
|
3269
|
+
conditions.push('extensionName = ?');
|
|
3270
|
+
params.push(filter.extensionName);
|
|
3271
|
+
}
|
|
3272
|
+
if (filter?.enabled !== undefined) {
|
|
3273
|
+
conditions.push('enabled = ?');
|
|
3274
|
+
params.push(filter.enabled ? 1 : 0);
|
|
3275
|
+
}
|
|
3276
|
+
if (conditions.length > 0)
|
|
3277
|
+
sql += ' WHERE ' + conditions.join(' AND ');
|
|
3278
|
+
const rows = this.db.prepare(sql).all(...params);
|
|
3279
|
+
return rows.map(r => this.deserializeScheduledJob(r));
|
|
3280
|
+
}
|
|
3281
|
+
async updateScheduledJob(id, updates) {
|
|
3282
|
+
const existing = await this.getScheduledJob(id);
|
|
3283
|
+
if (!existing)
|
|
3284
|
+
return null;
|
|
3285
|
+
const updated = { ...existing, ...updates };
|
|
3286
|
+
this.db.prepare(`UPDATE scheduled_jobs SET name = ?, type = ?, extensionName = ?, instanceId = ?,
|
|
3287
|
+
actionId = ?, coreHandler = ?, cron = ?, enabled = ?, input = ?,
|
|
3288
|
+
lastRunAt = ?, lastRunResult = ?, lastRunError = ?, lastRunDurationMs = ?,
|
|
3289
|
+
nextRunAt = ?, createdBy = ?, createdAt = ?, updatedAt = ? WHERE id = ?`).run(updated.name, updated.type, updated.extensionName ?? null, updated.instanceId ?? null, updated.actionId ?? null, updated.coreHandler ?? null, updated.cron, updated.enabled ? 1 : 0, updated.input ? JSON.stringify(updated.input) : null, updated.lastRunAt ?? null, updated.lastRunResult ?? null, updated.lastRunError ?? null, updated.lastRunDurationMs ?? null, updated.nextRunAt ?? null, updated.createdBy, updated.createdAt, updated.updatedAt, id);
|
|
3290
|
+
return updated;
|
|
3291
|
+
}
|
|
3292
|
+
async deleteScheduledJob(id) {
|
|
3293
|
+
const result = this.db.prepare('DELETE FROM scheduled_jobs WHERE id = ?').run(id);
|
|
3294
|
+
return result.changes > 0;
|
|
3295
|
+
}
|
|
3296
|
+
deserializeScheduledJob(row) {
|
|
3297
|
+
const record = {
|
|
3298
|
+
id: row.id,
|
|
3299
|
+
name: row.name,
|
|
3300
|
+
type: row.type,
|
|
3301
|
+
cron: row.cron,
|
|
3302
|
+
enabled: row.enabled === 1,
|
|
3303
|
+
createdBy: row.createdBy,
|
|
3304
|
+
createdAt: row.createdAt,
|
|
3305
|
+
updatedAt: row.updatedAt,
|
|
3306
|
+
};
|
|
3307
|
+
if (row.extensionName)
|
|
3308
|
+
record.extensionName = row.extensionName;
|
|
3309
|
+
if (row.instanceId)
|
|
3310
|
+
record.instanceId = row.instanceId;
|
|
3311
|
+
if (row.actionId)
|
|
3312
|
+
record.actionId = row.actionId;
|
|
3313
|
+
if (row.coreHandler)
|
|
3314
|
+
record.coreHandler = row.coreHandler;
|
|
3315
|
+
if (row.input)
|
|
3316
|
+
record.input = JSON.parse(row.input);
|
|
3317
|
+
if (row.lastRunAt)
|
|
3318
|
+
record.lastRunAt = row.lastRunAt;
|
|
3319
|
+
if (row.lastRunResult)
|
|
3320
|
+
record.lastRunResult = row.lastRunResult;
|
|
3321
|
+
if (row.lastRunError)
|
|
3322
|
+
record.lastRunError = row.lastRunError;
|
|
3323
|
+
if (row.lastRunDurationMs !== null && row.lastRunDurationMs !== undefined)
|
|
3324
|
+
record.lastRunDurationMs = row.lastRunDurationMs;
|
|
3325
|
+
if (row.nextRunAt)
|
|
3326
|
+
record.nextRunAt = row.nextRunAt;
|
|
3327
|
+
return record;
|
|
3328
|
+
}
|
|
3329
|
+
// ══════════════════════════════════════════════════════════
|
|
3330
|
+
// ── Execution Log ──
|
|
3331
|
+
// ══════════════════════════════════════════════════════════
|
|
3332
|
+
async createExecutionLog(entry) {
|
|
3333
|
+
this.db.prepare(`INSERT INTO execution_log (id, jobId, jobName, type, extensionName, actionId,
|
|
3334
|
+
"trigger", result, errorMessage, durationMs, memoryReads, memoryWrites, createdAt)
|
|
3335
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(entry.id, entry.jobId, entry.jobName, entry.type, entry.extensionName ?? null, entry.actionId ?? null, entry.trigger, entry.result, entry.errorMessage ?? null, entry.durationMs, JSON.stringify(entry.memoryReads), JSON.stringify(entry.memoryWrites), entry.createdAt);
|
|
3336
|
+
return entry;
|
|
3337
|
+
}
|
|
3338
|
+
async listExecutionLogs(filter) {
|
|
3339
|
+
let sql = 'SELECT * FROM execution_log';
|
|
3340
|
+
const conditions = [];
|
|
3341
|
+
const params = [];
|
|
3342
|
+
if (filter?.jobId) {
|
|
3343
|
+
conditions.push('jobId = ?');
|
|
3344
|
+
params.push(filter.jobId);
|
|
3345
|
+
}
|
|
3346
|
+
if (filter?.extensionName) {
|
|
3347
|
+
conditions.push('extensionName = ?');
|
|
3348
|
+
params.push(filter.extensionName);
|
|
3349
|
+
}
|
|
3350
|
+
if (filter?.trigger) {
|
|
3351
|
+
conditions.push('"trigger" = ?');
|
|
3352
|
+
params.push(filter.trigger);
|
|
3353
|
+
}
|
|
3354
|
+
if (filter?.result) {
|
|
3355
|
+
conditions.push('result = ?');
|
|
3356
|
+
params.push(filter.result);
|
|
3357
|
+
}
|
|
3358
|
+
if (conditions.length > 0)
|
|
3359
|
+
sql += ' WHERE ' + conditions.join(' AND ');
|
|
3360
|
+
sql += ' ORDER BY createdAt DESC';
|
|
3361
|
+
if (filter?.limit) {
|
|
3362
|
+
sql += ' LIMIT ?';
|
|
3363
|
+
params.push(filter.limit);
|
|
3364
|
+
}
|
|
3365
|
+
if (filter?.offset) {
|
|
3366
|
+
sql += ' OFFSET ?';
|
|
3367
|
+
params.push(filter.offset);
|
|
3368
|
+
}
|
|
3369
|
+
const rows = this.db.prepare(sql).all(...params);
|
|
3370
|
+
return rows.map(r => this.deserializeExecutionLog(r));
|
|
3371
|
+
}
|
|
3372
|
+
async countExecutionLogs(filter) {
|
|
3373
|
+
let sql = 'SELECT COUNT(*) as cnt FROM execution_log';
|
|
3374
|
+
const conditions = [];
|
|
3375
|
+
const params = [];
|
|
3376
|
+
if (filter?.jobId) {
|
|
3377
|
+
conditions.push('jobId = ?');
|
|
3378
|
+
params.push(filter.jobId);
|
|
3379
|
+
}
|
|
3380
|
+
if (filter?.extensionName) {
|
|
3381
|
+
conditions.push('extensionName = ?');
|
|
3382
|
+
params.push(filter.extensionName);
|
|
3383
|
+
}
|
|
3384
|
+
if (filter?.trigger) {
|
|
3385
|
+
conditions.push('"trigger" = ?');
|
|
3386
|
+
params.push(filter.trigger);
|
|
3387
|
+
}
|
|
3388
|
+
if (filter?.result) {
|
|
3389
|
+
conditions.push('result = ?');
|
|
3390
|
+
params.push(filter.result);
|
|
3391
|
+
}
|
|
3392
|
+
if (conditions.length > 0)
|
|
3393
|
+
sql += ' WHERE ' + conditions.join(' AND ');
|
|
3394
|
+
const row = this.db.prepare(sql).get(...params);
|
|
3395
|
+
return row.cnt ?? 0;
|
|
3396
|
+
}
|
|
3397
|
+
async pruneExecutionLogs(beforeDate) {
|
|
3398
|
+
const result = this.db.prepare('DELETE FROM execution_log WHERE createdAt < ?').run(beforeDate);
|
|
3399
|
+
return result.changes;
|
|
3400
|
+
}
|
|
3401
|
+
deserializeExecutionLog(row) {
|
|
3402
|
+
return {
|
|
3403
|
+
id: row.id,
|
|
3404
|
+
jobId: row.jobId,
|
|
3405
|
+
jobName: row.jobName,
|
|
3406
|
+
type: row.type,
|
|
3407
|
+
extensionName: row.extensionName || undefined,
|
|
3408
|
+
actionId: row.actionId || undefined,
|
|
3409
|
+
trigger: row.trigger,
|
|
3410
|
+
result: row.result,
|
|
3411
|
+
errorMessage: row.errorMessage || undefined,
|
|
3412
|
+
durationMs: row.durationMs,
|
|
3413
|
+
memoryReads: JSON.parse(row.memoryReads || '[]'),
|
|
3414
|
+
memoryWrites: JSON.parse(row.memoryWrites || '[]'),
|
|
3415
|
+
createdAt: row.createdAt,
|
|
3416
|
+
};
|
|
3417
|
+
}
|
|
3418
|
+
// ══════════════════════════════════════════════════════════
|
|
3419
|
+
// ── Extension Instances ──
|
|
3420
|
+
// ══════════════════════════════════════════════════════════
|
|
3421
|
+
async createExtensionInstance(record) {
|
|
3422
|
+
try {
|
|
3423
|
+
this.db.prepare(`INSERT INTO extension_instances (id, extensionName, config, status, createdBy, createdAt, updatedAt)
|
|
3424
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`).run(record.id, record.extensionName, JSON.stringify(record.config), record.status, record.createdBy, record.createdAt, record.updatedAt);
|
|
3425
|
+
return record;
|
|
3426
|
+
}
|
|
3427
|
+
catch (err) {
|
|
3428
|
+
if (err instanceof Error && err.message?.includes('UNIQUE constraint failed')) {
|
|
3429
|
+
throw new Error(`Extension instance "${record.id}" already exists for "${record.extensionName}"`, { cause: err });
|
|
3430
|
+
}
|
|
3431
|
+
throw err;
|
|
3432
|
+
}
|
|
3433
|
+
}
|
|
3434
|
+
async getExtensionInstance(extensionName, instanceId) {
|
|
3435
|
+
const row = this.db.prepare('SELECT * FROM extension_instances WHERE extensionName = ? AND id = ?').get(extensionName, instanceId);
|
|
3436
|
+
return row ? this.deserializeExtensionInstance(row) : null;
|
|
3437
|
+
}
|
|
3438
|
+
async listExtensionInstances(extensionName) {
|
|
3439
|
+
const rows = this.db.prepare('SELECT * FROM extension_instances WHERE extensionName = ?').all(extensionName);
|
|
3440
|
+
return rows.map(r => this.deserializeExtensionInstance(r));
|
|
3441
|
+
}
|
|
3442
|
+
async updateExtensionInstance(extensionName, instanceId, updates) {
|
|
3443
|
+
const existing = await this.getExtensionInstance(extensionName, instanceId);
|
|
3444
|
+
if (!existing)
|
|
3445
|
+
return null;
|
|
3446
|
+
const updated = { ...existing, ...updates };
|
|
3447
|
+
this.db.prepare(`UPDATE extension_instances SET config = ?, status = ?, createdBy = ?, createdAt = ?, updatedAt = ?
|
|
3448
|
+
WHERE extensionName = ? AND id = ?`).run(JSON.stringify(updated.config), updated.status, updated.createdBy, updated.createdAt, updated.updatedAt, extensionName, instanceId);
|
|
3449
|
+
return updated;
|
|
3450
|
+
}
|
|
3451
|
+
async deleteExtensionInstance(extensionName, instanceId) {
|
|
3452
|
+
const result = this.db.prepare('DELETE FROM extension_instances WHERE extensionName = ? AND id = ?').run(extensionName, instanceId);
|
|
3453
|
+
return result.changes > 0;
|
|
3454
|
+
}
|
|
3455
|
+
deserializeExtensionInstance(row) {
|
|
3456
|
+
return {
|
|
3457
|
+
id: row.id,
|
|
3458
|
+
extensionName: row.extensionName,
|
|
3459
|
+
config: JSON.parse(row.config),
|
|
3460
|
+
status: row.status,
|
|
3461
|
+
createdBy: row.createdBy,
|
|
3462
|
+
createdAt: row.createdAt,
|
|
3463
|
+
updatedAt: row.updatedAt,
|
|
3464
|
+
};
|
|
3465
|
+
}
|
|
3466
|
+
// ══════════════════════════════════════════════════════════
|
|
3467
|
+
// ── Replication Queue (B.1) ──
|
|
3468
|
+
// ══════════════════════════════════════════════════════════
|
|
3469
|
+
async enqueueReplication(entry) {
|
|
3470
|
+
const id = randomUUID();
|
|
3471
|
+
this.db.prepare(`INSERT INTO replication_queue (id, type, targetPeers, payload, createdAt, attempts, lastAttemptAt, status)
|
|
3472
|
+
VALUES (?, ?, ?, ?, ?, 0, NULL, 'pending')`).run(id, entry.type, JSON.stringify(entry.targetPeers), JSON.stringify(entry.payload), entry.createdAt);
|
|
3473
|
+
return id;
|
|
3474
|
+
}
|
|
3475
|
+
async dequeueReplication(peerId, limit) {
|
|
3476
|
+
// Fetch all pending entries ordered by creation time
|
|
3477
|
+
const rows = this.db.prepare(`SELECT * FROM replication_queue WHERE status = 'pending' ORDER BY createdAt ASC`).all();
|
|
3478
|
+
const results = [];
|
|
3479
|
+
for (const row of rows) {
|
|
3480
|
+
const peers = JSON.parse(row.targetPeers);
|
|
3481
|
+
if (peers.includes(peerId)) {
|
|
3482
|
+
results.push(this.deserializeReplicationEntry(row));
|
|
3483
|
+
if (results.length >= limit)
|
|
3484
|
+
break;
|
|
3485
|
+
}
|
|
3486
|
+
}
|
|
3487
|
+
return results;
|
|
3488
|
+
}
|
|
3489
|
+
async markReplicationSent(ids) {
|
|
3490
|
+
if (ids.length === 0)
|
|
3491
|
+
return;
|
|
3492
|
+
const placeholders = ids.map(() => '?').join(',');
|
|
3493
|
+
this.db.prepare(`UPDATE replication_queue SET status = 'sent' WHERE id IN (${placeholders})`).run(...ids);
|
|
3494
|
+
}
|
|
3495
|
+
async markReplicationFailed(ids) {
|
|
3496
|
+
if (ids.length === 0)
|
|
3497
|
+
return;
|
|
3498
|
+
const now = new Date().toISOString();
|
|
3499
|
+
const stmt = this.db.prepare(`UPDATE replication_queue SET status = 'failed', attempts = attempts + 1, lastAttemptAt = ? WHERE id = ?`);
|
|
3500
|
+
for (const id of ids) {
|
|
3501
|
+
stmt.run(now, id);
|
|
3502
|
+
}
|
|
3503
|
+
}
|
|
3504
|
+
async pruneReplicationQueue(maxAge) {
|
|
3505
|
+
const maxAgeIso = maxAge.toISOString();
|
|
3506
|
+
const result = this.db.prepare(`DELETE FROM replication_queue WHERE createdAt < ? OR status = 'sent'`).run(maxAgeIso);
|
|
3507
|
+
return result.changes;
|
|
3508
|
+
}
|
|
3509
|
+
async replicationQueueSize() {
|
|
3510
|
+
const row = this.db.prepare('SELECT COUNT(*) as cnt FROM replication_queue').get();
|
|
3511
|
+
return row.cnt;
|
|
3512
|
+
}
|
|
3513
|
+
deserializeReplicationEntry(row) {
|
|
3514
|
+
return {
|
|
3515
|
+
id: row.id,
|
|
3516
|
+
type: row.type,
|
|
3517
|
+
targetPeers: JSON.parse(row.targetPeers),
|
|
3518
|
+
payload: row.payload ? JSON.parse(row.payload) : null,
|
|
3519
|
+
createdAt: row.createdAt,
|
|
3520
|
+
attempts: row.attempts,
|
|
3521
|
+
lastAttemptAt: row.lastAttemptAt || null,
|
|
3522
|
+
status: row.status,
|
|
3523
|
+
};
|
|
3524
|
+
}
|
|
3525
|
+
// ══════════════════════════════════════════════════════════
|
|
3526
|
+
// ── Device Authorization (RFC 8628) ──
|
|
3527
|
+
// ══════════════════════════════════════════════════════════
|
|
3528
|
+
async createDeviceAuth(req) {
|
|
3529
|
+
this.db.prepare(`INSERT INTO device_auth (deviceCode, userCode, ownerName, agentName, displayName, description, status, scopes, createdAt, expiresAt, lastPolledAt, pollInterval, approvedBy, agentCredentials)
|
|
3530
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(req.deviceCode, req.userCode, req.ownerName, req.agentName, req.displayName ?? null, req.description ?? null, req.status, req.scopes ? JSON.stringify(req.scopes) : null, req.createdAt, req.expiresAt, req.lastPolledAt ?? null, req.pollInterval, req.approvedBy ?? null, req.agentCredentials ? JSON.stringify(req.agentCredentials) : null);
|
|
3531
|
+
}
|
|
3532
|
+
async getDeviceAuthByDeviceCode(deviceCode) {
|
|
3533
|
+
const row = this.db.prepare('SELECT * FROM device_auth WHERE deviceCode = ?').get(deviceCode);
|
|
3534
|
+
return row ? this.deserializeDeviceAuth(row) : null;
|
|
3535
|
+
}
|
|
3536
|
+
async getDeviceAuthByUserCode(userCode) {
|
|
3537
|
+
const row = this.db.prepare('SELECT * FROM device_auth WHERE userCode = ?').get(userCode);
|
|
3538
|
+
return row ? this.deserializeDeviceAuth(row) : null;
|
|
3539
|
+
}
|
|
3540
|
+
async updateDeviceAuth(deviceCode, updates) {
|
|
3541
|
+
const fields = [];
|
|
3542
|
+
const values = [];
|
|
3543
|
+
if (updates.status !== undefined) {
|
|
3544
|
+
fields.push('status = ?');
|
|
3545
|
+
values.push(updates.status);
|
|
3546
|
+
}
|
|
3547
|
+
if (updates.scopes !== undefined) {
|
|
3548
|
+
fields.push('scopes = ?');
|
|
3549
|
+
values.push(JSON.stringify(updates.scopes));
|
|
3550
|
+
}
|
|
3551
|
+
if (updates.lastPolledAt !== undefined) {
|
|
3552
|
+
fields.push('lastPolledAt = ?');
|
|
3553
|
+
values.push(updates.lastPolledAt);
|
|
3554
|
+
}
|
|
3555
|
+
if (updates.pollInterval !== undefined) {
|
|
3556
|
+
fields.push('pollInterval = ?');
|
|
3557
|
+
values.push(updates.pollInterval);
|
|
3558
|
+
}
|
|
3559
|
+
if (updates.approvedBy !== undefined) {
|
|
3560
|
+
fields.push('approvedBy = ?');
|
|
3561
|
+
values.push(updates.approvedBy);
|
|
3562
|
+
}
|
|
3563
|
+
if ('agentCredentials' in updates) {
|
|
3564
|
+
fields.push('agentCredentials = ?');
|
|
3565
|
+
values.push(updates.agentCredentials ? JSON.stringify(updates.agentCredentials) : null);
|
|
3566
|
+
}
|
|
3567
|
+
if (fields.length === 0)
|
|
3568
|
+
return;
|
|
3569
|
+
values.push(deviceCode);
|
|
3570
|
+
this.db.prepare(`UPDATE device_auth SET ${fields.join(', ')} WHERE deviceCode = ?`).run(...values);
|
|
3571
|
+
}
|
|
3572
|
+
async countPendingDeviceAuthByOwner(ownerName) {
|
|
3573
|
+
const row = this.db.prepare(`SELECT COUNT(*) as cnt FROM device_auth WHERE ownerName = ? AND status = 'pending' AND expiresAt > ?`).get(ownerName, new Date().toISOString());
|
|
3574
|
+
return row.cnt;
|
|
3575
|
+
}
|
|
3576
|
+
async listPendingDeviceAuthByOwner(ownerName) {
|
|
3577
|
+
const rows = this.db.prepare(`SELECT * FROM device_auth WHERE ownerName = ? AND status = 'pending' AND expiresAt > ? ORDER BY createdAt DESC`).all(ownerName, new Date().toISOString());
|
|
3578
|
+
return rows.map(row => this.deserializeDeviceAuth(row));
|
|
3579
|
+
}
|
|
3580
|
+
async cleanupExpiredDeviceAuth() {
|
|
3581
|
+
const result = this.db.prepare(`DELETE FROM device_auth WHERE status = 'pending' AND expiresAt <= ?`).run(new Date().toISOString());
|
|
3582
|
+
return result.changes;
|
|
3583
|
+
}
|
|
3584
|
+
deserializeDeviceAuth(row) {
|
|
3585
|
+
return {
|
|
3586
|
+
deviceCode: row.deviceCode,
|
|
3587
|
+
userCode: row.userCode,
|
|
3588
|
+
ownerName: row.ownerName,
|
|
3589
|
+
agentName: row.agentName,
|
|
3590
|
+
displayName: row.displayName,
|
|
3591
|
+
description: row.description,
|
|
3592
|
+
status: row.status,
|
|
3593
|
+
scopes: row.scopes ? JSON.parse(row.scopes) : undefined,
|
|
3594
|
+
createdAt: row.createdAt,
|
|
3595
|
+
expiresAt: row.expiresAt,
|
|
3596
|
+
lastPolledAt: row.lastPolledAt,
|
|
3597
|
+
pollInterval: row.pollInterval,
|
|
3598
|
+
approvedBy: row.approvedBy,
|
|
3599
|
+
agentCredentials: row.agentCredentials ? JSON.parse(row.agentCredentials) : undefined,
|
|
3600
|
+
};
|
|
3601
|
+
}
|
|
3602
|
+
// ══════════════════════════════════════════════════════════
|
|
3603
|
+
// ── OAuth 2.1 Persistent State ──
|
|
3604
|
+
// ══════════════════════════════════════════════════════════
|
|
3605
|
+
// ── Clients ──
|
|
3606
|
+
async createOAuthClient(client) {
|
|
3607
|
+
this.db.prepare(`INSERT INTO oauth_clients (clientId, clientSecret, clientName, redirectUris, createdAt)
|
|
3608
|
+
VALUES (?, ?, ?, ?, ?)`).run(client.clientId, client.clientSecret, client.clientName, JSON.stringify(client.redirectUris), client.createdAt);
|
|
3609
|
+
}
|
|
3610
|
+
async getOAuthClient(clientId) {
|
|
3611
|
+
const row = this.db.prepare('SELECT * FROM oauth_clients WHERE clientId = ?').get(clientId);
|
|
3612
|
+
if (!row)
|
|
3613
|
+
return null;
|
|
3614
|
+
return {
|
|
3615
|
+
clientId: row.clientId,
|
|
3616
|
+
clientSecret: row.clientSecret,
|
|
3617
|
+
clientName: row.clientName,
|
|
3618
|
+
redirectUris: JSON.parse(row.redirectUris),
|
|
3619
|
+
createdAt: row.createdAt,
|
|
3620
|
+
};
|
|
3621
|
+
}
|
|
3622
|
+
async deleteOAuthClient(clientId) {
|
|
3623
|
+
const txn = this.db.transaction(() => {
|
|
3624
|
+
this.db.prepare('DELETE FROM oauth_refresh_tokens WHERE clientId = ?').run(clientId);
|
|
3625
|
+
this.db.prepare('DELETE FROM oauth_approvals WHERE clientId = ?').run(clientId);
|
|
3626
|
+
const result = this.db.prepare('DELETE FROM oauth_clients WHERE clientId = ?').run(clientId);
|
|
3627
|
+
return result.changes > 0;
|
|
3628
|
+
});
|
|
3629
|
+
return txn();
|
|
3630
|
+
}
|
|
3631
|
+
async listOAuthClients() {
|
|
3632
|
+
const rows = this.db.prepare('SELECT * FROM oauth_clients ORDER BY createdAt DESC').all();
|
|
3633
|
+
return rows.map(row => ({
|
|
3634
|
+
clientId: row.clientId,
|
|
3635
|
+
clientSecret: row.clientSecret,
|
|
3636
|
+
clientName: row.clientName,
|
|
3637
|
+
redirectUris: JSON.parse(row.redirectUris),
|
|
3638
|
+
createdAt: row.createdAt,
|
|
3639
|
+
}));
|
|
3640
|
+
}
|
|
3641
|
+
// ── Refresh Tokens ──
|
|
3642
|
+
async createOAuthRefreshToken(token) {
|
|
3643
|
+
this.db.prepare(`INSERT INTO oauth_refresh_tokens (tokenHash, clientId, gaii, owner, roles, createdAt)
|
|
3644
|
+
VALUES (?, ?, ?, ?, ?, ?)`).run(token.tokenHash, token.clientId, token.gaii, token.owner, JSON.stringify(token.roles), token.createdAt);
|
|
3645
|
+
}
|
|
3646
|
+
async getOAuthRefreshToken(tokenHash) {
|
|
3647
|
+
const row = this.db.prepare('SELECT * FROM oauth_refresh_tokens WHERE tokenHash = ?').get(tokenHash);
|
|
3648
|
+
if (!row)
|
|
3649
|
+
return null;
|
|
3650
|
+
return {
|
|
3651
|
+
tokenHash: row.tokenHash,
|
|
3652
|
+
clientId: row.clientId,
|
|
3653
|
+
gaii: row.gaii,
|
|
3654
|
+
owner: row.owner,
|
|
3655
|
+
roles: JSON.parse(row.roles),
|
|
3656
|
+
createdAt: row.createdAt,
|
|
3657
|
+
};
|
|
3658
|
+
}
|
|
3659
|
+
async deleteOAuthRefreshToken(tokenHash) {
|
|
3660
|
+
const result = this.db.prepare('DELETE FROM oauth_refresh_tokens WHERE tokenHash = ?').run(tokenHash);
|
|
3661
|
+
return result.changes > 0;
|
|
3662
|
+
}
|
|
3663
|
+
async deleteOAuthRefreshTokensByClient(clientId) {
|
|
3664
|
+
const result = this.db.prepare('DELETE FROM oauth_refresh_tokens WHERE clientId = ?').run(clientId);
|
|
3665
|
+
return result.changes;
|
|
3666
|
+
}
|
|
3667
|
+
async deleteOAuthRefreshTokensByGaii(gaii) {
|
|
3668
|
+
const result = this.db.prepare('DELETE FROM oauth_refresh_tokens WHERE gaii = ?').run(gaii);
|
|
3669
|
+
return result.changes;
|
|
3670
|
+
}
|
|
3671
|
+
// ── Approvals ──
|
|
3672
|
+
async createOAuthApproval(approval) {
|
|
3673
|
+
this.db.prepare(`INSERT OR REPLACE INTO oauth_approvals (clientId, gaii, owner, scope, approvedAt)
|
|
3674
|
+
VALUES (?, ?, ?, ?, ?)`).run(approval.clientId, approval.gaii, approval.owner, approval.scope, approval.approvedAt);
|
|
3675
|
+
}
|
|
3676
|
+
async getOAuthApproval(clientId, gaii) {
|
|
3677
|
+
const row = this.db.prepare('SELECT * FROM oauth_approvals WHERE clientId = ? AND gaii = ?').get(clientId, gaii);
|
|
3678
|
+
if (!row)
|
|
3679
|
+
return null;
|
|
3680
|
+
return {
|
|
3681
|
+
clientId: row.clientId,
|
|
3682
|
+
gaii: row.gaii,
|
|
3683
|
+
owner: row.owner,
|
|
3684
|
+
scope: row.scope,
|
|
3685
|
+
approvedAt: row.approvedAt,
|
|
3686
|
+
};
|
|
3687
|
+
}
|
|
3688
|
+
async deleteOAuthApproval(clientId, gaii) {
|
|
3689
|
+
const result = this.db.prepare('DELETE FROM oauth_approvals WHERE clientId = ? AND gaii = ?').run(clientId, gaii);
|
|
3690
|
+
return result.changes > 0;
|
|
3691
|
+
}
|
|
3692
|
+
async deleteOAuthApprovalsByClient(clientId) {
|
|
3693
|
+
const result = this.db.prepare('DELETE FROM oauth_approvals WHERE clientId = ?').run(clientId);
|
|
3694
|
+
return result.changes;
|
|
3695
|
+
}
|
|
3696
|
+
async deleteOAuthApprovalsByGaii(gaii) {
|
|
3697
|
+
const result = this.db.prepare('DELETE FROM oauth_approvals WHERE gaii = ?').run(gaii);
|
|
3698
|
+
return result.changes;
|
|
3699
|
+
}
|
|
3700
|
+
async listOAuthApprovalsByOwner(owner) {
|
|
3701
|
+
const rows = this.db.prepare('SELECT * FROM oauth_approvals WHERE owner = ? ORDER BY approvedAt DESC').all(owner);
|
|
3702
|
+
return rows.map(row => ({
|
|
3703
|
+
clientId: row.clientId,
|
|
3704
|
+
gaii: row.gaii,
|
|
3705
|
+
owner: row.owner,
|
|
3706
|
+
scope: row.scope,
|
|
3707
|
+
approvedAt: row.approvedAt,
|
|
3708
|
+
}));
|
|
3709
|
+
}
|
|
3710
|
+
// ── System Prompts ────────────────────────────────────────────────
|
|
3711
|
+
deserializeSystemPrompt(row) {
|
|
3712
|
+
return {
|
|
3713
|
+
id: row.id,
|
|
3714
|
+
group: row.grp,
|
|
3715
|
+
name: row.name,
|
|
3716
|
+
description: row.description,
|
|
3717
|
+
content: row.content,
|
|
3718
|
+
locales: row.locales ? JSON.parse(row.locales) : undefined,
|
|
3719
|
+
active: row.active === 1,
|
|
3720
|
+
variables: JSON.parse(row.variables),
|
|
3721
|
+
usedIn: JSON.parse(row.usedIn),
|
|
3722
|
+
version: row.version,
|
|
3723
|
+
updatedAt: row.updatedAt,
|
|
3724
|
+
updatedBy: row.updatedBy,
|
|
3725
|
+
};
|
|
3726
|
+
}
|
|
3727
|
+
deserializeSystemPromptVersion(row) {
|
|
3728
|
+
return {
|
|
3729
|
+
promptId: row.promptId,
|
|
3730
|
+
version: row.version,
|
|
3731
|
+
content: row.content,
|
|
3732
|
+
locales: row.locales ? JSON.parse(row.locales) : undefined,
|
|
3733
|
+
changedBy: row.changedBy,
|
|
3734
|
+
changedAt: row.changedAt,
|
|
3735
|
+
changeNote: row.changeNote,
|
|
3736
|
+
};
|
|
3737
|
+
}
|
|
3738
|
+
async listSystemPrompts(opts) {
|
|
3739
|
+
const sql = opts?.group
|
|
3740
|
+
? 'SELECT * FROM system_prompts WHERE grp = ? ORDER BY grp, name'
|
|
3741
|
+
: 'SELECT * FROM system_prompts ORDER BY grp, name';
|
|
3742
|
+
const rows = (opts?.group
|
|
3743
|
+
? this.db.prepare(sql).all(opts.group)
|
|
3744
|
+
: this.db.prepare(sql).all());
|
|
3745
|
+
return rows.map(r => this.deserializeSystemPrompt(r));
|
|
3746
|
+
}
|
|
3747
|
+
async getSystemPrompt(id) {
|
|
3748
|
+
const row = this.db.prepare('SELECT * FROM system_prompts WHERE id = ?').get(id);
|
|
3749
|
+
return row ? this.deserializeSystemPrompt(row) : null;
|
|
3750
|
+
}
|
|
3751
|
+
async upsertSystemPrompt(record) {
|
|
3752
|
+
this.db.prepare(`INSERT INTO system_prompts (id, grp, name, description, content, locales, active, variables, usedIn, version, updatedAt, updatedBy)
|
|
3753
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
3754
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
3755
|
+
grp = excluded.grp, name = excluded.name, description = excluded.description,
|
|
3756
|
+
content = excluded.content, locales = excluded.locales, active = excluded.active,
|
|
3757
|
+
variables = excluded.variables, usedIn = excluded.usedIn, version = excluded.version,
|
|
3758
|
+
updatedAt = excluded.updatedAt, updatedBy = excluded.updatedBy`).run(record.id, record.group, record.name, record.description, record.content, record.locales ? JSON.stringify(record.locales) : null, record.active ? 1 : 0, JSON.stringify(record.variables), JSON.stringify(record.usedIn), record.version, record.updatedAt, record.updatedBy);
|
|
3759
|
+
return record;
|
|
3760
|
+
}
|
|
3761
|
+
async getSystemPromptVersions(promptId) {
|
|
3762
|
+
const rows = this.db.prepare('SELECT * FROM system_prompt_versions WHERE promptId = ? ORDER BY version DESC').all(promptId);
|
|
3763
|
+
return rows.map(r => this.deserializeSystemPromptVersion(r));
|
|
3764
|
+
}
|
|
3765
|
+
async getSystemPromptVersion(promptId, version) {
|
|
3766
|
+
const row = this.db.prepare('SELECT * FROM system_prompt_versions WHERE promptId = ? AND version = ?').get(promptId, version);
|
|
3767
|
+
return row ? this.deserializeSystemPromptVersion(row) : null;
|
|
3768
|
+
}
|
|
3769
|
+
async createSystemPromptVersion(record) {
|
|
3770
|
+
this.db.prepare(`INSERT INTO system_prompt_versions (promptId, version, content, locales, changedBy, changedAt, changeNote)
|
|
3771
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`).run(record.promptId, record.version, record.content, record.locales ? JSON.stringify(record.locales) : null, record.changedBy, record.changedAt, record.changeNote ?? null);
|
|
3772
|
+
return record;
|
|
3773
|
+
}
|
|
3774
|
+
async pruneSystemPromptVersions(promptId, keepCount) {
|
|
3775
|
+
const result = this.db.prepare(`DELETE FROM system_prompt_versions WHERE promptId = ? AND version NOT IN (
|
|
3776
|
+
SELECT version FROM system_prompt_versions WHERE promptId = ? ORDER BY version DESC LIMIT ?
|
|
3777
|
+
)`).run(promptId, promptId, keepCount);
|
|
3778
|
+
return result.changes;
|
|
3779
|
+
}
|
|
3780
|
+
async deleteAllSystemPrompts() {
|
|
3781
|
+
this.db.prepare('DELETE FROM system_prompt_versions').run();
|
|
3782
|
+
this.db.prepare('DELETE FROM system_prompts').run();
|
|
3783
|
+
}
|
|
3784
|
+
// ══════════════════════════════════════════════════════════
|
|
3785
|
+
// ── Packages ──
|
|
3786
|
+
// ══════════════════════════════════════════════════════════
|
|
3787
|
+
deserializePackage(row) {
|
|
3788
|
+
return {
|
|
3789
|
+
id: row.id,
|
|
3790
|
+
packageGroupId: row.packageGroupId,
|
|
3791
|
+
name: row.name,
|
|
3792
|
+
author: row.author,
|
|
3793
|
+
authorGhii: row.authorGhii,
|
|
3794
|
+
version: row.version,
|
|
3795
|
+
changelog: row.changelog,
|
|
3796
|
+
description: row.description,
|
|
3797
|
+
category: row.category,
|
|
3798
|
+
tags: JSON.parse(row.tags),
|
|
3799
|
+
visibility: row.visibility,
|
|
3800
|
+
status: row.status,
|
|
3801
|
+
components: JSON.parse(row.components),
|
|
3802
|
+
manifest: row.manifest,
|
|
3803
|
+
createdAt: row.createdAt,
|
|
3804
|
+
updatedAt: row.updatedAt,
|
|
3805
|
+
};
|
|
3806
|
+
}
|
|
3807
|
+
async createPackage(record) {
|
|
3808
|
+
try {
|
|
3809
|
+
this.db.prepare(`INSERT INTO packages (id, packageGroupId, name, author, authorGhii, version, changelog, description, category, tags, visibility, status, components, manifest, createdAt, updatedAt)
|
|
3810
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(record.id, record.packageGroupId, record.name, record.author, record.authorGhii, record.version, record.changelog, record.description, record.category, JSON.stringify(record.tags), record.visibility, record.status, JSON.stringify(record.components), record.manifest, record.createdAt, record.updatedAt);
|
|
3811
|
+
}
|
|
3812
|
+
catch (e) {
|
|
3813
|
+
if (e.message?.includes('UNIQUE constraint failed')) {
|
|
3814
|
+
throw new Error('PACKAGE_EXISTS', { cause: e });
|
|
3815
|
+
}
|
|
3816
|
+
throw e;
|
|
3817
|
+
}
|
|
3818
|
+
return record;
|
|
3819
|
+
}
|
|
3820
|
+
async getPackage(id) {
|
|
3821
|
+
const row = this.db.prepare('SELECT * FROM packages WHERE id = ?').get(id);
|
|
3822
|
+
return row ? this.deserializePackage(row) : null;
|
|
3823
|
+
}
|
|
3824
|
+
async getPackageByGroupAndVersion(groupId, version) {
|
|
3825
|
+
const row = this.db.prepare('SELECT * FROM packages WHERE packageGroupId = ? AND version = ?').get(groupId, version);
|
|
3826
|
+
return row ? this.deserializePackage(row) : null;
|
|
3827
|
+
}
|
|
3828
|
+
async getLatestPublished(groupId) {
|
|
3829
|
+
const row = this.db.prepare(`SELECT * FROM packages WHERE packageGroupId = ? AND status = 'published' ORDER BY version DESC LIMIT 1`).get(groupId);
|
|
3830
|
+
return row ? this.deserializePackage(row) : null;
|
|
3831
|
+
}
|
|
3832
|
+
async listPackages(filter) {
|
|
3833
|
+
const conditions = [];
|
|
3834
|
+
const params = [];
|
|
3835
|
+
if (filter.author) {
|
|
3836
|
+
conditions.push('author = ?');
|
|
3837
|
+
params.push(filter.author);
|
|
3838
|
+
}
|
|
3839
|
+
if (filter.category) {
|
|
3840
|
+
conditions.push('category = ?');
|
|
3841
|
+
params.push(filter.category);
|
|
3842
|
+
}
|
|
3843
|
+
if (filter.status) {
|
|
3844
|
+
conditions.push('status = ?');
|
|
3845
|
+
params.push(filter.status);
|
|
3846
|
+
}
|
|
3847
|
+
if (filter.visibility) {
|
|
3848
|
+
conditions.push('visibility = ?');
|
|
3849
|
+
params.push(filter.visibility);
|
|
3850
|
+
}
|
|
3851
|
+
if (filter.search) {
|
|
3852
|
+
conditions.push('(name LIKE ? OR description LIKE ? OR tags LIKE ?)');
|
|
3853
|
+
const s = `%${filter.search}%`;
|
|
3854
|
+
params.push(s, s, s);
|
|
3855
|
+
}
|
|
3856
|
+
const where = conditions.length ? `WHERE ${conditions.join(' AND ')}` : '';
|
|
3857
|
+
const limit = filter.limit ?? 50;
|
|
3858
|
+
const offset = filter.offset ?? 0;
|
|
3859
|
+
const total = this.db.prepare(`SELECT COUNT(*) as c FROM packages ${where}`).get(...params).c;
|
|
3860
|
+
const rows = this.db.prepare(`SELECT * FROM packages ${where} ORDER BY createdAt DESC LIMIT ? OFFSET ?`).all(...params, limit, offset);
|
|
3861
|
+
return { packages: rows.map(r => this.deserializePackage(r)), total };
|
|
3862
|
+
}
|
|
3863
|
+
async listVersions(groupId, limit, offset) {
|
|
3864
|
+
const lim = limit ?? 50;
|
|
3865
|
+
const off = offset ?? 0;
|
|
3866
|
+
const total = this.db.prepare('SELECT COUNT(*) as c FROM packages WHERE packageGroupId = ?').get(groupId).c;
|
|
3867
|
+
const rows = this.db.prepare('SELECT * FROM packages WHERE packageGroupId = ? ORDER BY version DESC LIMIT ? OFFSET ?').all(groupId, lim, off);
|
|
3868
|
+
return { versions: rows.map(r => this.deserializePackage(r)), total };
|
|
3869
|
+
}
|
|
3870
|
+
async updatePackage(id, updates) {
|
|
3871
|
+
const existing = await this.getPackage(id);
|
|
3872
|
+
if (!existing)
|
|
3873
|
+
return null;
|
|
3874
|
+
const merged = { ...existing, ...updates, id };
|
|
3875
|
+
this.db.prepare(`UPDATE packages SET packageGroupId = ?, name = ?, author = ?, authorGhii = ?, version = ?,
|
|
3876
|
+
changelog = ?, description = ?, category = ?, tags = ?, visibility = ?, status = ?,
|
|
3877
|
+
components = ?, manifest = ?, updatedAt = ? WHERE id = ?`).run(merged.packageGroupId, merged.name, merged.author, merged.authorGhii, merged.version, merged.changelog, merged.description, merged.category, JSON.stringify(merged.tags), merged.visibility, merged.status, JSON.stringify(merged.components), merged.manifest, merged.updatedAt, id);
|
|
3878
|
+
return merged;
|
|
3879
|
+
}
|
|
3880
|
+
async archivePackage(id) {
|
|
3881
|
+
const result = this.db.prepare(`UPDATE packages SET status = 'archived', updatedAt = ? WHERE id = ?`).run(new Date().toISOString(), id);
|
|
3882
|
+
return result.changes > 0;
|
|
3883
|
+
}
|
|
3884
|
+
// ══════════════════════════════════════════════════════════
|
|
3885
|
+
// ── Template Listings ──
|
|
3886
|
+
// ══════════════════════════════════════════════════════════
|
|
3887
|
+
deserializeTemplateListing(row) {
|
|
3888
|
+
return {
|
|
3889
|
+
id: row.id,
|
|
3890
|
+
packageGroupId: row.packageGroupId,
|
|
3891
|
+
packageName: row.packageName,
|
|
3892
|
+
packageAuthor: row.packageAuthor,
|
|
3893
|
+
publishedBy: row.publishedBy,
|
|
3894
|
+
publishedByGhii: row.publishedByGhii,
|
|
3895
|
+
title: row.title,
|
|
3896
|
+
description: row.description,
|
|
3897
|
+
screenshots: JSON.parse(row.screenshots),
|
|
3898
|
+
category: row.category,
|
|
3899
|
+
tags: JSON.parse(row.tags),
|
|
3900
|
+
featured: !!row.featured,
|
|
3901
|
+
installCount: row.installCount,
|
|
3902
|
+
rating: row.rating,
|
|
3903
|
+
reviewCount: row.reviewCount,
|
|
3904
|
+
status: row.status,
|
|
3905
|
+
createdAt: row.createdAt,
|
|
3906
|
+
updatedAt: row.updatedAt,
|
|
3907
|
+
...(row.rejectionReason ? { rejectionReason: row.rejectionReason } : {}),
|
|
3908
|
+
...(row.reviewedBy ? { reviewedBy: row.reviewedBy } : {}),
|
|
3909
|
+
...(row.reviewedAt ? { reviewedAt: row.reviewedAt } : {}),
|
|
3910
|
+
...(row.reviewComment ? { reviewComment: row.reviewComment } : {}),
|
|
3911
|
+
...(row.proposedAt ? { proposedAt: row.proposedAt } : {}),
|
|
3912
|
+
...(row.proposedBy ? { proposedBy: row.proposedBy } : {}),
|
|
3913
|
+
};
|
|
3914
|
+
}
|
|
3915
|
+
deserializeReview(row) {
|
|
3916
|
+
return {
|
|
3917
|
+
id: row.id,
|
|
3918
|
+
listingId: row.listingId,
|
|
3919
|
+
authorGhii: row.authorGhii,
|
|
3920
|
+
authorName: row.authorName,
|
|
3921
|
+
rating: row.rating,
|
|
3922
|
+
comment: row.comment,
|
|
3923
|
+
createdAt: row.createdAt,
|
|
3924
|
+
};
|
|
3925
|
+
}
|
|
3926
|
+
deserializeDiscussion(row) {
|
|
3927
|
+
return {
|
|
3928
|
+
id: row.id,
|
|
3929
|
+
listingId: row.listingId,
|
|
3930
|
+
authorGhii: row.authorGhii,
|
|
3931
|
+
authorName: row.authorName,
|
|
3932
|
+
message: row.message,
|
|
3933
|
+
parentId: row.parentId,
|
|
3934
|
+
createdAt: row.createdAt,
|
|
3935
|
+
};
|
|
3936
|
+
}
|
|
3937
|
+
async createTemplateListing(record) {
|
|
3938
|
+
this.db.prepare(`INSERT INTO template_listings (id, packageGroupId, packageName, packageAuthor, publishedBy, publishedByGhii, title, description, screenshots, category, tags, featured, installCount, rating, reviewCount, status, createdAt, updatedAt, rejectionReason, reviewedBy, reviewedAt, reviewComment, proposedAt, proposedBy)
|
|
3939
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(record.id, record.packageGroupId, record.packageName, record.packageAuthor, record.publishedBy, record.publishedByGhii, record.title, record.description, JSON.stringify(record.screenshots), record.category, JSON.stringify(record.tags), record.featured ? 1 : 0, record.installCount, record.rating, record.reviewCount, record.status, record.createdAt, record.updatedAt, record.rejectionReason ?? null, record.reviewedBy ?? null, record.reviewedAt ?? null, record.reviewComment ?? null, record.proposedAt ?? null, record.proposedBy ?? null);
|
|
3940
|
+
return record;
|
|
3941
|
+
}
|
|
3942
|
+
async getTemplateListing(id) {
|
|
3943
|
+
const row = this.db.prepare('SELECT * FROM template_listings WHERE id = ?').get(id);
|
|
3944
|
+
return row ? this.deserializeTemplateListing(row) : null;
|
|
3945
|
+
}
|
|
3946
|
+
async getListingByPackage(packageGroupId) {
|
|
3947
|
+
const row = this.db.prepare('SELECT * FROM template_listings WHERE packageGroupId = ?').get(packageGroupId);
|
|
3948
|
+
return row ? this.deserializeTemplateListing(row) : null;
|
|
3949
|
+
}
|
|
3950
|
+
async listTemplateListings(filter) {
|
|
3951
|
+
const conditions = [];
|
|
3952
|
+
const params = [];
|
|
3953
|
+
if (filter.category) {
|
|
3954
|
+
conditions.push('category = ?');
|
|
3955
|
+
params.push(filter.category);
|
|
3956
|
+
}
|
|
3957
|
+
if (filter.tags && filter.tags.length > 0) {
|
|
3958
|
+
const tagConditions = filter.tags.map(() => 'tags LIKE ?');
|
|
3959
|
+
conditions.push(`(${tagConditions.join(' AND ')})`);
|
|
3960
|
+
for (const tag of filter.tags)
|
|
3961
|
+
params.push(`%${tag}%`);
|
|
3962
|
+
}
|
|
3963
|
+
if (filter.featured !== undefined) {
|
|
3964
|
+
conditions.push('featured = ?');
|
|
3965
|
+
params.push(filter.featured ? 1 : 0);
|
|
3966
|
+
}
|
|
3967
|
+
if (filter.status) {
|
|
3968
|
+
conditions.push('status = ?');
|
|
3969
|
+
params.push(filter.status);
|
|
3970
|
+
}
|
|
3971
|
+
if (filter.search) {
|
|
3972
|
+
conditions.push('(title LIKE ? OR description LIKE ? OR tags LIKE ?)');
|
|
3973
|
+
const s = `%${filter.search}%`;
|
|
3974
|
+
params.push(s, s, s);
|
|
3975
|
+
}
|
|
3976
|
+
const where = conditions.length ? `WHERE ${conditions.join(' AND ')}` : '';
|
|
3977
|
+
const limit = filter.limit ?? 50;
|
|
3978
|
+
const offset = filter.offset ?? 0;
|
|
3979
|
+
let orderBy;
|
|
3980
|
+
switch (filter.sort) {
|
|
3981
|
+
case 'rating':
|
|
3982
|
+
orderBy = 'rating DESC';
|
|
3983
|
+
break;
|
|
3984
|
+
case 'installs':
|
|
3985
|
+
orderBy = 'installCount DESC';
|
|
3986
|
+
break;
|
|
3987
|
+
case 'newest':
|
|
3988
|
+
orderBy = 'createdAt DESC';
|
|
3989
|
+
break;
|
|
3990
|
+
default: orderBy = 'createdAt DESC';
|
|
3991
|
+
}
|
|
3992
|
+
const total = this.db.prepare(`SELECT COUNT(*) as c FROM template_listings ${where}`).get(...params).c;
|
|
3993
|
+
const rows = this.db.prepare(`SELECT * FROM template_listings ${where} ORDER BY ${orderBy} LIMIT ? OFFSET ?`).all(...params, limit, offset);
|
|
3994
|
+
return { listings: rows.map(r => this.deserializeTemplateListing(r)), total };
|
|
3995
|
+
}
|
|
3996
|
+
async updateTemplateListing(id, updates) {
|
|
3997
|
+
const existing = await this.getTemplateListing(id);
|
|
3998
|
+
if (!existing)
|
|
3999
|
+
return null;
|
|
4000
|
+
const merged = { ...existing, ...updates, id };
|
|
4001
|
+
this.db.prepare(`UPDATE template_listings SET packageGroupId = ?, packageName = ?, packageAuthor = ?,
|
|
4002
|
+
publishedBy = ?, publishedByGhii = ?, title = ?, description = ?, screenshots = ?,
|
|
4003
|
+
category = ?, tags = ?, featured = ?, installCount = ?, rating = ?, reviewCount = ?,
|
|
4004
|
+
status = ?, updatedAt = ?, rejectionReason = ?, reviewedBy = ?, reviewedAt = ?,
|
|
4005
|
+
reviewComment = ?, proposedAt = ?, proposedBy = ? WHERE id = ?`).run(merged.packageGroupId, merged.packageName, merged.packageAuthor, merged.publishedBy, merged.publishedByGhii, merged.title, merged.description, JSON.stringify(merged.screenshots), merged.category, JSON.stringify(merged.tags), merged.featured ? 1 : 0, merged.installCount, merged.rating, merged.reviewCount, merged.status, merged.updatedAt, merged.rejectionReason ?? null, merged.reviewedBy ?? null, merged.reviewedAt ?? null, merged.reviewComment ?? null, merged.proposedAt ?? null, merged.proposedBy ?? null, id);
|
|
4006
|
+
return merged;
|
|
4007
|
+
}
|
|
4008
|
+
async deleteTemplateListing(id) {
|
|
4009
|
+
this.db.prepare('DELETE FROM template_reviews WHERE listingId = ?').run(id);
|
|
4010
|
+
this.db.prepare('DELETE FROM template_discussions WHERE listingId = ?').run(id);
|
|
4011
|
+
const result = this.db.prepare('DELETE FROM template_listings WHERE id = ?').run(id);
|
|
4012
|
+
return result.changes > 0;
|
|
4013
|
+
}
|
|
4014
|
+
async incrementInstallCount(listingId) {
|
|
4015
|
+
this.db.prepare('UPDATE template_listings SET installCount = installCount + 1 WHERE id = ?').run(listingId);
|
|
4016
|
+
}
|
|
4017
|
+
async listPendingTemplates(limit = 20, offset = 0) {
|
|
4018
|
+
const rows = this.db.prepare('SELECT * FROM template_listings WHERE status = ? ORDER BY createdAt ASC LIMIT ? OFFSET ?').all('pending_review', limit, offset);
|
|
4019
|
+
return rows.map(r => this.deserializeTemplateListing(r));
|
|
4020
|
+
}
|
|
4021
|
+
async addReview(review) {
|
|
4022
|
+
this.db.prepare(`INSERT OR REPLACE INTO template_reviews (id, listingId, authorGhii, authorName, rating, comment, createdAt)
|
|
4023
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`).run(review.id, review.listingId, review.authorGhii, review.authorName, review.rating, review.comment, review.createdAt);
|
|
4024
|
+
await this.recalculateRating(review.listingId);
|
|
4025
|
+
return review;
|
|
4026
|
+
}
|
|
4027
|
+
async getReviewsByListing(listingId, limit, offset) {
|
|
4028
|
+
const lim = limit ?? 50;
|
|
4029
|
+
const off = offset ?? 0;
|
|
4030
|
+
const total = this.db.prepare('SELECT COUNT(*) as c FROM template_reviews WHERE listingId = ?').get(listingId).c;
|
|
4031
|
+
const rows = this.db.prepare('SELECT * FROM template_reviews WHERE listingId = ? ORDER BY createdAt DESC LIMIT ? OFFSET ?').all(listingId, lim, off);
|
|
4032
|
+
return { reviews: rows.map(r => this.deserializeReview(r)), total };
|
|
4033
|
+
}
|
|
4034
|
+
async getReviewByAuthor(listingId, authorGhii) {
|
|
4035
|
+
const row = this.db.prepare('SELECT * FROM template_reviews WHERE listingId = ? AND authorGhii = ?').get(listingId, authorGhii);
|
|
4036
|
+
return row ? this.deserializeReview(row) : null;
|
|
4037
|
+
}
|
|
4038
|
+
async updateReview(id, updates) {
|
|
4039
|
+
const row = this.db.prepare('SELECT * FROM template_reviews WHERE id = ?').get(id);
|
|
4040
|
+
if (!row)
|
|
4041
|
+
return null;
|
|
4042
|
+
const existing = this.deserializeReview(row);
|
|
4043
|
+
const merged = { ...existing, ...updates, id };
|
|
4044
|
+
this.db.prepare('UPDATE template_reviews SET rating = ?, comment = ? WHERE id = ?').run(merged.rating, merged.comment, id);
|
|
4045
|
+
await this.recalculateRating(merged.listingId);
|
|
4046
|
+
return merged;
|
|
4047
|
+
}
|
|
4048
|
+
async deleteReview(id) {
|
|
4049
|
+
const row = this.db.prepare('SELECT * FROM template_reviews WHERE id = ?').get(id);
|
|
4050
|
+
if (!row)
|
|
4051
|
+
return false;
|
|
4052
|
+
const listingId = row.listingId;
|
|
4053
|
+
const result = this.db.prepare('DELETE FROM template_reviews WHERE id = ?').run(id);
|
|
4054
|
+
if (result.changes > 0) {
|
|
4055
|
+
await this.recalculateRating(listingId);
|
|
4056
|
+
return true;
|
|
4057
|
+
}
|
|
4058
|
+
return false;
|
|
4059
|
+
}
|
|
4060
|
+
async recalculateRating(listingId) {
|
|
4061
|
+
const stats = this.db.prepare('SELECT AVG(rating) as avg, COUNT(*) as cnt FROM template_reviews WHERE listingId = ?').get(listingId);
|
|
4062
|
+
const rating = stats.avg ?? 0;
|
|
4063
|
+
const reviewCount = stats.cnt;
|
|
4064
|
+
this.db.prepare('UPDATE template_listings SET rating = ?, reviewCount = ? WHERE id = ?').run(rating, reviewCount, listingId);
|
|
4065
|
+
return { rating, reviewCount };
|
|
4066
|
+
}
|
|
4067
|
+
async addDiscussion(discussion) {
|
|
4068
|
+
this.db.prepare(`INSERT INTO template_discussions (id, listingId, authorGhii, authorName, message, parentId, createdAt)
|
|
4069
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`).run(discussion.id, discussion.listingId, discussion.authorGhii, discussion.authorName, discussion.message, discussion.parentId ?? null, discussion.createdAt);
|
|
4070
|
+
return discussion;
|
|
4071
|
+
}
|
|
4072
|
+
async getDiscussionsByListing(listingId, limit, offset) {
|
|
4073
|
+
const lim = limit ?? 50;
|
|
4074
|
+
const off = offset ?? 0;
|
|
4075
|
+
const total = this.db.prepare('SELECT COUNT(*) as c FROM template_discussions WHERE listingId = ?').get(listingId).c;
|
|
4076
|
+
const rows = this.db.prepare('SELECT * FROM template_discussions WHERE listingId = ? ORDER BY createdAt ASC LIMIT ? OFFSET ?').all(listingId, lim, off);
|
|
4077
|
+
return { discussions: rows.map(r => this.deserializeDiscussion(r)), total };
|
|
4078
|
+
}
|
|
4079
|
+
async deleteDiscussion(id) {
|
|
4080
|
+
const result = this.db.prepare('DELETE FROM template_discussions WHERE id = ?').run(id);
|
|
4081
|
+
return result.changes > 0;
|
|
4082
|
+
}
|
|
4083
|
+
// ══════════════════════════════════════════════════════════
|
|
4084
|
+
// ── Package Instances ──
|
|
4085
|
+
// ══════════════════════════════════════════════════════════
|
|
4086
|
+
deserializeInstance(row) {
|
|
4087
|
+
return {
|
|
4088
|
+
id: row.id,
|
|
4089
|
+
packageGroupId: row.packageGroupId,
|
|
4090
|
+
packageVersion: row.packageVersion,
|
|
4091
|
+
packageRecordId: row.packageRecordId,
|
|
4092
|
+
owner: row.owner,
|
|
4093
|
+
ownerGhii: row.ownerGhii,
|
|
4094
|
+
label: row.label,
|
|
4095
|
+
installedComponents: JSON.parse(row.installedComponents),
|
|
4096
|
+
status: row.status,
|
|
4097
|
+
installedAt: row.installedAt,
|
|
4098
|
+
updatedAt: row.updatedAt,
|
|
4099
|
+
};
|
|
4100
|
+
}
|
|
4101
|
+
async createInstance(record) {
|
|
4102
|
+
this.db.prepare(`INSERT INTO package_instances (id, packageGroupId, packageVersion, packageRecordId, owner, ownerGhii, label, installedComponents, status, installedAt, updatedAt)
|
|
4103
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(record.id, record.packageGroupId, record.packageVersion, record.packageRecordId, record.owner, record.ownerGhii, record.label, JSON.stringify(record.installedComponents), record.status, record.installedAt, record.updatedAt);
|
|
4104
|
+
return record;
|
|
4105
|
+
}
|
|
4106
|
+
async getInstance(id) {
|
|
4107
|
+
const row = this.db.prepare('SELECT * FROM package_instances WHERE id = ?').get(id);
|
|
4108
|
+
return row ? this.deserializeInstance(row) : null;
|
|
4109
|
+
}
|
|
4110
|
+
async listInstances(filter) {
|
|
4111
|
+
const conditions = [];
|
|
4112
|
+
const params = [];
|
|
4113
|
+
if (filter.owner) {
|
|
4114
|
+
conditions.push('owner = ?');
|
|
4115
|
+
params.push(filter.owner);
|
|
4116
|
+
}
|
|
4117
|
+
if (filter.ownerGhii) {
|
|
4118
|
+
conditions.push('ownerGhii = ?');
|
|
4119
|
+
params.push(filter.ownerGhii);
|
|
4120
|
+
}
|
|
4121
|
+
if (filter.packageGroupId) {
|
|
4122
|
+
conditions.push('packageGroupId = ?');
|
|
4123
|
+
params.push(filter.packageGroupId);
|
|
4124
|
+
}
|
|
4125
|
+
if (filter.status) {
|
|
4126
|
+
conditions.push('status = ?');
|
|
4127
|
+
params.push(filter.status);
|
|
4128
|
+
}
|
|
4129
|
+
const where = conditions.length ? `WHERE ${conditions.join(' AND ')}` : '';
|
|
4130
|
+
const limit = filter.limit ?? 50;
|
|
4131
|
+
const offset = filter.offset ?? 0;
|
|
4132
|
+
const total = this.db.prepare(`SELECT COUNT(*) as c FROM package_instances ${where}`).get(...params).c;
|
|
4133
|
+
const rows = this.db.prepare(`SELECT * FROM package_instances ${where} ORDER BY installedAt DESC LIMIT ? OFFSET ?`).all(...params, limit, offset);
|
|
4134
|
+
return { instances: rows.map(r => this.deserializeInstance(r)), total };
|
|
4135
|
+
}
|
|
4136
|
+
async updateInstance(id, updates) {
|
|
4137
|
+
const existing = await this.getInstance(id);
|
|
4138
|
+
if (!existing)
|
|
4139
|
+
return null;
|
|
4140
|
+
const merged = { ...existing, ...updates, id };
|
|
4141
|
+
this.db.prepare(`UPDATE package_instances SET packageGroupId = ?, packageVersion = ?, packageRecordId = ?,
|
|
4142
|
+
owner = ?, ownerGhii = ?, label = ?, installedComponents = ?, status = ?, updatedAt = ?
|
|
4143
|
+
WHERE id = ?`).run(merged.packageGroupId, merged.packageVersion, merged.packageRecordId, merged.owner, merged.ownerGhii, merged.label, JSON.stringify(merged.installedComponents), merged.status, merged.updatedAt, id);
|
|
4144
|
+
return merged;
|
|
4145
|
+
}
|
|
4146
|
+
async deleteInstance(id) {
|
|
4147
|
+
const result = this.db.prepare('DELETE FROM package_instances WHERE id = ?').run(id);
|
|
4148
|
+
return result.changes > 0;
|
|
4149
|
+
}
|
|
4150
|
+
async listInstancesByPackage(packageGroupId) {
|
|
4151
|
+
const total = this.db.prepare('SELECT COUNT(*) as c FROM package_instances WHERE packageGroupId = ?').get(packageGroupId).c;
|
|
4152
|
+
const rows = this.db.prepare('SELECT * FROM package_instances WHERE packageGroupId = ? ORDER BY installedAt DESC').all(packageGroupId);
|
|
4153
|
+
return { instances: rows.map(r => this.deserializeInstance(r)), total };
|
|
4154
|
+
}
|
|
4155
|
+
}
|
|
4156
|
+
//# sourceMappingURL=index.js.map
|