@xopcai/xopc 0.0.188 → 0.0.189
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/README.md +1 -1
- package/dist/browser-ext/manifest.json +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/{activity-store--QoUGhiS.js → activity-store-DTFKTPs8.js} +1 -1
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-B0yweLNQ.js → agent-avatar-dicebear-DqO25Qe5.js} +1 -1
- package/dist/gateway/static/root/assets/agents-BvdqdER9.js +2 -0
- package/dist/gateway/static/root/assets/{api-5laWqfiE.js → api-DWeXfwVU.js} +1 -1
- package/dist/gateway/static/root/assets/{app-management-settings-panel-BXJ8Jiv_.js → app-management-settings-panel-BRpV8PXG.js} +1 -1
- package/dist/gateway/static/root/assets/{appearance-settings-DG9n9D1M.js → appearance-settings-3ntKM9nQ.js} +1 -1
- package/dist/gateway/static/root/assets/apps-page-nKLsfy6C.js +1 -0
- package/dist/gateway/static/root/assets/{archive-plugin-Ceh5qrZf.js → archive-plugin-Df7Ycj_7.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-jFW7uYf7.js → attachment-load-UHgfZYtj.js} +1 -1
- package/dist/gateway/static/root/assets/automations-page-Dh5bdZiN.js +6 -0
- package/dist/gateway/static/root/assets/{binary-plugins-XDlkI1r4.js → binary-plugins-BJah99wE.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-kyKc2IhE.js → block-editor-DNlY5KqF.js} +46 -46
- package/dist/gateway/static/root/assets/browser-settings-page-CRJ_nAuh.js +8 -0
- package/dist/gateway/static/root/assets/{browser-workflow-inputs-BJa5Iz_v.js → browser-workflow-inputs-vyUfdzAd.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-workflows-page-CTQQOsn5.js → browser-workflows-page-C1cKZjx-.js} +2 -2
- package/dist/gateway/static/root/assets/capabilities-settings-panel-DeQR5BZt.js +7 -0
- package/dist/gateway/static/root/assets/{capability-presets-api-Dn-7MMEo.js → capability-presets-api-D7tkhDMv.js} +1 -1
- package/dist/gateway/static/root/assets/capability-presets-settings-panel-D7uK3q1p.js +1 -0
- package/dist/gateway/static/root/assets/{channel-recipient-api-BgIkWq01.js → channel-recipient-api-BGG2kpJB.js} +1 -1
- package/dist/gateway/static/root/assets/channels-settings-Bw6Dniyq.js +1 -0
- package/dist/gateway/static/root/assets/channels-status-swr-BrUTz4uU.js +1 -0
- package/dist/gateway/static/root/assets/{connector-logo-TRr9ybf6.js → connector-logo-8YU53RUT.js} +1 -1
- package/dist/gateway/static/root/assets/connectors-page-k9tMiOL5.js +2 -0
- package/dist/gateway/static/root/assets/{desktop-pet-CSwd00CV.js → desktop-pet-BUuBEJuP.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-settings-CuNQvHGu.js → desktop-pet-settings-BGfVIJM3.js} +1 -1
- package/dist/gateway/static/root/assets/{directory-picker-path-field-ct0n5Rje.js → directory-picker-path-field-DrQFfHH_.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-BeRBuMUU.js → extension-debug-page-CEnIFOX-.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-DbK-Cwao.js → extension-page-CCqHbexs.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-C9ag5tb5.js → extension-settings-page-DEZw_7zN.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-api-CC7EvEFp.js → gateway-config-api-CgUE2Ii1.js} +1 -1
- package/dist/gateway/static/root/assets/gateway-settings-Dg8qCiIp.js +1 -0
- package/dist/gateway/static/root/assets/{gateway-startup-retry-B2DmdlwO.js → gateway-startup-retry-DxhKNiCF.js} +1 -1
- package/dist/gateway/static/root/assets/{heartbeat-settings-fl1oC_-5.js → heartbeat-settings-27tRzDEo.js} +1 -1
- package/dist/gateway/static/root/assets/home-page-VA511kMr.js +3 -0
- package/dist/gateway/static/root/assets/{image-generation-api-GMTaS6eI.js → image-generation-api-DCFdc-HD.js} +1 -1
- package/dist/gateway/static/root/assets/index-BvR-i-m6.js +216 -0
- package/dist/gateway/static/root/assets/index-zybP34zk.css +1 -0
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-DhGYddbI.js → keyboard-shortcuts-settings-Bf2VLtXW.js} +1 -1
- package/dist/gateway/static/root/assets/local-app-workbench-page-CaJq4-wV.js +2 -0
- package/dist/gateway/static/root/assets/{local-apps-page-CmnrBmWi.js → local-apps-page-Ca1RuI13.js} +1 -1
- package/dist/gateway/static/root/assets/{locale-store-D6hs74o5.js → locale-store-DUW5YE3R.js} +1 -1
- package/dist/gateway/static/root/assets/logs-page-BLTCg69e.js +2 -0
- package/dist/gateway/static/root/assets/management-settings-B3Eop1zn.js +1 -0
- package/dist/gateway/static/root/assets/{markdown-split-CZaBPOVn.js → markdown-split-BGOYjgnM.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-CiKvSgCv.js → media-plugins-CT0txyM4.js} +1 -1
- package/dist/gateway/static/root/assets/messages-CMjnbdbn.js +3 -0
- package/dist/gateway/static/root/assets/{notes-page-Bisbzqlz.js → notes-page-DfKgAe78.js} +1 -1
- package/dist/gateway/static/root/assets/notes-workbench-Bp1w9UM1.js +4 -0
- package/dist/gateway/static/root/assets/{page-tabs-BxlwhAbH.js → page-tabs-DNrbCgmC.js} +1 -1
- package/dist/gateway/static/root/assets/{preview-open-alternatives-RM8wXxfT.js → preview-open-alternatives-BTqViDOR.js} +1 -1
- package/dist/gateway/static/root/assets/product-delivery-tRr-7IxY.js +1 -0
- package/dist/gateway/static/root/assets/{product-open-page-DdBlibek.js → product-open-page-C_2L4_Iu.js} +1 -1
- package/dist/gateway/static/root/assets/project-detail-page-CZmbPDQN.js +1 -0
- package/dist/gateway/static/root/assets/projects-page-D5pzqfm5.js +1 -0
- package/dist/gateway/static/root/assets/remote-access-hub-CC-XhVGI.js +1 -0
- package/dist/gateway/static/root/assets/{schema-form-BDKbpmXv.js → schema-form-BFLfkDGG.js} +1 -1
- package/dist/gateway/static/root/assets/{schemas-C6onAggk.js → schemas-zIBW3wiV.js} +1 -1
- package/dist/gateway/static/root/assets/sessions-page-B2eTLVDV.js +1 -0
- package/dist/gateway/static/root/assets/settings-advanced-gate-CRuZ_bl0.js +1 -0
- package/dist/gateway/static/root/assets/{settings-form-section-zxo5ZGOu.js → settings-form-section-C4GXGeA4.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-loading-skeleton-DgIp8_S6.js → settings-loading-skeleton-C9a-EbTZ.js} +1 -1
- package/dist/gateway/static/root/assets/settings-page-DI9qDH5x.js +1 -0
- package/dist/gateway/static/root/assets/{settings-page-layout-97LmwrzC.js → settings-page-layout-D-LWQdI5.js} +1 -1
- package/dist/gateway/static/root/assets/setup-status-panel-CJwmPvgs.js +1 -0
- package/dist/gateway/static/root/assets/{share-preview-page-BH7i3g1S.js → share-preview-page-BKSMG9r5.js} +1 -1
- package/dist/gateway/static/root/assets/shares-settings-YzSDS4G_.js +1 -0
- package/dist/gateway/static/root/assets/skill-reload-api-CwOo8zZk.js +1 -0
- package/dist/gateway/static/root/assets/skills-page-xd1cgSGN.js +2 -0
- package/dist/gateway/static/root/assets/{skills-page.utils-s-a5WV9j.js → skills-page.utils-zsz2HIlZ.js} +1 -1
- package/dist/gateway/static/root/assets/{system-settings-panel-BxjeMT5k.js → system-settings-panel-IWNDBI1O.js} +1 -1
- package/dist/gateway/static/root/assets/{task-detail-page-BahycnfW.js → task-detail-page-CgtK02kP.js} +1 -1
- package/dist/gateway/static/root/assets/{theme-store-BRxdsmic.js → theme-store-BkJe7UDo.js} +1 -1
- package/dist/gateway/static/root/assets/time-picker-CstQ___Y.js +1 -0
- package/dist/gateway/static/root/assets/{typed-models-lib-39MjwPSq.js → typed-models-lib-C07LGthO.js} +1 -1
- package/dist/gateway/static/root/assets/{use-save-bar-registration-5aVSjTjO.js → use-save-bar-registration-CQYE0l6z.js} +1 -1
- package/dist/gateway/static/root/assets/user-context-page-BnQAWS_B.js +1 -0
- package/dist/gateway/static/root/assets/{voice-api-key-field-XU-7sBSg.js → voice-api-key-field-Boue1D46.js} +1 -1
- package/dist/gateway/static/root/assets/{work-discovery-overlay-CJqu8UHq.js → work-discovery-overlay-bbmvCmc8.js} +1 -1
- package/dist/gateway/static/root/assets/work-discovery-page-CAszL1y8.js +1 -0
- package/dist/gateway/static/root/assets/{workflow-run-setup-panel-DXtg8Qxl.js → workflow-run-setup-panel-DlHv-GZC.js} +2 -2
- package/dist/gateway/static/root/assets/workflows-page-DxGzo3-x.js +11 -0
- package/dist/gateway/static/root/index.html +9 -9
- package/dist/package.js +1 -1
- package/dist/packages/endpoint-tools-protocol/src/index.js +184 -0
- package/dist/packages/realtime-client/src/index.js +199 -0
- package/dist/packages/realtime-protocol/src/index.js +83 -0
- package/dist/src/agent/agent-manager.d.ts +1 -0
- package/dist/src/agent/agent-manager.js +1 -0
- package/dist/src/agent/background-review/prompts.d.ts +1 -1
- package/dist/src/agent/background-review/prompts.js +3 -1
- package/dist/src/agent/background-review/run-background-review.d.ts +2 -0
- package/dist/src/agent/background-review/run-background-review.js +5 -2
- package/dist/src/agent/client-error-format.d.ts +1 -1
- package/dist/src/agent/client-error-format.js +1 -1
- package/dist/src/agent/external-tools/endpoint-provider.d.ts +17 -0
- package/dist/src/agent/external-tools/endpoint-provider.js +93 -0
- package/dist/src/agent/external-tools/gateway-tools.js +1 -1
- package/dist/src/agent/external-tools/index.d.ts +4 -5
- package/dist/src/agent/external-tools/index.js +8 -2
- package/dist/src/agent/external-tools/types.d.ts +8 -1
- package/dist/src/agent/external-tools/types.js +2 -1
- package/dist/src/agent/inbound/inbound-loop.js +4 -0
- package/dist/src/agent/inbound/turn-dispatcher.d.ts +9 -8
- package/dist/src/agent/inbound/turn-dispatcher.js +13 -11
- package/dist/src/agent/messaging/command-handler.d.ts +2 -2
- package/dist/src/agent/messaging/command-handler.js +3 -3
- package/dist/src/agent/service/async-queue.d.ts +1 -1
- package/dist/src/agent/service/async-queue.js +1 -1
- package/dist/src/agent/service/direct-turn-helpers.d.ts +1 -1
- package/dist/src/agent/service/direct-turn-helpers.js +1 -1
- package/dist/src/agent/service/process-direct-one-shot.d.ts +3 -1
- package/dist/src/agent/service/process-direct-one-shot.js +1 -1
- package/dist/src/agent/service/process-direct-streaming.d.ts +7 -5
- package/dist/src/agent/service/process-direct-streaming.js +5 -5
- package/dist/src/agent/service.d.ts +1 -1
- package/dist/src/agent/service.js +6 -4
- package/dist/src/agent/service.types.d.ts +2 -0
- package/dist/src/agent/session/index.d.ts +1 -1
- package/dist/src/agent/session/session-context.d.ts +2 -0
- package/dist/src/agent/session/session-context.js +4 -0
- package/dist/src/agent/session/session-state-bag.d.ts +4 -4
- package/dist/src/agent/session/session-state-bag.js +1 -1
- package/dist/src/agent/streaming/reasoning-visibility.d.ts +6 -0
- package/dist/src/agent/streaming/{reasoning-visibility-sse.js → reasoning-visibility.js} +3 -6
- package/dist/src/agent/tools/factory.d.ts +4 -0
- package/dist/src/agent/tools/factory.js +1 -0
- package/dist/src/automations/domain/types.d.ts +2 -1
- package/dist/src/automations/domain/validation.d.ts +1 -1
- package/dist/src/automations/service/action-executor.js +4 -1
- package/dist/src/channels/pairing/pairing-challenge.d.ts +1 -1
- package/dist/src/channels/pairing/pairing-challenge.js +1 -1
- package/dist/src/cli/commands/agent/stream-renderer.js +4 -1
- package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
- package/dist/src/cli/commands/extension-marketplace.js +1 -1
- package/dist/src/config/rules.js +0 -5
- package/dist/src/config/schema.d.ts +3 -5
- package/dist/src/config/schema.js +0 -3
- package/dist/src/config/web-patch.js +0 -1
- package/dist/src/endpoint-tools/auth.d.ts +20 -0
- package/dist/src/endpoint-tools/auth.js +51 -0
- package/dist/src/endpoint-tools/index.d.ts +5 -0
- package/dist/src/endpoint-tools/index.js +6 -0
- package/dist/src/endpoint-tools/invocation-service.d.ts +68 -0
- package/dist/src/endpoint-tools/invocation-service.js +190 -0
- package/dist/src/endpoint-tools/policy.d.ts +10 -0
- package/dist/src/endpoint-tools/policy.js +18 -0
- package/dist/src/endpoint-tools/registry.d.ts +45 -0
- package/dist/src/endpoint-tools/registry.js +121 -0
- package/dist/src/endpoint-tools/runtime.d.ts +23 -0
- package/dist/src/endpoint-tools/runtime.js +86 -0
- package/dist/src/endpoint-tools/upload-service.d.ts +41 -0
- package/dist/src/endpoint-tools/upload-service.js +103 -0
- package/dist/src/extensions/types/hooks.d.ts +1 -1
- package/dist/src/extensions/update.js +1 -1
- package/dist/src/gateway/chat-stream/protocol.d.ts +0 -1
- package/dist/src/gateway/clarify-bridge.d.ts +5 -4
- package/dist/src/gateway/clarify-bridge.js +3 -4
- package/dist/src/gateway/heartbeat/service.js +4 -1
- package/dist/src/gateway/hono/app.js +1 -5
- package/dist/src/gateway/hono/index.d.ts +0 -1
- package/dist/src/gateway/hono/index.js +1 -2
- package/dist/src/gateway/hono/lib/config-payload.d.ts +1 -2
- package/dist/src/gateway/hono/lib/config-payload.js +0 -1
- package/dist/src/gateway/hono/middleware/auth.js +3 -5
- package/dist/src/gateway/hono/routes/agent-stream.js +49 -6
- package/dist/src/gateway/hono/routes/auth-registry-extensions.js +1 -1
- package/dist/src/gateway/hono/routes/config-patch/gateway.d.ts +2 -2
- package/dist/src/gateway/hono/routes/config-patch/gateway.js +0 -19
- package/dist/src/gateway/hono/routes/deps.d.ts +0 -2
- package/dist/src/gateway/hono/routes/endpoint-tools.d.ts +3 -0
- package/dist/src/gateway/hono/routes/endpoint-tools.js +196 -0
- package/dist/src/gateway/hono/routes/index.js +4 -0
- package/dist/src/gateway/hono/routes/logs.js +0 -4
- package/dist/src/gateway/hono/routes/public-gateway.js +2 -2
- package/dist/src/gateway/hono/routes/realtime.d.ts +3 -0
- package/dist/src/gateway/hono/routes/realtime.js +35 -0
- package/dist/src/gateway/hono/routes/workflows.js +1 -1
- package/dist/src/gateway/hono/routes/you.d.ts +0 -9
- package/dist/src/gateway/hono/routes/you.js +78 -125
- package/dist/src/gateway/index.d.ts +1 -1
- package/dist/src/gateway/index.js +1 -2
- package/dist/src/gateway/protocol.d.ts +1 -7
- package/dist/src/gateway/security/index.js +1 -1
- package/dist/src/gateway/security/operator-scopes.js +1 -1
- package/dist/src/gateway/server.js +4 -4
- package/dist/src/gateway/service/agent-runner.d.ts +11 -9
- package/dist/src/gateway/service/agent-runner.js +18 -18
- package/dist/src/gateway/service/config-coordinator.d.ts +2 -2
- package/dist/src/gateway/service/run-gateway-agent.d.ts +4 -3
- package/dist/src/gateway/service/run-gateway-agent.js +13 -13
- package/dist/src/gateway/service/session-input-coordinator.d.ts +3 -0
- package/dist/src/gateway/service/session-input-coordinator.js +4 -2
- package/dist/src/gateway/service/types.d.ts +0 -5
- package/dist/src/gateway/service.d.ts +7 -6
- package/dist/src/gateway/service.js +43 -26
- package/dist/src/infra/update-runner.js +1 -1
- package/dist/src/mcp/channel-bridge.d.ts +6 -9
- package/dist/src/mcp/channel-bridge.js +48 -76
- package/dist/src/mcp/channel-server.js +2 -6
- package/dist/src/realtime/broker.d.ts +30 -0
- package/dist/src/realtime/broker.js +97 -0
- package/dist/src/realtime/runtime.d.ts +20 -0
- package/dist/src/realtime/runtime.js +258 -0
- package/dist/src/realtime/tickets.d.ts +15 -0
- package/dist/src/realtime/tickets.js +38 -0
- package/dist/src/realtime/writer.d.ts +15 -0
- package/dist/src/realtime/writer.js +78 -0
- package/dist/src/share/share-config.js +0 -1
- package/dist/src/share/site-share-config.js +0 -1
- package/dist/src/storage/sqlite/endpoint-principal-repository.d.ts +17 -0
- package/dist/src/storage/sqlite/endpoint-principal-repository.js +54 -0
- package/dist/src/storage/sqlite/endpoint-tool-invocation-repository.d.ts +25 -0
- package/dist/src/storage/sqlite/endpoint-tool-invocation-repository.js +42 -0
- package/dist/src/storage/sqlite/index.d.ts +2 -0
- package/dist/src/storage/sqlite/index.js +3 -1
- package/dist/src/storage/sqlite/migrations/109_endpoint_principals.sql +14 -0
- package/dist/src/storage/sqlite/migrations/110_session_input_origin.sql +43 -0
- package/dist/src/storage/sqlite/migrations/111_endpoint_tool_invocations.sql +18 -0
- package/dist/src/storage/sqlite/migrations/112_endpoint_instance_bindings.sql +8 -0
- package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
- package/dist/src/storage/sqlite/migrations/runner.js +3 -3
- package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
- package/dist/src/storage/sqlite/schema.js +1 -1
- package/dist/src/storage/sqlite/session-input-repository.d.ts +3 -0
- package/dist/src/storage/sqlite/session-input-repository.js +5 -3
- package/dist/src/tasks/home-query-service.d.ts +1 -1
- package/dist/src/tui/backends/embedded-backend.js +5 -2
- package/dist/src/tui/backends/{gateway-sse-backend.d.ts → gateway-realtime-backend.d.ts} +13 -10
- package/dist/src/tui/backends/{gateway-sse-backend.js → gateway-realtime-backend.js} +114 -151
- package/dist/src/tui/tui-agent-events.d.ts +0 -1
- package/dist/src/tui/tui-agent-events.js +2 -15
- package/dist/src/tui/tui-backend.d.ts +4 -4
- package/dist/src/tui/tui-run-state.js +0 -4
- package/dist/src/tui/tui-types.d.ts +2 -9
- package/dist/src/tui/tui-types.js +0 -1
- package/dist/src/tui/tui.js +5 -5
- package/dist/src/tunnel/gateway-lifecycle.d.ts +1 -1
- package/dist/src/tunnel/gateway-lifecycle.js +1 -1
- package/dist/src/user-context/actionableInsights.d.ts +14 -0
- package/dist/src/user-context/actionableInsights.js +34 -0
- package/dist/src/user-context/manual-understanding.js +6 -1
- package/dist/src/user-context/projection.d.ts +1 -1
- package/dist/src/user-context/understandingQuality.d.ts +5 -0
- package/dist/src/user-context/understandingQuality.js +44 -0
- package/dist/src/utils/logger/log-stream.d.ts +3 -10
- package/dist/src/utils/logger/log-stream.js +2 -73
- package/dist/src/utils/logger/pino-record.d.ts +1 -1
- package/dist/src/utils/logger/streams.js +1 -1
- package/package.json +6 -3
- package/dist/gateway/static/root/assets/agents-C6OV-gMm.js +0 -2
- package/dist/gateway/static/root/assets/apps-page-Ci14lDeO.js +0 -1
- package/dist/gateway/static/root/assets/automations-page-Q1xmb4Rh.js +0 -6
- package/dist/gateway/static/root/assets/browser-settings-page-DbowrHXD.js +0 -8
- package/dist/gateway/static/root/assets/capabilities-settings-panel-DoZPFdKF.js +0 -7
- package/dist/gateway/static/root/assets/capability-presets-settings-panel-Bt8Hnx0h.js +0 -1
- package/dist/gateway/static/root/assets/channels-settings-Bpsebsru.js +0 -1
- package/dist/gateway/static/root/assets/channels-status-swr-DFmdYRE5.js +0 -1
- package/dist/gateway/static/root/assets/connectors-page-Tg5kl22i.js +0 -2
- package/dist/gateway/static/root/assets/gateway-settings-9g8uIry9.js +0 -1
- package/dist/gateway/static/root/assets/home-page-Bao9jq_z.js +0 -3
- package/dist/gateway/static/root/assets/index-Cl5n1f_U.css +0 -1
- package/dist/gateway/static/root/assets/index-OJgiMh07.js +0 -223
- package/dist/gateway/static/root/assets/local-app-workbench-page-B69NvtvH.js +0 -2
- package/dist/gateway/static/root/assets/logs-page-Cq3cABqg.js +0 -7
- package/dist/gateway/static/root/assets/messages-D02NNLCZ.js +0 -3
- package/dist/gateway/static/root/assets/notes-workbench-BOZfJFhE.js +0 -4
- package/dist/gateway/static/root/assets/product-delivery-C8JbSj5Y.js +0 -1
- package/dist/gateway/static/root/assets/project-detail-page-TSKFb2tN.js +0 -1
- package/dist/gateway/static/root/assets/projects-page-BokeBrTT.js +0 -1
- package/dist/gateway/static/root/assets/remote-access-hub-C56anxCX.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-C7TQADrD.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-BDOPKeAX.js +0 -1
- package/dist/gateway/static/root/assets/settings-page-BUPSIa9P.js +0 -1
- package/dist/gateway/static/root/assets/setup-status-panel-BRg766eW.js +0 -1
- package/dist/gateway/static/root/assets/shares-settings-DhEAviQ_.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-Ct7AV8ls.js +0 -1
- package/dist/gateway/static/root/assets/skills-page-DoVuprZm.js +0 -2
- package/dist/gateway/static/root/assets/time-picker-BGk_Tbsj.js +0 -1
- package/dist/gateway/static/root/assets/user-context-page-B9K41qmy.js +0 -1
- package/dist/gateway/static/root/assets/work-discovery-page-BBVnRHju.js +0 -1
- package/dist/gateway/static/root/assets/workflows-page-Bs_tYfst.js +0 -11
- package/dist/src/agent/streaming/reasoning-visibility-sse.d.ts +0 -15
- package/dist/src/gateway/agent-run-relay.d.ts +0 -19
- package/dist/src/gateway/agent-run-relay.js +0 -169
- package/dist/src/gateway/hono/sse.d.ts +0 -53
- package/dist/src/gateway/hono/sse.js +0 -207
- package/dist/src/gateway/service/sse-hub.d.ts +0 -10
- package/dist/src/gateway/service/sse-hub.js +0 -53
- package/dist/src/tui/sse-consumer.d.ts +0 -15
- package/dist/src/tui/sse-consumer.js +0 -73
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { realtimeEventNameSchema, realtimeTopicSchema } from "../../packages/realtime-protocol/src/index.js";
|
|
2
|
+
import crypto from "node:crypto";
|
|
3
|
+
//#region src/realtime/broker.ts
|
|
4
|
+
const DEFAULT_TOPIC_POLICY = { replayCapacity: 0 };
|
|
5
|
+
function defaultRealtimeTopicPolicy(topic) {
|
|
6
|
+
if (topic.startsWith("run:")) return { replayCapacity: 8e3 };
|
|
7
|
+
if (topic.startsWith("session:")) return { replayCapacity: 200 };
|
|
8
|
+
if (topic === "logs") return { replayCapacity: 500 };
|
|
9
|
+
if (topic === "gateway" || topic === "sessions") return { replayCapacity: 1e3 };
|
|
10
|
+
return DEFAULT_TOPIC_POLICY;
|
|
11
|
+
}
|
|
12
|
+
var RealtimeBroker = class {
|
|
13
|
+
topics = /* @__PURE__ */ new Map();
|
|
14
|
+
constructor(resolvePolicy = defaultRealtimeTopicPolicy, onListenerError = () => {}) {
|
|
15
|
+
this.resolvePolicy = resolvePolicy;
|
|
16
|
+
this.onListenerError = onListenerError;
|
|
17
|
+
}
|
|
18
|
+
publish(topic, event, data, now = Date.now()) {
|
|
19
|
+
realtimeTopicSchema.parse(topic);
|
|
20
|
+
realtimeEventNameSchema.parse(event);
|
|
21
|
+
const state = this.ensureTopic(topic);
|
|
22
|
+
const message = {
|
|
23
|
+
protocolVersion: 1,
|
|
24
|
+
messageId: crypto.randomUUID(),
|
|
25
|
+
kind: "realtime.event",
|
|
26
|
+
sentAt: now,
|
|
27
|
+
payload: {
|
|
28
|
+
topic,
|
|
29
|
+
seq: state.nextSeq++,
|
|
30
|
+
event,
|
|
31
|
+
data
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
const capacity = this.resolvePolicy(topic).replayCapacity;
|
|
35
|
+
if (capacity > 0) {
|
|
36
|
+
state.events.push(message);
|
|
37
|
+
if (state.events.length > capacity) state.events.splice(0, state.events.length - capacity);
|
|
38
|
+
}
|
|
39
|
+
for (const listener of state.listeners) try {
|
|
40
|
+
listener(message);
|
|
41
|
+
} catch (error) {
|
|
42
|
+
this.onListenerError(error, topic);
|
|
43
|
+
}
|
|
44
|
+
return message;
|
|
45
|
+
}
|
|
46
|
+
subscribe(topic, afterSeq, listener) {
|
|
47
|
+
realtimeTopicSchema.parse(topic);
|
|
48
|
+
const state = this.ensureTopic(topic);
|
|
49
|
+
state.listeners.add(listener);
|
|
50
|
+
return {
|
|
51
|
+
initial: afterSeq === void 0 ? [] : this.replayFrom(topic, state, afterSeq),
|
|
52
|
+
unsubscribe: () => {
|
|
53
|
+
state.listeners.delete(listener);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
removeTopic(topic) {
|
|
58
|
+
this.topics.delete(topic);
|
|
59
|
+
}
|
|
60
|
+
currentSequence(topic) {
|
|
61
|
+
return (this.topics.get(topic)?.nextSeq ?? 1) - 1;
|
|
62
|
+
}
|
|
63
|
+
hasTopic(topic) {
|
|
64
|
+
return this.topics.has(topic);
|
|
65
|
+
}
|
|
66
|
+
replayFrom(topic, state, afterSeq) {
|
|
67
|
+
const currentSeq = state.nextSeq - 1;
|
|
68
|
+
if (currentSeq <= afterSeq) return [];
|
|
69
|
+
const earliestSeq = state.events[0]?.payload.seq;
|
|
70
|
+
if (earliestSeq === void 0 || afterSeq + 1 < earliestSeq) return [{
|
|
71
|
+
protocolVersion: 1,
|
|
72
|
+
messageId: crypto.randomUUID(),
|
|
73
|
+
kind: "realtime.gap",
|
|
74
|
+
sentAt: Date.now(),
|
|
75
|
+
payload: {
|
|
76
|
+
topic,
|
|
77
|
+
requestedSeq: afterSeq,
|
|
78
|
+
earliestSeq: earliestSeq ?? currentSeq + 1
|
|
79
|
+
}
|
|
80
|
+
}, ...state.events];
|
|
81
|
+
return state.events.filter((event) => event.payload.seq > afterSeq);
|
|
82
|
+
}
|
|
83
|
+
ensureTopic(topic) {
|
|
84
|
+
let state = this.topics.get(topic);
|
|
85
|
+
if (!state) {
|
|
86
|
+
state = {
|
|
87
|
+
nextSeq: 1,
|
|
88
|
+
events: [],
|
|
89
|
+
listeners: /* @__PURE__ */ new Set()
|
|
90
|
+
};
|
|
91
|
+
this.topics.set(topic, state);
|
|
92
|
+
}
|
|
93
|
+
return state;
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
//#endregion
|
|
97
|
+
export { RealtimeBroker, defaultRealtimeTopicPolicy };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { IncomingMessage } from 'node:http';
|
|
2
|
+
import type { Socket } from 'node:net';
|
|
3
|
+
import type { EndpointToolRuntime } from '../endpoint-tools/runtime.js';
|
|
4
|
+
import { RealtimeBroker } from './broker.js';
|
|
5
|
+
import { RealtimeTicketStore } from './tickets.js';
|
|
6
|
+
export declare class RealtimeRuntime {
|
|
7
|
+
private readonly endpointTools?;
|
|
8
|
+
readonly broker: RealtimeBroker;
|
|
9
|
+
readonly tickets: RealtimeTicketStore;
|
|
10
|
+
private readonly preauthBudget;
|
|
11
|
+
private readonly wss;
|
|
12
|
+
private readonly topicCleanupTimers;
|
|
13
|
+
private closed;
|
|
14
|
+
constructor(endpointTools?: EndpointToolRuntime);
|
|
15
|
+
handleUpgrade(req: IncomingMessage, socket: Socket, head: Buffer): boolean;
|
|
16
|
+
close(): void;
|
|
17
|
+
completeTopic(topic: string, ttlMs?: number): void;
|
|
18
|
+
private handleConnection;
|
|
19
|
+
private rawDataToText;
|
|
20
|
+
}
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import { createLogger } from "../utils/logger/index.js";
|
|
2
|
+
import { init_logger } from "../utils/logger.js";
|
|
3
|
+
import { REALTIME_HEARTBEAT_INTERVAL_MS, REALTIME_HEARTBEAT_TIMEOUT_MS, REALTIME_HELLO_TIMEOUT_MS, REALTIME_MAX_CLIENT_FRAME_BYTES, parseClientRealtimeJsonFrame, parseClientRealtimeMessage } from "../../packages/realtime-protocol/src/index.js";
|
|
4
|
+
import { createPreauthConnectionBudget } from "../gateway/security/preauth-connection-budget.js";
|
|
5
|
+
import { RealtimeBroker } from "./broker.js";
|
|
6
|
+
import { RealtimeTicketStore } from "./tickets.js";
|
|
7
|
+
import { RealtimeSocketWriter } from "./writer.js";
|
|
8
|
+
import { createRequire } from "node:module";
|
|
9
|
+
import crypto from "node:crypto";
|
|
10
|
+
//#region src/realtime/runtime.ts
|
|
11
|
+
init_logger();
|
|
12
|
+
const log = createLogger("Realtime");
|
|
13
|
+
const { WebSocketServer } = createRequire(import.meta.url)("ws");
|
|
14
|
+
const REALTIME_WS_PATH = "/api/realtime/v1/ws";
|
|
15
|
+
const MAX_MESSAGE_CLOCK_SKEW_MS = 6e4;
|
|
16
|
+
const MAX_SUBSCRIPTIONS = 100;
|
|
17
|
+
const MAX_CONNECTIONS = 100;
|
|
18
|
+
function serverMessage(kind, payload) {
|
|
19
|
+
return {
|
|
20
|
+
protocolVersion: 1,
|
|
21
|
+
messageId: crypto.randomUUID(),
|
|
22
|
+
kind,
|
|
23
|
+
sentAt: Date.now(),
|
|
24
|
+
payload
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function isAuthorizedTopic(topic) {
|
|
28
|
+
return topic === "gateway" || topic === "sessions" || topic === "logs" || topic.startsWith("session:") && topic.length > 8 || topic.startsWith("run:") && topic.length > 4 || topic.startsWith("workflow:") && topic.length > 9;
|
|
29
|
+
}
|
|
30
|
+
var RealtimeRuntime = class {
|
|
31
|
+
broker = new RealtimeBroker(void 0, (error, topic) => {
|
|
32
|
+
log.warn({
|
|
33
|
+
err: error,
|
|
34
|
+
topic
|
|
35
|
+
}, "Realtime subscriber failed");
|
|
36
|
+
});
|
|
37
|
+
tickets = new RealtimeTicketStore();
|
|
38
|
+
preauthBudget = createPreauthConnectionBudget();
|
|
39
|
+
wss = new WebSocketServer({
|
|
40
|
+
noServer: true,
|
|
41
|
+
maxPayload: REALTIME_MAX_CLIENT_FRAME_BYTES,
|
|
42
|
+
perMessageDeflate: false
|
|
43
|
+
});
|
|
44
|
+
topicCleanupTimers = /* @__PURE__ */ new Map();
|
|
45
|
+
closed = false;
|
|
46
|
+
constructor(endpointTools) {
|
|
47
|
+
this.endpointTools = endpointTools;
|
|
48
|
+
this.wss.on("connection", (socket, request) => this.handleConnection(socket, request));
|
|
49
|
+
this.wss.on("error", (err) => log.error({ err }, "Realtime WebSocket server failed"));
|
|
50
|
+
}
|
|
51
|
+
handleUpgrade(req, socket, head) {
|
|
52
|
+
if (new URL(req.url ?? "/", "http://gateway.local").pathname !== REALTIME_WS_PATH) return false;
|
|
53
|
+
if (this.closed) {
|
|
54
|
+
socket.destroy();
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
if (this.wss.clients.size >= MAX_CONNECTIONS) {
|
|
58
|
+
socket.destroy();
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
const clientIp = req.socket.remoteAddress;
|
|
62
|
+
if (!this.preauthBudget.acquire(clientIp)) {
|
|
63
|
+
socket.destroy();
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
try {
|
|
67
|
+
this.wss.handleUpgrade(req, socket, head, (ws) => {
|
|
68
|
+
this.wss.emit("connection", ws, req);
|
|
69
|
+
});
|
|
70
|
+
} catch (error) {
|
|
71
|
+
this.preauthBudget.release(clientIp);
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
close() {
|
|
77
|
+
if (this.closed) return;
|
|
78
|
+
this.closed = true;
|
|
79
|
+
for (const client of this.wss.clients) client.close(1001, "Gateway stopping");
|
|
80
|
+
for (const timer of this.topicCleanupTimers.values()) clearTimeout(timer);
|
|
81
|
+
this.topicCleanupTimers.clear();
|
|
82
|
+
this.wss.close();
|
|
83
|
+
}
|
|
84
|
+
completeTopic(topic, ttlMs = 5 * 6e4) {
|
|
85
|
+
const existing = this.topicCleanupTimers.get(topic);
|
|
86
|
+
if (existing) clearTimeout(existing);
|
|
87
|
+
this.topicCleanupTimers.set(topic, setTimeout(() => {
|
|
88
|
+
this.topicCleanupTimers.delete(topic);
|
|
89
|
+
this.broker.removeTopic(topic);
|
|
90
|
+
}, ttlMs));
|
|
91
|
+
}
|
|
92
|
+
handleConnection(socket, request) {
|
|
93
|
+
const clientIp = request.socket.remoteAddress;
|
|
94
|
+
const writer = new RealtimeSocketWriter(socket);
|
|
95
|
+
const subscriptions = /* @__PURE__ */ new Map();
|
|
96
|
+
let authenticated = false;
|
|
97
|
+
let lastSeenAt = Date.now();
|
|
98
|
+
let connectionId;
|
|
99
|
+
let endpointId;
|
|
100
|
+
let budgetHeld = true;
|
|
101
|
+
const releaseBudget = () => {
|
|
102
|
+
if (!budgetHeld) return;
|
|
103
|
+
budgetHeld = false;
|
|
104
|
+
this.preauthBudget.release(clientIp);
|
|
105
|
+
};
|
|
106
|
+
const helloTimer = setTimeout(() => socket.close(4401, "Realtime hello timeout"), REALTIME_HELLO_TIMEOUT_MS);
|
|
107
|
+
const heartbeatTimer = setInterval(() => {
|
|
108
|
+
if (Date.now() - lastSeenAt > 45e3) socket.close(4408, "Realtime heartbeat timeout");
|
|
109
|
+
}, REALTIME_HEARTBEAT_INTERVAL_MS);
|
|
110
|
+
const sendError = (code, message) => {
|
|
111
|
+
writer.enqueue(serverMessage("realtime.error", {
|
|
112
|
+
code,
|
|
113
|
+
message
|
|
114
|
+
}), "critical");
|
|
115
|
+
};
|
|
116
|
+
const subscribe = (requested) => {
|
|
117
|
+
const authorized = requested.filter((item) => {
|
|
118
|
+
if (isAuthorizedTopic(item.topic)) return true;
|
|
119
|
+
sendError("FORBIDDEN_TOPIC", `Topic is not available: ${item.topic}`);
|
|
120
|
+
return false;
|
|
121
|
+
});
|
|
122
|
+
if (new Set([...subscriptions.keys(), ...authorized.map((item) => item.topic)]).size > MAX_SUBSCRIPTIONS) {
|
|
123
|
+
sendError("TOO_MANY_SUBSCRIPTIONS", `At most ${MAX_SUBSCRIPTIONS} topics may be subscribed`);
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
for (const item of authorized) {
|
|
127
|
+
if (item.topic.startsWith("run:") && item.afterSeq !== void 0 && !this.broker.hasTopic(item.topic)) {
|
|
128
|
+
writer.enqueue(serverMessage("realtime.gap", {
|
|
129
|
+
topic: item.topic,
|
|
130
|
+
requestedSeq: item.afterSeq,
|
|
131
|
+
earliestSeq: 1
|
|
132
|
+
}));
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
subscriptions.get(item.topic)?.unsubscribe();
|
|
136
|
+
const handle = this.broker.subscribe(item.topic, item.afterSeq, (event) => {
|
|
137
|
+
writer.enqueue(event);
|
|
138
|
+
});
|
|
139
|
+
subscriptions.set(item.topic, handle);
|
|
140
|
+
for (const message of handle.initial) writer.enqueue(message);
|
|
141
|
+
}
|
|
142
|
+
return true;
|
|
143
|
+
};
|
|
144
|
+
socket.on("message", (data, isBinary) => {
|
|
145
|
+
if (isBinary) {
|
|
146
|
+
socket.close(4400, "Binary realtime frames are not supported");
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
let message;
|
|
150
|
+
try {
|
|
151
|
+
message = parseClientRealtimeMessage(parseClientRealtimeJsonFrame(this.rawDataToText(data)));
|
|
152
|
+
if (Math.abs(Date.now() - message.sentAt) > MAX_MESSAGE_CLOCK_SKEW_MS) throw new Error("Realtime message timestamp is outside the allowed window");
|
|
153
|
+
} catch (error) {
|
|
154
|
+
log.warn({ err: error }, "Realtime client sent an invalid frame");
|
|
155
|
+
socket.close(4400, "Invalid realtime protocol frame");
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
lastSeenAt = Date.now();
|
|
159
|
+
if (!authenticated) {
|
|
160
|
+
if (message.kind !== "realtime.hello") {
|
|
161
|
+
socket.close(4401, "First realtime message must be hello");
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
const claim = this.tickets.consume(message.payload.ticket, message.payload.clientId, message.payload.clientKind);
|
|
165
|
+
if (!claim) {
|
|
166
|
+
socket.close(4401, "Realtime authentication failed");
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
authenticated = true;
|
|
170
|
+
connectionId = crypto.randomUUID();
|
|
171
|
+
let endpointReady;
|
|
172
|
+
if (message.payload.endpoint) {
|
|
173
|
+
if (!this.endpointTools || message.payload.endpoint.kind !== message.payload.clientKind) {
|
|
174
|
+
socket.close(4401, "Realtime endpoint identity is invalid");
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const transport = {
|
|
178
|
+
get readyState() {
|
|
179
|
+
return socket.readyState;
|
|
180
|
+
},
|
|
181
|
+
send: (data) => {
|
|
182
|
+
writer.enqueue(serverMessage("endpoint.message", JSON.parse(data)), "critical");
|
|
183
|
+
},
|
|
184
|
+
close: (code, reason) => socket.close(code, reason)
|
|
185
|
+
};
|
|
186
|
+
try {
|
|
187
|
+
const registration = this.endpointTools.connect(message.payload.endpoint, connectionId, transport);
|
|
188
|
+
endpointId = message.payload.endpoint.endpointId;
|
|
189
|
+
endpointReady = {
|
|
190
|
+
endpointId,
|
|
191
|
+
turnToken: registration.turnToken
|
|
192
|
+
};
|
|
193
|
+
} catch (error) {
|
|
194
|
+
log.warn({
|
|
195
|
+
err: error,
|
|
196
|
+
principalId: message.payload.endpoint.principalId
|
|
197
|
+
}, "Realtime endpoint authentication failed");
|
|
198
|
+
socket.close(4401, "Realtime endpoint authentication failed");
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
clearTimeout(helloTimer);
|
|
203
|
+
releaseBudget();
|
|
204
|
+
writer.enqueue(serverMessage("realtime.ready", {
|
|
205
|
+
connectionId,
|
|
206
|
+
heartbeatIntervalMs: REALTIME_HEARTBEAT_INTERVAL_MS,
|
|
207
|
+
heartbeatTimeoutMs: REALTIME_HEARTBEAT_TIMEOUT_MS,
|
|
208
|
+
...endpointReady ? { endpoint: endpointReady } : {}
|
|
209
|
+
}), "critical");
|
|
210
|
+
subscribe(message.payload.subscriptions);
|
|
211
|
+
log.info({
|
|
212
|
+
connectionId,
|
|
213
|
+
clientId: claim.clientId,
|
|
214
|
+
clientKind: claim.clientKind
|
|
215
|
+
}, "Realtime client connected");
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
if (message.kind === "realtime.hello") socket.close(4400, "Realtime hello may only be sent once");
|
|
219
|
+
else if (message.kind === "realtime.subscribe") subscribe(message.payload.subscriptions);
|
|
220
|
+
else if (message.kind === "realtime.unsubscribe") for (const topic of message.payload.topics) {
|
|
221
|
+
subscriptions.get(topic)?.unsubscribe();
|
|
222
|
+
subscriptions.delete(topic);
|
|
223
|
+
}
|
|
224
|
+
else if (message.kind === "realtime.ping") {
|
|
225
|
+
if (endpointId && connectionId) this.endpointTools?.touch(endpointId, connectionId);
|
|
226
|
+
writer.enqueue(serverMessage("realtime.pong", {}), "critical");
|
|
227
|
+
} else if (!endpointId || !connectionId || !this.endpointTools) sendError("ENDPOINT_NOT_REGISTERED", "Endpoint messages require an endpoint identity");
|
|
228
|
+
else try {
|
|
229
|
+
this.endpointTools.handleMessage(endpointId, connectionId, message.payload);
|
|
230
|
+
} catch (error) {
|
|
231
|
+
log.warn({
|
|
232
|
+
err: error,
|
|
233
|
+
endpointId,
|
|
234
|
+
connectionId
|
|
235
|
+
}, "Realtime endpoint message failed");
|
|
236
|
+
socket.close(4400, "Invalid endpoint message");
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
socket.on("close", () => {
|
|
240
|
+
clearTimeout(helloTimer);
|
|
241
|
+
clearInterval(heartbeatTimer);
|
|
242
|
+
releaseBudget();
|
|
243
|
+
writer.close();
|
|
244
|
+
for (const handle of subscriptions.values()) handle.unsubscribe();
|
|
245
|
+
subscriptions.clear();
|
|
246
|
+
if (endpointId && connectionId) this.endpointTools?.remove(endpointId, connectionId);
|
|
247
|
+
if (connectionId) log.info({ connectionId }, "Realtime client disconnected");
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
rawDataToText(data) {
|
|
251
|
+
if (typeof data === "string") return data;
|
|
252
|
+
if (Array.isArray(data)) return Buffer.concat(data).toString("utf8");
|
|
253
|
+
if (data instanceof ArrayBuffer) return Buffer.from(data).toString("utf8");
|
|
254
|
+
return data.toString("utf8");
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
//#endregion
|
|
258
|
+
export { RealtimeRuntime };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { RealtimeClientKind } from '@xopcai/realtime-protocol';
|
|
2
|
+
export interface RealtimeTicketClaim {
|
|
3
|
+
clientId: string;
|
|
4
|
+
clientKind: RealtimeClientKind;
|
|
5
|
+
expiresAt: number;
|
|
6
|
+
}
|
|
7
|
+
export interface IssuedRealtimeTicket extends RealtimeTicketClaim {
|
|
8
|
+
ticket: string;
|
|
9
|
+
}
|
|
10
|
+
export declare class RealtimeTicketStore {
|
|
11
|
+
private readonly claims;
|
|
12
|
+
issue(clientId: string, clientKind: RealtimeClientKind, now?: number): IssuedRealtimeTicket;
|
|
13
|
+
consume(ticket: string, clientId: string, clientKind: RealtimeClientKind, now?: number): RealtimeTicketClaim | undefined;
|
|
14
|
+
private prune;
|
|
15
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
//#region src/realtime/tickets.ts
|
|
3
|
+
const TICKET_TTL_MS = 3e4;
|
|
4
|
+
const MAX_OUTSTANDING_TICKETS = 1e3;
|
|
5
|
+
function ticketKey(ticket) {
|
|
6
|
+
return crypto.createHash("sha256").update(ticket).digest("hex");
|
|
7
|
+
}
|
|
8
|
+
var RealtimeTicketStore = class {
|
|
9
|
+
claims = /* @__PURE__ */ new Map();
|
|
10
|
+
issue(clientId, clientKind, now = Date.now()) {
|
|
11
|
+
this.prune(now);
|
|
12
|
+
if (this.claims.size >= MAX_OUTSTANDING_TICKETS) throw new Error("Too many outstanding realtime tickets");
|
|
13
|
+
const ticket = crypto.randomBytes(32).toString("base64url");
|
|
14
|
+
const claim = {
|
|
15
|
+
clientId,
|
|
16
|
+
clientKind,
|
|
17
|
+
expiresAt: now + TICKET_TTL_MS
|
|
18
|
+
};
|
|
19
|
+
this.claims.set(ticketKey(ticket), claim);
|
|
20
|
+
return {
|
|
21
|
+
ticket,
|
|
22
|
+
...claim
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
consume(ticket, clientId, clientKind, now = Date.now()) {
|
|
26
|
+
const key = ticketKey(ticket);
|
|
27
|
+
const claim = this.claims.get(key);
|
|
28
|
+
this.claims.delete(key);
|
|
29
|
+
if (!claim || claim.expiresAt < now) return void 0;
|
|
30
|
+
if (claim.clientId !== clientId || claim.clientKind !== clientKind) return void 0;
|
|
31
|
+
return claim;
|
|
32
|
+
}
|
|
33
|
+
prune(now) {
|
|
34
|
+
for (const [key, claim] of this.claims) if (claim.expiresAt < now) this.claims.delete(key);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
//#endregion
|
|
38
|
+
export { RealtimeTicketStore };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ServerRealtimeMessage } from '@xopcai/realtime-protocol';
|
|
2
|
+
import type { WebSocket } from 'ws';
|
|
3
|
+
export declare class RealtimeSocketWriter {
|
|
4
|
+
private readonly socket;
|
|
5
|
+
private readonly critical;
|
|
6
|
+
private readonly normal;
|
|
7
|
+
private queuedBytes;
|
|
8
|
+
private sending;
|
|
9
|
+
private closed;
|
|
10
|
+
private retryTimer?;
|
|
11
|
+
constructor(socket: WebSocket);
|
|
12
|
+
enqueue(message: ServerRealtimeMessage, priority?: 'critical' | 'normal'): boolean;
|
|
13
|
+
close(): void;
|
|
14
|
+
private flush;
|
|
15
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
//#region src/realtime/writer.ts
|
|
2
|
+
const SOFT_BUFFERED_BYTES = 512 * 1024;
|
|
3
|
+
const HARD_BUFFERED_BYTES = 4 * 1024 * 1024;
|
|
4
|
+
const MAX_QUEUED_BYTES = 2 * 1024 * 1024;
|
|
5
|
+
var RealtimeSocketWriter = class {
|
|
6
|
+
critical = [];
|
|
7
|
+
normal = [];
|
|
8
|
+
queuedBytes = 0;
|
|
9
|
+
sending = false;
|
|
10
|
+
closed = false;
|
|
11
|
+
retryTimer;
|
|
12
|
+
constructor(socket) {
|
|
13
|
+
this.socket = socket;
|
|
14
|
+
}
|
|
15
|
+
enqueue(message, priority = "normal") {
|
|
16
|
+
if (this.closed || this.socket.readyState !== 1) return false;
|
|
17
|
+
let data;
|
|
18
|
+
try {
|
|
19
|
+
data = JSON.stringify(message);
|
|
20
|
+
} catch {
|
|
21
|
+
this.socket.close(1011, "Realtime message serialization failed");
|
|
22
|
+
this.close();
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
const item = {
|
|
26
|
+
data,
|
|
27
|
+
bytes: Buffer.byteLength(data)
|
|
28
|
+
};
|
|
29
|
+
if (this.queuedBytes + item.bytes > MAX_QUEUED_BYTES) {
|
|
30
|
+
this.socket.close(4413, "Realtime client is too slow");
|
|
31
|
+
this.close();
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
this.queuedBytes += item.bytes;
|
|
35
|
+
(priority === "critical" ? this.critical : this.normal).push(item);
|
|
36
|
+
this.flush();
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
close() {
|
|
40
|
+
if (this.closed) return;
|
|
41
|
+
this.closed = true;
|
|
42
|
+
if (this.retryTimer) clearTimeout(this.retryTimer);
|
|
43
|
+
this.retryTimer = void 0;
|
|
44
|
+
this.critical.length = 0;
|
|
45
|
+
this.normal.length = 0;
|
|
46
|
+
this.queuedBytes = 0;
|
|
47
|
+
}
|
|
48
|
+
flush() {
|
|
49
|
+
if (this.closed || this.sending || this.socket.readyState !== 1) return;
|
|
50
|
+
if (this.socket.bufferedAmount > HARD_BUFFERED_BYTES) {
|
|
51
|
+
this.socket.close(4413, "Realtime client is too slow");
|
|
52
|
+
this.close();
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (this.socket.bufferedAmount > SOFT_BUFFERED_BYTES) {
|
|
56
|
+
if (!this.retryTimer) this.retryTimer = setTimeout(() => {
|
|
57
|
+
this.retryTimer = void 0;
|
|
58
|
+
this.flush();
|
|
59
|
+
}, 10);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const item = this.critical.shift() ?? this.normal.shift();
|
|
63
|
+
if (!item) return;
|
|
64
|
+
this.sending = true;
|
|
65
|
+
this.socket.send(item.data, (error) => {
|
|
66
|
+
this.sending = false;
|
|
67
|
+
this.queuedBytes -= item.bytes;
|
|
68
|
+
if (error) {
|
|
69
|
+
this.socket.close(1011, "Realtime delivery failed");
|
|
70
|
+
this.close();
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
this.flush();
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
//#endregion
|
|
78
|
+
export { RealtimeSocketWriter };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { EndpointKind } from '@xopcai/endpoint-tools-protocol';
|
|
2
|
+
export interface EndpointPrincipal {
|
|
3
|
+
id: string;
|
|
4
|
+
kind: EndpointKind;
|
|
5
|
+
displayName: string;
|
|
6
|
+
platform: string;
|
|
7
|
+
publicKey: string;
|
|
8
|
+
createdAt: number;
|
|
9
|
+
lastSeenAt?: number;
|
|
10
|
+
revokedAt?: number;
|
|
11
|
+
}
|
|
12
|
+
export declare function createEndpointPrincipal(principal: Omit<EndpointPrincipal, 'createdAt' | 'lastSeenAt' | 'revokedAt'>): EndpointPrincipal;
|
|
13
|
+
export declare function getEndpointPrincipal(id: string): EndpointPrincipal | undefined;
|
|
14
|
+
export declare function listEndpointPrincipals(): EndpointPrincipal[];
|
|
15
|
+
export declare function touchEndpointPrincipal(id: string, seenAt?: number): void;
|
|
16
|
+
export declare function bindEndpointPrincipal(endpointId: string, principalId: string, boundAt?: number): boolean;
|
|
17
|
+
export declare function revokeEndpointPrincipal(id: string, revokedAt?: number): boolean;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { getSqliteDatabase, runSqliteWriteTransaction } from "./transaction.js";
|
|
2
|
+
//#region src/storage/sqlite/endpoint-principal-repository.ts
|
|
3
|
+
function fromRow(row) {
|
|
4
|
+
return {
|
|
5
|
+
id: row.id,
|
|
6
|
+
kind: row.kind,
|
|
7
|
+
displayName: row.display_name,
|
|
8
|
+
platform: row.platform,
|
|
9
|
+
publicKey: row.public_key,
|
|
10
|
+
createdAt: row.created_at,
|
|
11
|
+
...row.last_seen_at === null ? {} : { lastSeenAt: row.last_seen_at },
|
|
12
|
+
...row.revoked_at === null ? {} : { revokedAt: row.revoked_at }
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
function createEndpointPrincipal(principal) {
|
|
16
|
+
const createdAt = Date.now();
|
|
17
|
+
runSqliteWriteTransaction((db) => {
|
|
18
|
+
db.prepare(`
|
|
19
|
+
INSERT INTO endpoint_principals (
|
|
20
|
+
id, kind, display_name, platform, public_key, created_at
|
|
21
|
+
) VALUES (?, ?, ?, ?, ?, ?)
|
|
22
|
+
`).run(principal.id, principal.kind, principal.displayName, principal.platform, principal.publicKey, createdAt);
|
|
23
|
+
});
|
|
24
|
+
return {
|
|
25
|
+
...principal,
|
|
26
|
+
createdAt
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function getEndpointPrincipal(id) {
|
|
30
|
+
const row = getSqliteDatabase().prepare("SELECT * FROM endpoint_principals WHERE id = ?").get(id);
|
|
31
|
+
return row ? fromRow(row) : void 0;
|
|
32
|
+
}
|
|
33
|
+
function listEndpointPrincipals() {
|
|
34
|
+
return getSqliteDatabase().prepare("SELECT * FROM endpoint_principals ORDER BY created_at DESC").all().map(fromRow);
|
|
35
|
+
}
|
|
36
|
+
function touchEndpointPrincipal(id, seenAt = Date.now()) {
|
|
37
|
+
getSqliteDatabase().prepare("UPDATE endpoint_principals SET last_seen_at = ? WHERE id = ? AND revoked_at IS NULL").run(seenAt, id);
|
|
38
|
+
}
|
|
39
|
+
function bindEndpointPrincipal(endpointId, principalId, boundAt = Date.now()) {
|
|
40
|
+
return runSqliteWriteTransaction((db) => {
|
|
41
|
+
db.prepare(`
|
|
42
|
+
INSERT OR IGNORE INTO endpoint_instance_bindings (endpoint_id, principal_id, bound_at)
|
|
43
|
+
VALUES (?, ?, ?)
|
|
44
|
+
`).run(endpointId, principalId, boundAt);
|
|
45
|
+
return db.prepare(`
|
|
46
|
+
SELECT principal_id FROM endpoint_instance_bindings WHERE endpoint_id = ?
|
|
47
|
+
`).get(endpointId)?.principal_id === principalId;
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
function revokeEndpointPrincipal(id, revokedAt = Date.now()) {
|
|
51
|
+
return getSqliteDatabase().prepare("UPDATE endpoint_principals SET revoked_at = ? WHERE id = ? AND revoked_at IS NULL").run(revokedAt, id).changes > 0;
|
|
52
|
+
}
|
|
53
|
+
//#endregion
|
|
54
|
+
export { bindEndpointPrincipal, createEndpointPrincipal, getEndpointPrincipal, listEndpointPrincipals, revokeEndpointPrincipal, touchEndpointPrincipal };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { EndpointEffect, EndpointToolErrorCode } from '@xopcai/endpoint-tools-protocol';
|
|
2
|
+
export interface EndpointToolInvocationAudit {
|
|
3
|
+
id: string;
|
|
4
|
+
principalId: string;
|
|
5
|
+
endpointId: string;
|
|
6
|
+
toolCallId: string;
|
|
7
|
+
toolName: string;
|
|
8
|
+
effect: EndpointEffect;
|
|
9
|
+
confirmationRequired: boolean;
|
|
10
|
+
argumentsSha256: string;
|
|
11
|
+
status: 'running' | 'succeeded' | 'failed';
|
|
12
|
+
errorCode?: EndpointToolErrorCode;
|
|
13
|
+
errorMessage?: string;
|
|
14
|
+
startedAt: number;
|
|
15
|
+
completedAt?: number;
|
|
16
|
+
}
|
|
17
|
+
export declare function startEndpointToolInvocationAudit(audit: Omit<EndpointToolInvocationAudit, 'status' | 'completedAt' | 'errorCode' | 'errorMessage'>): void;
|
|
18
|
+
export declare function finishEndpointToolInvocationAudit(params: {
|
|
19
|
+
id: string;
|
|
20
|
+
status: 'succeeded' | 'failed';
|
|
21
|
+
errorCode?: EndpointToolErrorCode;
|
|
22
|
+
errorMessage?: string;
|
|
23
|
+
completedAt?: number;
|
|
24
|
+
}): void;
|
|
25
|
+
export declare function listEndpointToolInvocationAudits(limit?: number): EndpointToolInvocationAudit[];
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { getSqliteDatabase } from "./transaction.js";
|
|
2
|
+
//#region src/storage/sqlite/endpoint-tool-invocation-repository.ts
|
|
3
|
+
function fromRow(row) {
|
|
4
|
+
return {
|
|
5
|
+
id: row.id,
|
|
6
|
+
principalId: row.principal_id,
|
|
7
|
+
endpointId: row.endpoint_id,
|
|
8
|
+
toolCallId: row.tool_call_id,
|
|
9
|
+
toolName: row.tool_name,
|
|
10
|
+
effect: row.effect,
|
|
11
|
+
confirmationRequired: row.confirmation_required === 1,
|
|
12
|
+
argumentsSha256: row.arguments_sha256,
|
|
13
|
+
status: row.status,
|
|
14
|
+
...row.error_code === null ? {} : { errorCode: row.error_code },
|
|
15
|
+
...row.error_message === null ? {} : { errorMessage: row.error_message },
|
|
16
|
+
startedAt: row.started_at,
|
|
17
|
+
...row.completed_at === null ? {} : { completedAt: row.completed_at }
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function startEndpointToolInvocationAudit(audit) {
|
|
21
|
+
getSqliteDatabase().prepare(`
|
|
22
|
+
INSERT INTO endpoint_tool_invocations (
|
|
23
|
+
id, principal_id, endpoint_id, tool_call_id, tool_name, effect,
|
|
24
|
+
confirmation_required, arguments_sha256, status, started_at
|
|
25
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'running', ?)
|
|
26
|
+
`).run(audit.id, audit.principalId, audit.endpointId, audit.toolCallId, audit.toolName, audit.effect, audit.confirmationRequired ? 1 : 0, audit.argumentsSha256, audit.startedAt);
|
|
27
|
+
}
|
|
28
|
+
function finishEndpointToolInvocationAudit(params) {
|
|
29
|
+
getSqliteDatabase().prepare(`
|
|
30
|
+
UPDATE endpoint_tool_invocations
|
|
31
|
+
SET status = ?, error_code = ?, error_message = ?, completed_at = ?
|
|
32
|
+
WHERE id = ? AND status = 'running'
|
|
33
|
+
`).run(params.status, params.errorCode ?? null, params.errorMessage?.slice(0, 2e3) ?? null, params.completedAt ?? Date.now(), params.id);
|
|
34
|
+
}
|
|
35
|
+
function listEndpointToolInvocationAudits(limit = 100) {
|
|
36
|
+
const boundedLimit = Math.max(1, Math.min(500, Math.trunc(limit)));
|
|
37
|
+
return getSqliteDatabase().prepare(`
|
|
38
|
+
SELECT * FROM endpoint_tool_invocations ORDER BY started_at DESC LIMIT ?
|
|
39
|
+
`).all(boundedLimit).map(fromRow);
|
|
40
|
+
}
|
|
41
|
+
//#endregion
|
|
42
|
+
export { finishEndpointToolInvocationAudit, listEndpointToolInvocationAudits, startEndpointToolInvocationAudit };
|