@xopcai/xopc 0.0.270 → 0.0.272
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/browser-ext/dist/assets/{auth-cwEj8rFb.js → auth-C1QI-uMG.js} +65 -14
- package/dist/browser-ext/dist/background.js +23 -2
- package/dist/browser-ext/dist/pairing-handoff.js +51 -0
- package/dist/browser-ext/dist/sidepanel.html +1 -1
- package/dist/browser-ext/dist/sidepanel.js +39 -27
- package/dist/browser-ext/manifest.json +12 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/{activity-store-D_0FmdRj.js → activity-store-DffqjeqQ.js} +1 -1
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-IZvEXak_.js → agent-avatar-dicebear-Cwj4HCYY.js} +1 -1
- package/dist/gateway/static/root/assets/agent-defaults-settings-panel-6CLfLxVc.js +1 -0
- package/dist/gateway/static/root/assets/agents-B6mu64fY.js +1 -0
- package/dist/gateway/static/root/assets/{api-O3TTQ4qN.js → api-CGLdye4i.js} +1 -1
- package/dist/gateway/static/root/assets/{app-management-settings-panel-Cqqsbzri.js → app-management-settings-panel-BLujSbaw.js} +1 -1
- package/dist/gateway/static/root/assets/{appearance-settings-DaJyf32s.js → appearance-settings-BXMcueb8.js} +1 -1
- package/dist/gateway/static/root/assets/apps-page-BEgy4hY3.js +1 -0
- package/dist/gateway/static/root/assets/{archive-plugin-kDPnDTJI.js → archive-plugin-D4bUampf.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-CJ3VceKS.js → attachment-load-CBGNA2Ys.js} +1 -1
- package/dist/gateway/static/root/assets/{automations-page-JwMtU8XB.js → automations-page-dQyolNmy.js} +1 -1
- package/dist/gateway/static/root/assets/automations-workspace-DxRFrmrV.js +6 -0
- package/dist/gateway/static/root/assets/{binary-plugins-DzyZXLBX.js → binary-plugins-bbvp81h2.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-kTt3gYIJ.js → block-editor-C8e6Wri4.js} +3 -3
- package/dist/gateway/static/root/assets/{browser-automation-inputs-BKohPYR4.js → browser-automation-inputs-oJWJtQa6.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-automations-page-ktQfFPk-.js → browser-automations-page-CAcJQSjN.js} +2 -2
- package/dist/gateway/static/root/assets/browser-settings-page-TLAlqcPo.js +8 -0
- package/dist/gateway/static/root/assets/capabilities-settings-panel-z-K_jrZz.js +8 -0
- package/dist/gateway/static/root/assets/channels-settings-Dzrk33aB.js +1 -0
- package/dist/gateway/static/root/assets/{channels-status-swr-DrqzYmDr.js → channels-status-swr-CfOPNnVr.js} +1 -1
- package/dist/gateway/static/root/assets/{chat-terminal-dock-CIFGGLGa.js → chat-terminal-dock-9ZOl92kh.js} +2 -2
- package/dist/gateway/static/root/assets/clock-3-DnrAnuyZ.js +1 -0
- package/dist/gateway/static/root/assets/connectors-page-B1dIoRNi.js +2 -0
- package/dist/gateway/static/root/assets/cron-expression-C26e13YU.js +2 -0
- package/dist/gateway/static/root/assets/date-picker-Dj6FRLF9.js +1 -0
- package/dist/gateway/static/root/assets/{dependency-picker-D3vNIjRI.js → dependency-picker-D1q89tVc.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-fQwRb_3p.js → desktop-pet-DfqZBvhp.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-settings-DfOJ-GvT.js → desktop-pet-settings-HCEh2MOm.js} +1 -1
- package/dist/gateway/static/root/assets/device-pairing-wizard-CPth92s6.js +1 -0
- package/dist/gateway/static/root/assets/{extension-debug-page-Cn53jB5-.js → extension-debug-page-C8cAWVoK.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-IDpLCxpC.js → extension-page-5N87Vaym.js} +1 -1
- package/dist/gateway/static/root/assets/extension-settings-page-D85SAUFP.js +1 -0
- package/dist/gateway/static/root/assets/{gateway-config-swr-B7dcmP7Q.js → gateway-config-swr-BjsDnB_n.js} +1 -1
- package/dist/gateway/static/root/assets/gateway-settings-Dqx8PmcT.js +1 -0
- package/dist/gateway/static/root/assets/{gateway-startup-retry-JGhGMhVZ.js → gateway-startup-retry-BIf0eLWX.js} +1 -1
- package/dist/gateway/static/root/assets/git-branch-Dh9uxTot.js +1 -0
- package/dist/gateway/static/root/assets/heartbeat-settings-BpDWGE1a.js +1 -0
- package/dist/gateway/static/root/assets/home-page-DJaqpC8-.js +1 -0
- package/dist/gateway/static/root/assets/{image-generation-api-BJLX7Qrh.js → image-generation-api-1m8xTVS_.js} +1 -1
- package/dist/gateway/static/root/assets/index-C6PmD0cK.js +74 -0
- package/dist/gateway/static/root/assets/index-DKy1Qn1_.css +2 -0
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-CBvMTFFx.js → keyboard-shortcuts-settings-Byy8mCSq.js} +1 -1
- package/dist/gateway/static/root/assets/local-app-workbench-page-l2AEiG2C.js +2 -0
- package/dist/gateway/static/root/assets/{local-apps-page-APCMNDvU.js → local-apps-page-BPo0wBgM.js} +1 -1
- package/dist/gateway/static/root/assets/locale-store-CRNble2Z.js +1 -0
- package/dist/gateway/static/root/assets/logs-page-X5FdLlXK.js +2 -0
- package/dist/gateway/static/root/assets/management-settings-DT6NoIjm.js +1 -0
- package/dist/gateway/static/root/assets/{markdown-view-BwjpJVPs.js → markdown-view-AglreSOj.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-C10byeaa.js → media-plugins-Bzka9qXu.js} +1 -1
- package/dist/gateway/static/root/assets/message-circle-Badhj-4D.js +1 -0
- package/dist/gateway/static/root/assets/message-square-fVE7Ghj1.js +1 -0
- package/dist/gateway/static/root/assets/{messages-D0V_bgRP.js → messages-DH_HCgmw.js} +3 -3
- package/dist/gateway/static/root/assets/note-detail-panel-BUJPoaGZ.js +5 -0
- package/dist/gateway/static/root/assets/notes-page-BwFpWRuo.js +8 -0
- package/dist/gateway/static/root/assets/{page-tabs-qQ36zncF.js → page-tabs-Ys0p65Lt.js} +1 -1
- package/dist/gateway/static/root/assets/popover-select-Dz_Qis2R.js +1 -0
- package/dist/gateway/static/root/assets/{preview-open-alternatives-BiAJvvCo.js → preview-open-alternatives-iknB5xEp.js} +1 -1
- package/dist/gateway/static/root/assets/{proactive-card-CcNBqHoH.js → proactive-card-BPZDgNNR.js} +1 -1
- package/dist/gateway/static/root/assets/{proactive-page-C3K61jve.js → proactive-page-CxdZR-p2.js} +1 -1
- package/dist/gateway/static/root/assets/{product-open-page-CunnDP4w.js → product-open-page-W1o3f-kz.js} +1 -1
- package/dist/gateway/static/root/assets/project-detail-page-BU1RKAcy.js +1 -0
- package/dist/gateway/static/root/assets/projects-page-z0X0tL8G.js +1 -0
- package/dist/gateway/static/root/assets/{registry-api-0JSegbNl.js → registry-api-DmYFpjsH.js} +1 -1
- package/dist/gateway/static/root/assets/remote-access-hub-NNTut2I8.js +1 -0
- package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-bu_mwBTr.js → runtime-tools-settings-panel-DP1XyYO8.js} +2 -2
- package/dist/gateway/static/root/assets/{schema-form-BE0sgZyz.js → schema-form-BMYKdzsH.js} +1 -1
- package/dist/gateway/static/root/assets/sessions-page-B6SsHlz5.js +1 -0
- package/dist/gateway/static/root/assets/settings-advanced-gate-BQu8bAcN.js +1 -0
- package/dist/gateway/static/root/assets/{settings-form-section-CBo517lV.js → settings-form-section-2LGjz7bu.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-DBPimx93.js → settings-page-7DjpC9_C.js} +1 -1
- package/dist/gateway/static/root/assets/setup-status-panel-Dj9jFWJt.js +1 -0
- package/dist/gateway/static/root/assets/{share-preview-page-DdV75VTe.js → share-preview-page-BxYdSi8c.js} +1 -1
- package/dist/gateway/static/root/assets/shares-settings-CMqE0KuK.js +1 -0
- package/dist/gateway/static/root/assets/skill-reload-api-BXqSJymN.js +1 -0
- package/dist/gateway/static/root/assets/{skills-page-Cn_-nvRo.js → skills-page-DNEScMZH.js} +1 -1
- package/dist/gateway/static/root/assets/skills-page.utils-q88l65YU.js +1 -0
- package/dist/gateway/static/root/assets/system-settings-panel-8NIssGKj.js +1 -0
- package/dist/gateway/static/root/assets/task-detail-page-L6dp9v2y.js +1 -0
- package/dist/gateway/static/root/assets/{theme-store-1wryLgBQ.js → theme-store-BXZG3S25.js} +1 -1
- package/dist/gateway/static/root/assets/tunnel-api-CWsRPM84.js +1 -0
- package/dist/gateway/static/root/assets/{use-autosave-N_lm4FdD.js → use-autosave-BwQuoTQU.js} +1 -1
- package/dist/gateway/static/root/assets/user-model-page-D4BucMrZ.js +2 -0
- package/dist/gateway/static/root/assets/{voice-api-key-field-DMsyVfri.js → voice-api-key-field-Bd03Ic0s.js} +1 -1
- package/dist/gateway/static/root/assets/{voice-config-api-QWVmiAp3.js → voice-config-api-gggUp3u9.js} +1 -1
- package/dist/gateway/static/root/assets/{work-discovery-overlay-BC0vAxp1.js → work-discovery-overlay-DF8PR_pK.js} +1 -1
- package/dist/gateway/static/root/assets/{workflow-run-setup-panel-Cz9jQIlP.js → workflow-run-setup-panel-BiAAWlir.js} +2 -2
- package/dist/gateway/static/root/assets/workflows-page-DryKUMfA.js +6 -0
- package/dist/gateway/static/root/index.html +15 -11
- package/dist/package.js +1 -1
- package/dist/packages/gateway-contract/src/device-pairing.js +2 -1
- package/dist/packages/gateway-contract/src/session-identity.js +37 -0
- package/dist/src/agent/memory/compaction-chunks.d.ts +18 -0
- package/dist/src/agent/memory/compaction-chunks.js +53 -0
- package/dist/src/agent/memory/compaction-request.d.ts +12 -0
- package/dist/src/agent/memory/compaction-request.js +72 -0
- package/dist/src/agent/memory/compaction.d.ts +3 -0
- package/dist/src/agent/memory/compaction.js +166 -118
- package/dist/src/agent/service.js +1 -0
- package/dist/src/browser/native-messaging-host.js +3 -1
- package/dist/src/cli/bin.js +2 -2
- package/dist/src/cli/commands/agent.js +2 -2
- package/dist/src/cli/commands/auth.js +1 -1
- package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
- package/dist/src/cli/commands/extension-dev.js +1 -1
- package/dist/src/cli/commands/extension-marketplace.js +1 -1
- package/dist/src/cli/commands/extension.js +1 -1
- package/dist/src/cli/commands/gateway/lifecycle.js +1 -1
- package/dist/src/cli/commands/gateway-ssh-tunnel.js +1 -1
- package/dist/src/cli/commands/models.js +2 -2
- package/dist/src/cli/commands/onboard.js +1 -1
- package/dist/src/cli/commands/platform.js +1 -1
- package/dist/src/cli/commands/profile.js +1 -1
- package/dist/src/cli/commands/tunnel.js +6 -1
- package/dist/src/cli/commands/update.js +1 -1
- package/dist/src/cli/extension-cli-register.js +1 -1
- package/dist/src/config/index.js +1 -1
- package/dist/src/gateway/heartbeat/service.js +1 -1
- package/dist/src/gateway/hono/app.js +1 -0
- package/dist/src/gateway/hono/middleware/auth.d.ts +1 -2
- package/dist/src/gateway/hono/middleware/auth.js +49 -31
- package/dist/src/gateway/hono/middleware/strict-rate-limit.js +11 -15
- package/dist/src/gateway/hono/routes/browser-session.d.ts +3 -0
- package/dist/src/gateway/hono/routes/browser-session.js +43 -0
- package/dist/src/gateway/hono/routes/devices.js +61 -96
- package/dist/src/gateway/hono/routes/exposure.js +3 -7
- package/dist/src/gateway/hono/routes/lazy-bundles.js +8 -0
- package/dist/src/gateway/hono/routes/models.js +60 -1
- package/dist/src/gateway/hono/routes/notes.js +3 -7
- package/dist/src/gateway/hono/routes/sessions.js +11 -0
- package/dist/src/gateway/hono/routes/shares.js +8 -20
- package/dist/src/gateway/hono/routes/site-shares.js +4 -8
- package/dist/src/gateway/hono/routes/tunnel.js +13 -13
- package/dist/src/gateway/image-generation-setup.d.ts +17 -0
- package/dist/src/gateway/image-generation-setup.js +30 -21
- package/dist/src/gateway/rate-limit/buckets.d.ts +3 -0
- package/dist/src/gateway/rate-limit/buckets.js +10 -0
- package/dist/src/gateway/run-loop.js +1 -1
- package/dist/src/gateway/runtime-config.js +2 -0
- package/dist/src/gateway/security/browser-session.d.ts +5 -0
- package/dist/src/gateway/security/browser-session.js +45 -0
- package/dist/src/gateway/security/gateway-principal.d.ts +1 -0
- package/dist/src/gateway/server.js +1 -1
- package/dist/src/gateway/service.d.ts +1 -0
- package/dist/src/gateway/service.js +11 -1
- package/dist/src/heartbeat/index.js +1 -1
- package/dist/src/infra/rate-limit/keyed-store.d.ts +3 -0
- package/dist/src/infra/rate-limit/keyed-store.js +9 -2
- package/dist/src/proactive/policy/service.d.ts +1 -1
- package/dist/src/realtime/runtime.d.ts +2 -1
- package/dist/src/realtime/runtime.js +9 -3
- package/dist/src/realtime/tickets.d.ts +1 -0
- package/dist/src/realtime/tickets.js +3 -0
- package/dist/src/session/store.d.ts +2 -0
- package/dist/src/session/store.js +17 -2
- package/dist/src/session/types.d.ts +2 -1
- package/dist/src/storage/sqlite/browser-session-repository.d.ts +17 -0
- package/dist/src/storage/sqlite/browser-session-repository.js +54 -0
- package/dist/src/storage/sqlite/device-pairing-approval.js +2 -1
- package/dist/src/storage/sqlite/device-pairing-repository.d.ts +4 -10
- package/dist/src/storage/sqlite/device-pairing-repository.js +10 -62
- package/dist/src/storage/sqlite/index.d.ts +1 -1
- package/dist/src/storage/sqlite/index.js +2 -2
- package/dist/src/storage/sqlite/migrations/164_browser_sessions.sql +8 -0
- package/dist/src/storage/sqlite/migrations/165_device_pairing_target.sql +40 -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-identity-sql.d.ts +2 -0
- package/dist/src/storage/sqlite/session-identity-sql.js +18 -0
- package/dist/src/storage/sqlite/session-repository.js +26 -22
- package/dist/src/tunnel/auth-policy.d.ts +3 -0
- package/dist/src/tunnel/auth-policy.js +8 -0
- package/dist/src/tunnel/broker-client.d.ts +5 -15
- package/dist/src/tunnel/broker-client.js +37 -49
- package/dist/src/tunnel/frpc-binary.d.ts +8 -6
- package/dist/src/tunnel/frpc-binary.js +76 -88
- package/dist/src/tunnel/frpc-config.d.ts +2 -1
- package/dist/src/tunnel/frpc-config.js +29 -12
- package/dist/src/tunnel/frpc-release.d.ts +27 -0
- package/dist/src/tunnel/frpc-release.js +30 -0
- package/dist/src/tunnel/gateway-lifecycle.js +8 -2
- package/dist/src/tunnel/index.js +2 -1
- package/dist/src/tunnel/registration-proof.d.ts +19 -0
- package/dist/src/tunnel/registration-proof.js +37 -0
- package/dist/src/tunnel/tunnel-persist.js +3 -1
- package/dist/src/tunnel/tunnel-service.d.ts +1 -0
- package/dist/src/tunnel/tunnel-service.js +35 -8
- package/dist/src/tunnel/tunnel-state.js +11 -5
- package/dist/src/tunnel/tunnel-types.d.ts +2 -0
- package/dist/src/tunnel/well-known.js +5 -1
- package/dist/src/user-context/config.js +2 -2
- package/dist/src/utils/logger/redact.js +4 -0
- package/dist/src/voice/realtime/runtime.d.ts +1 -0
- package/dist/src/voice/realtime/runtime.js +17 -1
- package/dist/src/voice/realtime/runtime.types.d.ts +1 -0
- package/dist/src/workflows/service/workflow-run-service.js +2 -0
- package/dist/src/workflows/service/workflow-session-bridge.d.ts +1 -0
- package/dist/src/workflows/service/workflow-session-bridge.js +1 -0
- package/package.json +4 -4
- package/dist/gateway/static/root/assets/agent-defaults-settings-panel-CE3ygr3l.js +0 -1
- package/dist/gateway/static/root/assets/agents-DzG2qw-c.js +0 -1
- package/dist/gateway/static/root/assets/apps-page-B4lX6yoT.js +0 -1
- package/dist/gateway/static/root/assets/automations-workspace-BiNBewg0.js +0 -6
- package/dist/gateway/static/root/assets/browser-settings-page-Dgdt5uVL.js +0 -8
- package/dist/gateway/static/root/assets/capabilities-settings-panel-BpDW-R9f.js +0 -7
- package/dist/gateway/static/root/assets/channels-settings-DU4EQnR0.js +0 -1
- package/dist/gateway/static/root/assets/connectors-page-CfJqy284.js +0 -2
- package/dist/gateway/static/root/assets/cron-expression-BNwbnrEb.js +0 -2
- package/dist/gateway/static/root/assets/date-picker-DKyirU4K.js +0 -1
- package/dist/gateway/static/root/assets/extension-settings-page-B1dds2bP.js +0 -1
- package/dist/gateway/static/root/assets/gateway-settings-pp9jN0wl.js +0 -1
- package/dist/gateway/static/root/assets/heartbeat-settings-B8_zTyev.js +0 -1
- package/dist/gateway/static/root/assets/home-page-g1KdAdLb.js +0 -1
- package/dist/gateway/static/root/assets/index-1zRt56Zt.css +0 -2
- package/dist/gateway/static/root/assets/index-OMnem2nr.js +0 -74
- package/dist/gateway/static/root/assets/local-app-workbench-page-bj75v2ZE.js +0 -2
- package/dist/gateway/static/root/assets/locale-store-BThkG0W5.js +0 -1
- package/dist/gateway/static/root/assets/logs-page-BX7n39zV.js +0 -2
- package/dist/gateway/static/root/assets/management-settings-CM62j8ZX.js +0 -1
- package/dist/gateway/static/root/assets/mobile-pairing-wizard-BCWfwVx3.js +0 -1
- package/dist/gateway/static/root/assets/note-detail-panel-koOqb0gX.js +0 -5
- package/dist/gateway/static/root/assets/notes-page-BH54rY3B.js +0 -8
- package/dist/gateway/static/root/assets/popover-select-CKBYnT3c.js +0 -1
- package/dist/gateway/static/root/assets/project-detail-page-SL2g8MZY.js +0 -1
- package/dist/gateway/static/root/assets/projects-page-BtxazT5J.js +0 -1
- package/dist/gateway/static/root/assets/remote-access-hub-BSb7b67A.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-ljcS2KVz.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-CR9Y9LQm.js +0 -1
- package/dist/gateway/static/root/assets/setup-status-panel-J3AnooPW.js +0 -1
- package/dist/gateway/static/root/assets/shares-settings-AAm4Iwcp.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-pRCjP5sm.js +0 -1
- package/dist/gateway/static/root/assets/skills-page.utils-CfVAc_lu.js +0 -1
- package/dist/gateway/static/root/assets/system-settings-panel-B-pzalp-.js +0 -1
- package/dist/gateway/static/root/assets/task-detail-page-CmrLy3PO.js +0 -1
- package/dist/gateway/static/root/assets/tunnel-api-Cgpnm2fL.js +0 -1
- package/dist/gateway/static/root/assets/user-model-page-u3QLDX6L.js +0 -2
- package/dist/gateway/static/root/assets/workflows-page-DA6OOK6X.js +0 -6
|
@@ -59,12 +59,10 @@ function getAgentImageGenerationConfig(config, agentIdRaw) {
|
|
|
59
59
|
model: resolveEffectiveAgentConfigForAgent(config, agentId).config.models.imageGeneration ?? null
|
|
60
60
|
};
|
|
61
61
|
}
|
|
62
|
-
function
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
error: `Agent not found: ${agentId}`
|
|
67
|
-
};
|
|
62
|
+
function getDefaultImageGenerationConfig(config) {
|
|
63
|
+
return { model: config.agents.defaults.models.imageGeneration ?? null };
|
|
64
|
+
}
|
|
65
|
+
function prepareImageGenerationConfig(config, input, applyModel) {
|
|
68
66
|
const providerId = input.providerId.trim();
|
|
69
67
|
const provider = getImageGenerationProvider(providerId);
|
|
70
68
|
if (!provider) return {
|
|
@@ -82,11 +80,6 @@ function prepareImageGenerationSetup(config, agentIdRaw, input) {
|
|
|
82
80
|
error: parsedProviderConfig.error
|
|
83
81
|
};
|
|
84
82
|
const next = structuredClone(config);
|
|
85
|
-
const index = next.agents.list.findIndex((entry) => entry.enabled !== false && normalizeAgentId(entry.id) === agentId);
|
|
86
|
-
if (index < 0) return {
|
|
87
|
-
ok: false,
|
|
88
|
-
error: `Agent not found: ${agentId}`
|
|
89
|
-
};
|
|
90
83
|
next.providers = {
|
|
91
84
|
...next.providers ?? {},
|
|
92
85
|
[providerId]: {
|
|
@@ -94,15 +87,11 @@ function prepareImageGenerationSetup(config, agentIdRaw, input) {
|
|
|
94
87
|
...parsedProviderConfig.value
|
|
95
88
|
}
|
|
96
89
|
};
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
fallbacks: [],
|
|
103
|
-
autoProviderFallback: false
|
|
104
|
-
}
|
|
105
|
-
};
|
|
90
|
+
applyModel(next, {
|
|
91
|
+
primary: `${providerId}/${modelId}`,
|
|
92
|
+
fallbacks: [],
|
|
93
|
+
autoProviderFallback: false
|
|
94
|
+
});
|
|
106
95
|
const parsed = ConfigSchema.safeParse(next);
|
|
107
96
|
if (!parsed.success) return {
|
|
108
97
|
ok: false,
|
|
@@ -115,6 +104,26 @@ function prepareImageGenerationSetup(config, agentIdRaw, input) {
|
|
|
115
104
|
modelId
|
|
116
105
|
};
|
|
117
106
|
}
|
|
107
|
+
function prepareDefaultImageGenerationSetup(config, input) {
|
|
108
|
+
return prepareImageGenerationConfig(config, input, (next, model) => {
|
|
109
|
+
next.agents.defaults.models.imageGeneration = model;
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
function prepareImageGenerationSetup(config, agentIdRaw, input) {
|
|
113
|
+
const agentId = normalizeAgentId(agentIdRaw);
|
|
114
|
+
if (!config.agents.list.some((entry) => entry.enabled !== false && normalizeAgentId(entry.id) === agentId)) return {
|
|
115
|
+
ok: false,
|
|
116
|
+
error: `Agent not found: ${agentId}`
|
|
117
|
+
};
|
|
118
|
+
return prepareImageGenerationConfig(config, input, (next, model) => {
|
|
119
|
+
const index = next.agents.list.findIndex((entry) => entry.enabled !== false && normalizeAgentId(entry.id) === agentId);
|
|
120
|
+
const entry = next.agents.list[index];
|
|
121
|
+
entry.models = {
|
|
122
|
+
...entry.models ?? {},
|
|
123
|
+
imageGeneration: model
|
|
124
|
+
};
|
|
125
|
+
});
|
|
126
|
+
}
|
|
118
127
|
async function verifyImageGenerationCredential(input) {
|
|
119
128
|
const apiKey = input.apiKey.trim();
|
|
120
129
|
if (!apiKey) return {
|
|
@@ -165,4 +174,4 @@ async function verifyImageGenerationCredential(input) {
|
|
|
165
174
|
}
|
|
166
175
|
}
|
|
167
176
|
//#endregion
|
|
168
|
-
export { getAgentImageGenerationConfig, getImageGenerationCatalog, prepareImageGenerationSetup, verifyImageGenerationCredential };
|
|
177
|
+
export { getAgentImageGenerationConfig, getDefaultImageGenerationConfig, getImageGenerationCatalog, prepareDefaultImageGenerationSetup, prepareImageGenerationSetup, verifyImageGenerationCredential };
|
|
@@ -34,6 +34,7 @@ export declare function authPolicyConfig(cfg: ResolvedAuthRateLimitConfig): Auth
|
|
|
34
34
|
declare class BucketRegistry {
|
|
35
35
|
private authFailureLimiter?;
|
|
36
36
|
private authFailureSignature?;
|
|
37
|
+
private identityChallengeLimiter?;
|
|
37
38
|
private strictApiLimiter?;
|
|
38
39
|
private chatApiLimiter?;
|
|
39
40
|
private mediaApiLimiter?;
|
|
@@ -50,6 +51,8 @@ declare class BucketRegistry {
|
|
|
50
51
|
* config read.
|
|
51
52
|
*/
|
|
52
53
|
authFailure(cfg: ResolvedAuthRateLimitConfig): FailureLimiter;
|
|
54
|
+
/** Global pre-authentication signing budget; independent of authenticated traffic. */
|
|
55
|
+
identityChallenge(): RateLimiter;
|
|
53
56
|
/** Authenticated admin / mutation endpoints — 150 req / 60 s per client IP. */
|
|
54
57
|
strictApi(): RateLimiter;
|
|
55
58
|
/** Interactive chat actions — 180 req / 60 s per client IP. */
|
|
@@ -46,6 +46,7 @@ function authPolicyConfig(cfg) {
|
|
|
46
46
|
var BucketRegistry = class {
|
|
47
47
|
authFailureLimiter;
|
|
48
48
|
authFailureSignature;
|
|
49
|
+
identityChallengeLimiter;
|
|
49
50
|
strictApiLimiter;
|
|
50
51
|
chatApiLimiter;
|
|
51
52
|
mediaApiLimiter;
|
|
@@ -75,6 +76,13 @@ var BucketRegistry = class {
|
|
|
75
76
|
}
|
|
76
77
|
return this.authFailureLimiter;
|
|
77
78
|
}
|
|
79
|
+
/** Global pre-authentication signing budget; independent of authenticated traffic. */
|
|
80
|
+
identityChallenge() {
|
|
81
|
+
return this.identityChallengeLimiter ??= new RateLimiter({
|
|
82
|
+
maxRequests: 300,
|
|
83
|
+
windowMs: 6e4
|
|
84
|
+
});
|
|
85
|
+
}
|
|
78
86
|
/** Authenticated admin / mutation endpoints — 150 req / 60 s per client IP. */
|
|
79
87
|
strictApi() {
|
|
80
88
|
if (!this.strictApiLimiter) this.strictApiLimiter = new RateLimiter({
|
|
@@ -158,6 +166,7 @@ var BucketRegistry = class {
|
|
|
158
166
|
}
|
|
159
167
|
destroyAll() {
|
|
160
168
|
this.authFailureLimiter?.destroy();
|
|
169
|
+
this.identityChallengeLimiter?.destroy();
|
|
161
170
|
this.strictApiLimiter?.destroy();
|
|
162
171
|
this.chatApiLimiter?.destroy();
|
|
163
172
|
this.mediaApiLimiter?.destroy();
|
|
@@ -169,6 +178,7 @@ var BucketRegistry = class {
|
|
|
169
178
|
this.sharePublicShortLimiter?.destroy();
|
|
170
179
|
this.sharePublicLongLimiter?.destroy();
|
|
171
180
|
this.authFailureLimiter = void 0;
|
|
181
|
+
this.identityChallengeLimiter = void 0;
|
|
172
182
|
this.strictApiLimiter = void 0;
|
|
173
183
|
this.chatApiLimiter = void 0;
|
|
174
184
|
this.mediaApiLimiter = void 0;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { loadConfig } from "../config/loader.js";
|
|
2
|
-
import { isRestartEnabled } from "../config/commands.flags.js";
|
|
3
2
|
import "../config/index.js";
|
|
3
|
+
import { isRestartEnabled } from "../config/commands.flags.js";
|
|
4
4
|
import { runBootstrapMigrationsSync } from "../migrations/runner.js";
|
|
5
5
|
import { restartGatewayProcessWithFreshPid } from "./respawn.js";
|
|
6
6
|
import { acquireGatewayLock } from "./lock.js";
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { isAllInterfacesHost, isLoopbackHost, resolveGatewayCorsOrigins } from "./host.js";
|
|
2
2
|
import { resolveGatewayListenPlan } from "./listen.js";
|
|
3
3
|
import { assertTailscaleExposureCompatible } from "./tailscale-lifecycle.js";
|
|
4
|
+
import { requireTunnelGatewayToken } from "../tunnel/auth-policy.js";
|
|
4
5
|
import { isAuthRateLimitGloballyDisabled, isGatewayStrictSecurityEnabled } from "./rate-limit/env-flags.js";
|
|
5
6
|
import "./rate-limit/index.js";
|
|
6
7
|
//#region src/gateway/runtime-config.ts
|
|
@@ -15,6 +16,7 @@ function hasSharedSecret(auth) {
|
|
|
15
16
|
* Throws when bind/auth/origin combinations are unsafe for network exposure.
|
|
16
17
|
*/
|
|
17
18
|
function assertGatewayRuntimeConfig(params) {
|
|
19
|
+
if (params.cfg.tunnel?.enabled) requireTunnelGatewayToken(params.auth);
|
|
18
20
|
const { bindMode, bindHost, customBindHost } = resolveGatewayListenPlan({
|
|
19
21
|
cfg: params.cfg,
|
|
20
22
|
bindOverride: params.bindOverride
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Context } from 'hono';
|
|
2
|
+
export declare function readBrowserSessionCookie(c: Context): string | undefined;
|
|
3
|
+
export declare function browserCookieRequestAllowed(c: Context): boolean;
|
|
4
|
+
export declare function writeBrowserSessionCookie(c: Context, token: string, expiresAt: number): void;
|
|
5
|
+
export declare function clearBrowserSessionCookie(c: Context): void;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { isLoopbackHost } from "../host.js";
|
|
2
|
+
import { deleteCookie, getCookie, setCookie } from "hono/cookie";
|
|
3
|
+
//#region src/gateway/security/browser-session.ts
|
|
4
|
+
function origin(c) {
|
|
5
|
+
return new URL(`http://${c.req.header("host") ?? new URL(c.req.url).host}`);
|
|
6
|
+
}
|
|
7
|
+
function secure(c) {
|
|
8
|
+
return !isLoopbackHost(origin(c).hostname.replace(/^\[|\]$/g, ""));
|
|
9
|
+
}
|
|
10
|
+
function name(c) {
|
|
11
|
+
return secure(c) ? "__Host-xopc-session" : "xopc-local-session";
|
|
12
|
+
}
|
|
13
|
+
function readBrowserSessionCookie(c) {
|
|
14
|
+
return getCookie(c, name(c));
|
|
15
|
+
}
|
|
16
|
+
function browserCookieRequestAllowed(c) {
|
|
17
|
+
const requestOrigin = c.req.header("origin");
|
|
18
|
+
if (requestOrigin) try {
|
|
19
|
+
const parsed = new URL(requestOrigin);
|
|
20
|
+
const localOrigin = isLoopbackHost(parsed.hostname.replace(/^\[|\]$/g, ""));
|
|
21
|
+
return (parsed.host === origin(c).host || !secure(c) && localOrigin) && (parsed.protocol === "https:" || !secure(c) && parsed.protocol === "http:");
|
|
22
|
+
} catch {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
return ["GET", "HEAD"].includes(c.req.method) && c.req.header("sec-fetch-site") !== "cross-site";
|
|
26
|
+
}
|
|
27
|
+
function writeBrowserSessionCookie(c, token, expiresAt) {
|
|
28
|
+
setCookie(c, name(c), token, {
|
|
29
|
+
httpOnly: true,
|
|
30
|
+
secure: secure(c),
|
|
31
|
+
sameSite: "Strict",
|
|
32
|
+
path: "/",
|
|
33
|
+
expires: new Date(expiresAt)
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
function clearBrowserSessionCookie(c) {
|
|
37
|
+
deleteCookie(c, name(c), {
|
|
38
|
+
httpOnly: true,
|
|
39
|
+
secure: secure(c),
|
|
40
|
+
sameSite: "Strict",
|
|
41
|
+
path: "/"
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
//#endregion
|
|
45
|
+
export { browserCookieRequestAllowed, clearBrowserSessionCookie, readBrowserSessionCookie, writeBrowserSessionCookie };
|
|
@@ -5,6 +5,7 @@ export type GatewayPrincipal = {
|
|
|
5
5
|
principalId: string;
|
|
6
6
|
deviceId?: string;
|
|
7
7
|
accessSessionId?: string;
|
|
8
|
+
browserSessionId?: string;
|
|
8
9
|
scopes: readonly GatewayScope[];
|
|
9
10
|
};
|
|
10
11
|
export declare function setGatewayPrincipal(c: Context, principal: GatewayPrincipal): void;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { resolveGatewayBindHost, resolveGatewayListenHosts } from "../config/gateway-bind.js";
|
|
2
|
-
import { resolveGatewayListenPlan } from "./listen.js";
|
|
3
2
|
import { ensureXopcCliOnPath } from "../infra/path-env.js";
|
|
3
|
+
import { resolveGatewayListenPlan } from "./listen.js";
|
|
4
4
|
import { GatewayService } from "./service.js";
|
|
5
5
|
import { handleSiteShareUpgrade } from "../share/site-share-router.js";
|
|
6
6
|
import { createHonoApp } from "./hono/app.js";
|
|
@@ -48,6 +48,7 @@ export declare class GatewayService {
|
|
|
48
48
|
private readonly modelCatalogSync;
|
|
49
49
|
private auth;
|
|
50
50
|
readonly endpointTools: EndpointToolRuntime;
|
|
51
|
+
private isRealtimePrincipalActive;
|
|
51
52
|
readonly realtime: RealtimeRuntime;
|
|
52
53
|
readonly voiceRealtime: VoiceRealtimeRuntime;
|
|
53
54
|
getConfig(): Config;
|
|
@@ -122,6 +122,7 @@ import { GatewaySessionsApi } from "./service/sessions-api.js";
|
|
|
122
122
|
import { GatewayMarketplaceService } from "./service/marketplace-service.js";
|
|
123
123
|
import { GatewayConfigCoordinator } from "./service/config-coordinator.js";
|
|
124
124
|
import { GatewayAgentRunner } from "./service/agent-runner.js";
|
|
125
|
+
import { isBrowserSessionActive } from "../storage/sqlite/browser-session-repository.js";
|
|
125
126
|
import { RealtimeRuntime } from "../realtime/runtime.js";
|
|
126
127
|
import { VoiceRealtimeRuntime } from "../voice/realtime/runtime.js";
|
|
127
128
|
import { DurableVoiceAgentBroker } from "../voice/realtime/agentBroker.js";
|
|
@@ -165,8 +166,17 @@ var GatewayService = class {
|
|
|
165
166
|
});
|
|
166
167
|
auth;
|
|
167
168
|
endpointTools = new EndpointToolRuntime();
|
|
168
|
-
|
|
169
|
+
isRealtimePrincipalActive = (principalId) => {
|
|
170
|
+
if (!principalId.startsWith("browser:")) return true;
|
|
171
|
+
try {
|
|
172
|
+
return isBrowserSessionActive(principalId.slice(8), this.auth);
|
|
173
|
+
} catch {
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
realtime = new RealtimeRuntime(this.endpointTools, this.isRealtimePrincipalActive);
|
|
169
178
|
voiceRealtime = new VoiceRealtimeRuntime({
|
|
179
|
+
isPrincipalActive: this.isRealtimePrincipalActive,
|
|
170
180
|
getConversationContext: async (sessionKey, expectedSessionId) => {
|
|
171
181
|
if ((await this.sessionIndex.getSessionMetadata(sessionKey))?.sessionId !== expectedSessionId) throw new Error("Conversation changed before voice connection");
|
|
172
182
|
const messages = await this.sessionIndex.loadMessages(sessionKey);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DEFAULT_ACK_MAX_CHARS, HEARTBEAT_OK, NO_REPLY, shouldSilence, stripHeartbeatToken } from "./tokens.js";
|
|
2
|
+
import { appendCronEventLines } from "./event-prompt.js";
|
|
2
3
|
import { isWithinActiveHours } from "./active-hours.js";
|
|
3
4
|
import { isHeartbeatContentEmpty } from "./content-check.js";
|
|
4
|
-
import { appendCronEventLines } from "./event-prompt.js";
|
|
5
5
|
import { createHeartbeatWake } from "./wake.js";
|
|
6
6
|
export { DEFAULT_ACK_MAX_CHARS, HEARTBEAT_OK, NO_REPLY, appendCronEventLines, createHeartbeatWake, isHeartbeatContentEmpty, isWithinActiveHours, shouldSilence, stripHeartbeatToken };
|
|
@@ -11,6 +11,7 @@ export type Clock = () => number;
|
|
|
11
11
|
export type KeyedStoreOptions = {
|
|
12
12
|
/** Drop entries whose `lastTouchedMs` is older than this. */
|
|
13
13
|
staleAfterMs: number;
|
|
14
|
+
maxEntries?: number;
|
|
14
15
|
/** How often the sweep runs. Defaults to `staleAfterMs / 4` (capped at 10min). */
|
|
15
16
|
cleanupIntervalMs?: number;
|
|
16
17
|
/** Injected clock for deterministic tests. */
|
|
@@ -20,6 +21,8 @@ export declare class KeyedStore<V extends {
|
|
|
20
21
|
lastTouchedMs: number;
|
|
21
22
|
}> {
|
|
22
23
|
private readonly map;
|
|
24
|
+
private readonly maxEntries;
|
|
25
|
+
private overflow?;
|
|
23
26
|
private readonly clock;
|
|
24
27
|
private readonly staleAfterMs;
|
|
25
28
|
private cleanupTimer?;
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
//#region src/infra/rate-limit/keyed-store.ts
|
|
2
2
|
var KeyedStore = class {
|
|
3
3
|
map = /* @__PURE__ */ new Map();
|
|
4
|
+
maxEntries;
|
|
5
|
+
overflow;
|
|
4
6
|
clock;
|
|
5
7
|
staleAfterMs;
|
|
6
8
|
cleanupTimer;
|
|
7
9
|
constructor(opts) {
|
|
10
|
+
this.maxEntries = Math.max(1, opts.maxEntries ?? 1e4);
|
|
8
11
|
this.clock = opts.clock ?? Date.now;
|
|
9
12
|
this.staleAfterMs = Math.max(1e3, opts.staleAfterMs);
|
|
10
13
|
const interval = Math.min(600 * 1e3, Math.max(1e3, opts.cleanupIntervalMs ?? Math.floor(this.staleAfterMs / 4)));
|
|
@@ -12,10 +15,11 @@ var KeyedStore = class {
|
|
|
12
15
|
this.cleanupTimer.unref?.();
|
|
13
16
|
}
|
|
14
17
|
get(key) {
|
|
15
|
-
return this.map.get(key);
|
|
18
|
+
return this.map.get(key) ?? (this.map.size >= this.maxEntries ? this.overflow : void 0);
|
|
16
19
|
}
|
|
17
20
|
set(key, value) {
|
|
18
|
-
this.map.set(key, value);
|
|
21
|
+
if (this.map.has(key) || this.map.size < this.maxEntries) this.map.set(key, value);
|
|
22
|
+
else this.overflow ??= value;
|
|
19
23
|
}
|
|
20
24
|
delete(key) {
|
|
21
25
|
this.map.delete(key);
|
|
@@ -25,6 +29,7 @@ var KeyedStore = class {
|
|
|
25
29
|
}
|
|
26
30
|
clear() {
|
|
27
31
|
this.map.clear();
|
|
32
|
+
this.overflow = void 0;
|
|
28
33
|
}
|
|
29
34
|
destroy() {
|
|
30
35
|
if (this.cleanupTimer) {
|
|
@@ -32,9 +37,11 @@ var KeyedStore = class {
|
|
|
32
37
|
this.cleanupTimer = void 0;
|
|
33
38
|
}
|
|
34
39
|
this.map.clear();
|
|
40
|
+
this.overflow = void 0;
|
|
35
41
|
}
|
|
36
42
|
sweep() {
|
|
37
43
|
const now = this.clock();
|
|
44
|
+
if (this.overflow && now - this.overflow.lastTouchedMs > this.staleAfterMs) this.overflow = void 0;
|
|
38
45
|
for (const [k, v] of this.map.entries()) if (now - v.lastTouchedMs > this.staleAfterMs) this.map.delete(k);
|
|
39
46
|
}
|
|
40
47
|
};
|
|
@@ -31,7 +31,7 @@ export declare function effectiveProactivePolicy(subscriptionId: string, now?: D
|
|
|
31
31
|
digestEnabled: boolean;
|
|
32
32
|
digestHour: number;
|
|
33
33
|
digestMinute: number;
|
|
34
|
-
preferredChannel: "mobile" | "
|
|
34
|
+
preferredChannel: "mobile" | "telegram" | "browser" | "all" | "auto";
|
|
35
35
|
suppressWhileViewing: boolean;
|
|
36
36
|
telegram: {
|
|
37
37
|
chatId: string;
|
|
@@ -5,6 +5,7 @@ import { RealtimeBroker } from './broker.js';
|
|
|
5
5
|
import { RealtimeTicketStore } from './tickets.js';
|
|
6
6
|
export declare class RealtimeRuntime {
|
|
7
7
|
private readonly endpointTools?;
|
|
8
|
+
private readonly isPrincipalActive;
|
|
8
9
|
readonly broker: RealtimeBroker;
|
|
9
10
|
readonly tickets: RealtimeTicketStore;
|
|
10
11
|
private readonly preauthBudget;
|
|
@@ -12,7 +13,7 @@ export declare class RealtimeRuntime {
|
|
|
12
13
|
private readonly topicCleanupTimers;
|
|
13
14
|
private readonly socketsByPrincipal;
|
|
14
15
|
private closed;
|
|
15
|
-
constructor(endpointTools?: EndpointToolRuntime);
|
|
16
|
+
constructor(endpointTools?: EndpointToolRuntime, isPrincipalActive?: (id: string) => boolean);
|
|
16
17
|
handleUpgrade(req: IncomingMessage, socket: Socket, head: Buffer): boolean;
|
|
17
18
|
close(): void;
|
|
18
19
|
disconnectPrincipal(principalId: string): void;
|
|
@@ -51,8 +51,9 @@ var RealtimeRuntime = class {
|
|
|
51
51
|
topicCleanupTimers = /* @__PURE__ */ new Map();
|
|
52
52
|
socketsByPrincipal = /* @__PURE__ */ new Map();
|
|
53
53
|
closed = false;
|
|
54
|
-
constructor(endpointTools) {
|
|
54
|
+
constructor(endpointTools, isPrincipalActive = () => true) {
|
|
55
55
|
this.endpointTools = endpointTools;
|
|
56
|
+
this.isPrincipalActive = isPrincipalActive;
|
|
56
57
|
this.wss.on("connection", (socket, request) => this.handleConnection(socket, request));
|
|
57
58
|
this.wss.on("error", (err) => log.error({ err }, "Realtime WebSocket server failed"));
|
|
58
59
|
}
|
|
@@ -91,7 +92,8 @@ var RealtimeRuntime = class {
|
|
|
91
92
|
this.socketsByPrincipal.clear();
|
|
92
93
|
}
|
|
93
94
|
disconnectPrincipal(principalId) {
|
|
94
|
-
|
|
95
|
+
this.tickets.revokePrincipal(principalId);
|
|
96
|
+
for (const socket of this.socketsByPrincipal.get(principalId) ?? []) socket.terminate();
|
|
95
97
|
}
|
|
96
98
|
completeTopic(topic, ttlMs = 5 * 6e4) {
|
|
97
99
|
const existing = this.topicCleanupTimers.get(topic);
|
|
@@ -119,6 +121,10 @@ var RealtimeRuntime = class {
|
|
|
119
121
|
};
|
|
120
122
|
const helloTimer = setTimeout(() => socket.close(4401, "Realtime hello timeout"), REALTIME_HELLO_TIMEOUT_MS);
|
|
121
123
|
const heartbeatTimer = setInterval(() => {
|
|
124
|
+
if (principalId && !this.isPrincipalActive(principalId)) {
|
|
125
|
+
socket.terminate();
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
122
128
|
if (Date.now() - lastSeenAt > 45e3) socket.close(4408, "Realtime heartbeat timeout");
|
|
123
129
|
}, REALTIME_HEARTBEAT_INTERVAL_MS);
|
|
124
130
|
const sendError = (code, message) => {
|
|
@@ -181,7 +187,7 @@ var RealtimeRuntime = class {
|
|
|
181
187
|
return;
|
|
182
188
|
}
|
|
183
189
|
const claim = this.tickets.consume(message.payload.ticket, message.payload.clientId, message.payload.clientKind);
|
|
184
|
-
if (!claim) {
|
|
190
|
+
if (!claim || !this.isPrincipalActive(claim.principalId)) {
|
|
185
191
|
socket.close(4401, "Realtime authentication failed");
|
|
186
192
|
return;
|
|
187
193
|
}
|
|
@@ -16,5 +16,6 @@ export declare class RealtimeTicketStore {
|
|
|
16
16
|
private readonly claims;
|
|
17
17
|
issue(clientId: string, clientKind: RealtimeClientKind, principal: Omit<RealtimeTicketClaim, 'clientId' | 'clientKind' | 'expiresAt'>, now?: number): IssuedRealtimeTicket;
|
|
18
18
|
consume(ticket: string, clientId: string, clientKind: RealtimeClientKind, now?: number): RealtimeTicketClaim | undefined;
|
|
19
|
+
revokePrincipal(principalId: string): void;
|
|
19
20
|
private prune;
|
|
20
21
|
}
|
|
@@ -31,6 +31,9 @@ var RealtimeTicketStore = class {
|
|
|
31
31
|
if (claim.clientId !== clientId || claim.clientKind !== clientKind) return void 0;
|
|
32
32
|
return claim;
|
|
33
33
|
}
|
|
34
|
+
revokePrincipal(principalId) {
|
|
35
|
+
for (const [key, claim] of this.claims) if (claim.principalId === principalId) this.claims.delete(key);
|
|
36
|
+
}
|
|
34
37
|
prune(now) {
|
|
35
38
|
for (const [key, claim] of this.claims) if (claim.expiresAt < now) this.claims.delete(key);
|
|
36
39
|
}
|
|
@@ -44,6 +44,8 @@ export declare class SessionStore {
|
|
|
44
44
|
private compactionHooks;
|
|
45
45
|
private readonly compactionTails;
|
|
46
46
|
constructor(options: SessionStoreOptions, windowConfig?: Partial<WindowConfig>, compactionConfig?: Partial<CompactionConfig>);
|
|
47
|
+
/** Refresh policy for subsequent compactions without changing an in-flight request. */
|
|
48
|
+
updateConfig(config: Config): void;
|
|
47
49
|
setCompactionHooks(hooks: SessionCompactionHooks): void;
|
|
48
50
|
private runCompactionHook;
|
|
49
51
|
private resolveWorkspaceCwd;
|
|
@@ -46,7 +46,18 @@ var SessionStore = class {
|
|
|
46
46
|
constructor(options, windowConfig, compactionConfig) {
|
|
47
47
|
this.options = options;
|
|
48
48
|
this.window = new SlidingWindow(windowConfig);
|
|
49
|
-
this.compactor = new SessionCompactor(
|
|
49
|
+
this.compactor = new SessionCompactor({
|
|
50
|
+
...resolveCompactionPolicy(options.config),
|
|
51
|
+
...compactionConfig
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
/** Refresh policy for subsequent compactions without changing an in-flight request. */
|
|
55
|
+
updateConfig(config) {
|
|
56
|
+
this.options = {
|
|
57
|
+
...this.options,
|
|
58
|
+
config
|
|
59
|
+
};
|
|
60
|
+
this.compactor = new SessionCompactor(resolveCompactionPolicy(config));
|
|
50
61
|
}
|
|
51
62
|
setCompactionHooks(hooks) {
|
|
52
63
|
this.compactionHooks = hooks;
|
|
@@ -509,7 +520,11 @@ var SessionStore = class {
|
|
|
509
520
|
messageCount: messages.length,
|
|
510
521
|
tokenCount
|
|
511
522
|
});
|
|
512
|
-
const result = await this.compactor.compact(snapshot.entries, model, instructions, force,
|
|
523
|
+
const result = await this.compactor.compact(snapshot.entries, model, instructions, force, {
|
|
524
|
+
...executionOptions,
|
|
525
|
+
sessionKey: key
|
|
526
|
+
});
|
|
527
|
+
executionOptions?.signal?.throwIfAborted();
|
|
513
528
|
if (result.compacted) {
|
|
514
529
|
const compacted = await this.applyCompaction(key, result, snapshot);
|
|
515
530
|
await this.runCompactionHook("after", {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { CompactionAudit } from './compaction-types.js';
|
|
2
|
+
import type { SessionDiscoveryQuery } from '@xopcai/gateway-contract';
|
|
2
3
|
import type { TranscriptStoredRow } from './session-context-for-llm.js';
|
|
3
4
|
export interface Message {
|
|
4
5
|
role: 'system' | 'user' | 'assistant' | 'tool' | 'toolResult';
|
|
@@ -139,7 +140,7 @@ export interface SessionDetail extends SessionMetadata {
|
|
|
139
140
|
transcriptRows?: TranscriptStoredRow[];
|
|
140
141
|
}
|
|
141
142
|
/** Session list query parameters */
|
|
142
|
-
export interface SessionListQuery {
|
|
143
|
+
export interface SessionListQuery extends SessionDiscoveryQuery {
|
|
143
144
|
status?: SessionStatus | SessionStatus[];
|
|
144
145
|
/** Single `sourceChannel`, or comma-separated list (e.g. `telegram,weixin`). */
|
|
145
146
|
channel?: string;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ResolvedGatewayAuth } from '../../gateway/auth.js';
|
|
2
|
+
export declare const BROWSER_SESSION_TTL_MS: number;
|
|
3
|
+
export declare class BrowserSessionLimitError extends Error {
|
|
4
|
+
constructor();
|
|
5
|
+
}
|
|
6
|
+
export declare function createBrowserSession(auth: ResolvedGatewayAuth, replacedSessionId?: string): {
|
|
7
|
+
sessionId: `${string}-${string}-${string}-${string}-${string}`;
|
|
8
|
+
token: string;
|
|
9
|
+
expiresAt: number;
|
|
10
|
+
};
|
|
11
|
+
export declare function authenticateBrowserSession(token: string, auth: ResolvedGatewayAuth): {
|
|
12
|
+
sessionId: string;
|
|
13
|
+
expiresAt: number;
|
|
14
|
+
renewed: boolean;
|
|
15
|
+
} | null;
|
|
16
|
+
export declare function isBrowserSessionActive(sessionId: string, auth: ResolvedGatewayAuth): boolean;
|
|
17
|
+
export declare function deleteBrowserSession(sessionId: string): void;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { getSqliteDatabase, runSqliteWriteTransaction } from "./transaction.js";
|
|
2
|
+
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
3
|
+
//#region src/storage/sqlite/browser-session-repository.ts
|
|
4
|
+
const BROWSER_SESSION_TTL_MS = 720 * 36e5;
|
|
5
|
+
const digest = (value) => createHash("sha256").update(value).digest("hex");
|
|
6
|
+
function fingerprint(auth) {
|
|
7
|
+
const credential = auth.mode === "token" ? auth.token : auth.mode === "password" ? auth.password : void 0;
|
|
8
|
+
return credential ? digest(JSON.stringify([auth.mode, credential])) : null;
|
|
9
|
+
}
|
|
10
|
+
var BrowserSessionLimitError = class extends Error {
|
|
11
|
+
constructor() {
|
|
12
|
+
super("Browser session limit reached");
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
function createBrowserSession(auth, replacedSessionId) {
|
|
16
|
+
const authFingerprint = fingerprint(auth);
|
|
17
|
+
if (!authFingerprint) throw new Error("Browser sessions require Gateway credentials");
|
|
18
|
+
const sessionId = randomUUID();
|
|
19
|
+
const token = randomBytes(32).toString("base64url");
|
|
20
|
+
const now = Date.now();
|
|
21
|
+
const expiresAt = now + BROWSER_SESSION_TTL_MS;
|
|
22
|
+
runSqliteWriteTransaction((db) => {
|
|
23
|
+
db.prepare("DELETE FROM browser_sessions WHERE expires_at <= ? OR auth_fingerprint <> ?").run(now, authFingerprint);
|
|
24
|
+
if (replacedSessionId) db.prepare("DELETE FROM browser_sessions WHERE session_id = ?").run(replacedSessionId);
|
|
25
|
+
if (db.prepare("SELECT count(*) AS count FROM browser_sessions").get().count >= 32) throw new BrowserSessionLimitError();
|
|
26
|
+
db.prepare("INSERT INTO browser_sessions (session_id, token_hash, auth_fingerprint, created_at, expires_at) VALUES (?, ?, ?, ?, ?)").run(sessionId, digest(token), authFingerprint, now, expiresAt);
|
|
27
|
+
});
|
|
28
|
+
return {
|
|
29
|
+
sessionId,
|
|
30
|
+
token,
|
|
31
|
+
expiresAt
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function authenticateBrowserSession(token, auth) {
|
|
35
|
+
if (!/^[A-Za-z0-9_-]{43}$/.test(token)) return null;
|
|
36
|
+
const row = getSqliteDatabase().prepare("SELECT session_id, expires_at FROM browser_sessions WHERE token_hash = ? AND auth_fingerprint = ? AND expires_at > ?").get(digest(token), fingerprint(auth), Date.now());
|
|
37
|
+
if (!row) return null;
|
|
38
|
+
const renewed = row.expires_at < Date.now() + 168 * 36e5;
|
|
39
|
+
const expiresAt = renewed ? Date.now() + BROWSER_SESSION_TTL_MS : row.expires_at;
|
|
40
|
+
if (renewed) getSqliteDatabase().prepare("UPDATE browser_sessions SET expires_at = ? WHERE session_id = ?").run(expiresAt, row.session_id);
|
|
41
|
+
return {
|
|
42
|
+
sessionId: row.session_id,
|
|
43
|
+
expiresAt,
|
|
44
|
+
renewed
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function isBrowserSessionActive(sessionId, auth) {
|
|
48
|
+
return Boolean(getSqliteDatabase().prepare("SELECT 1 FROM browser_sessions WHERE session_id = ? AND auth_fingerprint = ? AND expires_at > ?").get(sessionId, fingerprint(auth), Date.now()));
|
|
49
|
+
}
|
|
50
|
+
function deleteBrowserSession(sessionId) {
|
|
51
|
+
getSqliteDatabase().prepare("DELETE FROM browser_sessions WHERE session_id = ?").run(sessionId);
|
|
52
|
+
}
|
|
53
|
+
//#endregion
|
|
54
|
+
export { BROWSER_SESSION_TTL_MS, BrowserSessionLimitError, authenticateBrowserSession, createBrowserSession, deleteBrowserSession, isBrowserSessionActive };
|
|
@@ -66,7 +66,7 @@ function verifiedSetup(token, now) {
|
|
|
66
66
|
const id = /^xopc_pair_([0-9a-f-]{36})_/i.exec(token)?.[1];
|
|
67
67
|
if (!id) return fail("PAIRING_DENIED", 401);
|
|
68
68
|
const row = getSqliteDatabase().prepare("SELECT * FROM device_pairing_sessions WHERE pairing_id = ?").get(id);
|
|
69
|
-
if (!row || row.
|
|
69
|
+
if (!row || row.attempts_remaining <= 0) return fail("PAIRING_DENIED", 401);
|
|
70
70
|
if (!crypto.timingSafeEqual(Buffer.from(row.secret_hash, "hex"), Buffer.from(hash(token), "hex"))) {
|
|
71
71
|
getSqliteDatabase().prepare("UPDATE device_pairing_sessions SET attempts_remaining = attempts_remaining - 1 WHERE pairing_id = ?").run(id);
|
|
72
72
|
return fail("PAIRING_DENIED", 401);
|
|
@@ -93,6 +93,7 @@ function verifyProof(action, body, deviceJson, now) {
|
|
|
93
93
|
function submitDevicePairingRequest(body, now = Date.now()) {
|
|
94
94
|
const setup = verifiedSetup(String(body.pairingToken), now);
|
|
95
95
|
const device = devicePairingDeviceSchema.parse(body.device);
|
|
96
|
+
if (!(setup.target_kind === "browser" ? device.platform === "chrome" : setup.target_kind === "mobile" && device.platform !== "chrome")) fail("PAIRING_DEVICE_MISMATCH");
|
|
96
97
|
const deviceJson = JSON.stringify(device);
|
|
97
98
|
const autoApprove = isTrustedLocalBrowserSetup(setup, deviceJson);
|
|
98
99
|
verifyProof("request", body, deviceJson, now);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { DevicePairingTargetKind } from '@xopcai/gateway-contract';
|
|
1
2
|
export type DeviceRoute = {
|
|
2
3
|
id: string;
|
|
3
4
|
kind: 'xopc-secure-link' | 'tailscale' | 'custom-https' | 'local-browser';
|
|
@@ -8,6 +9,7 @@ export type DevicePairingSetup = {
|
|
|
8
9
|
token: string;
|
|
9
10
|
routes: DeviceRoute[];
|
|
10
11
|
expiresAt: number;
|
|
12
|
+
targetKind: DevicePairingTargetKind;
|
|
11
13
|
};
|
|
12
14
|
export type DevicePairingEnrollment = {
|
|
13
15
|
issuer: 'browser-native-host';
|
|
@@ -15,17 +17,9 @@ export type DevicePairingEnrollment = {
|
|
|
15
17
|
publicKeyThumbprint: string;
|
|
16
18
|
nonce: string;
|
|
17
19
|
};
|
|
18
|
-
export
|
|
19
|
-
|
|
20
|
-
pairingId: string;
|
|
21
|
-
routes: DeviceRoute[];
|
|
22
|
-
} | {
|
|
23
|
-
ok: false;
|
|
24
|
-
reason: 'invalid' | 'expired' | 'consumed';
|
|
25
|
-
};
|
|
26
|
-
export declare function createDevicePairingSetup(routes: readonly DeviceRoute[], now?: number, protocolVersion?: 2 | 3, options?: {
|
|
20
|
+
export declare function createDevicePairingSetup(routes: readonly DeviceRoute[], now: number, options: {
|
|
21
|
+
targetKind: DevicePairingTargetKind;
|
|
27
22
|
ttlMs?: number;
|
|
28
23
|
enrollment?: DevicePairingEnrollment;
|
|
29
24
|
}): DevicePairingSetup;
|
|
30
|
-
export declare function consumeDevicePairingToken(token: string, now?: number): PairingConsumeResult;
|
|
31
25
|
export declare function isDevicePairingSetupActive(pairingId: string, now?: number): boolean;
|