@xopcai/xopc 0.0.143 → 0.0.145
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/manifest.json +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/Combination-DolwnuKi.js +41 -0
- package/dist/gateway/static/root/assets/{activity-center-store-jITiX2vJ.js → activity-center-store-3l9ahUqb.js} +1 -1
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-DiME9u_P.js → agent-avatar-dicebear-BEUxdhZx.js} +1 -1
- package/dist/gateway/static/root/assets/agents-1bpT0xRO.js +2 -0
- package/dist/gateway/static/root/assets/app-management-settings-panel-CRHSZJWi.js +1 -0
- package/dist/gateway/static/root/assets/{appearance-settings-DUfdNQqk.js → appearance-settings-BunADYJb.js} +1 -1
- package/dist/gateway/static/root/assets/apps-page-Cc4JgVUQ.js +1 -0
- package/dist/gateway/static/root/assets/{archive-plugin-B-Qk6cGr.js → archive-plugin-B6vTkH2A.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-Di6EEbQX.js → attachment-load-BjZXzaNv.js} +1 -1
- package/dist/gateway/static/root/assets/{automation-suggestion-card-DGxvRxtO.js → automation-suggestion-card-ZDA-Stg1.js} +1 -1
- package/dist/gateway/static/root/assets/automations-page-mb0fdVpq.js +6 -0
- package/dist/gateway/static/root/assets/{binary-plugins-ZaOZDH2I.js → binary-plugins-DYZfLvPz.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-BTFhjWfg.js → block-editor-C3MUlYC9.js} +46 -46
- package/dist/gateway/static/root/assets/browser-settings-page-Bb0xhaMN.js +8 -0
- package/dist/gateway/static/root/assets/capability-presets-api-BV76yN9a.js +1 -0
- package/dist/gateway/static/root/assets/capability-presets-settings-panel-CUcGKZqC.js +2 -0
- package/dist/gateway/static/root/assets/channel-recipient-api-fTlTnWB4.js +1 -0
- package/dist/gateway/static/root/assets/channels-settings-BlSDMGdy.js +1 -0
- package/dist/gateway/static/root/assets/channels-status-swr-CJauF1CW.js +1 -0
- package/dist/gateway/static/root/assets/{code-editor--tMO38Tr.js → code-editor-D4_k-6QN.js} +1 -1
- package/dist/gateway/static/root/assets/connector-logo-BVFNT2zW.js +1 -0
- package/dist/gateway/static/root/assets/connectors-page-CMgAFGvb.js +2 -0
- package/dist/gateway/static/root/assets/copy-to-clipboard-HOJ79TWv.js +1 -0
- package/dist/gateway/static/root/assets/{desktop-pet---yzVxVR.js → desktop-pet-_jUmoXd3.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-settings-Dc3gqBJE.js → desktop-pet-settings-DwGOy4AQ.js} +1 -1
- package/dist/gateway/static/root/assets/{directory-picker-path-field-DCmOGLtK.js → directory-picker-path-field-akvF0F-u.js} +1 -1
- package/dist/gateway/static/root/assets/dist-C99RGfAH.js +1 -0
- package/dist/gateway/static/root/assets/dreaming-settings-BLetQiYY.js +3 -0
- package/dist/gateway/static/root/assets/{extension-debug-page-ByaKTTq4.js → extension-debug-page-Cof2ncxE.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-BmRzHAZr.js → extension-page-CwWktRNz.js} +1 -1
- package/dist/gateway/static/root/assets/extension-settings-page-CuggWOIl.js +1 -0
- package/dist/gateway/static/root/assets/gateway-config-api-B7sxIbza.js +1 -0
- package/dist/gateway/static/root/assets/gateway-settings-DIv0eKMf.js +1 -0
- package/dist/gateway/static/root/assets/gateway-startup-retry-DtoLihQE.js +1 -0
- package/dist/gateway/static/root/assets/goal-create-dialog-C4Kw0mfS.js +1 -0
- package/dist/gateway/static/root/assets/goal-detail-page-BMmzYpZL.js +4 -0
- package/dist/gateway/static/root/assets/goals-config-api-B0jm5JaA.js +1 -0
- package/dist/gateway/static/root/assets/goals-page-CGeT77PC.js +2 -0
- package/dist/gateway/static/root/assets/goals-settings-N5_iL7es.js +1 -0
- package/dist/gateway/static/root/assets/heartbeat-settings-XzH3lbRR.js +1 -0
- package/dist/gateway/static/root/assets/{html-workspace-editor-COqmtESs.js → html-workspace-editor-C3DKAkMc.js} +1 -1
- package/dist/gateway/static/root/assets/index-CSLF84-Q.css +1 -0
- package/dist/gateway/static/root/assets/index-I-ZCenVw.js +259 -0
- package/dist/gateway/static/root/assets/keyboard-shortcuts-settings-x_v0nrIi.js +1 -0
- package/dist/gateway/static/root/assets/locale-store-CiM-D9jA.js +1 -0
- package/dist/gateway/static/root/assets/logs-page-BSh1t8cD.js +7 -0
- package/dist/gateway/static/root/assets/markdown-editor-BvUebh3C.js +2 -0
- package/dist/gateway/static/root/assets/{markdown-split-Dw-yE2-y.js → markdown-split-5rJx5flj.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-D9NNNUgE.js → media-plugins-DYXccwAd.js} +1 -1
- package/dist/gateway/static/root/assets/messages-Cb1VMeqP.js +5 -0
- package/dist/gateway/static/root/assets/models-hub-panel-BNpTenAI.js +2 -0
- package/dist/gateway/static/root/assets/{notes-page-CRgbJGgk.js → notes-page-BgLyndCO.js} +1 -1
- package/dist/gateway/static/root/assets/notes-workbench-teklkhcE.js +4 -0
- package/dist/gateway/static/root/assets/{page-tabs-D4hkWVt_.js → page-tabs-6Al9_sGz.js} +1 -1
- package/dist/gateway/static/root/assets/{preview-open-alternatives-CIckv0uy.js → preview-open-alternatives-Cm3G72Hv.js} +1 -1
- package/dist/gateway/static/root/assets/project-detail-page-CCB_dKoh.js +1 -0
- package/dist/gateway/static/root/assets/projects-page-VOAFAHYj.js +1 -0
- package/dist/gateway/static/root/assets/remote-access-hub-USYu64Ru.js +1 -0
- package/dist/gateway/static/root/assets/{save-bar-controls-B0b42kB2.js → save-bar-controls-BYtfzu_F.js} +1 -1
- package/dist/gateway/static/root/assets/{schedule-field-BtjEkczK.js → schedule-field-CvIRc6yK.js} +1 -1
- package/dist/gateway/static/root/assets/{schema-form-Bf0bk3i6.js → schema-form-CQuKx0n7.js} +1 -1
- package/dist/gateway/static/root/assets/sessions-page-oGC8O5h_.js +1 -0
- package/dist/gateway/static/root/assets/settings-advanced-gate-D4rP66R1.js +1 -0
- package/dist/gateway/static/root/assets/{settings-collapsible-section-BTKTdTNX.js → settings-collapsible-section-CF3HHM-t.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-form-section-DL4P0sAj.js → settings-form-section-CwWjoRN6.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-loading-skeleton-BV6KFNj4.js → settings-loading-skeleton-aUFmJa13.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-B_4DsX5W.js → settings-page-B2UUU9TF.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-layout-CyGlrInB.js → settings-page-layout-DqpbTWqI.js} +1 -1
- package/dist/gateway/static/root/assets/setup-status-panel-Ck9keROI.js +1 -0
- package/dist/gateway/static/root/assets/share-preview-page-BOhlPsS1.js +2 -0
- package/dist/gateway/static/root/assets/shares-settings-BYLsCuqj.js +1 -0
- package/dist/gateway/static/root/assets/skill-reload-api-BNHUyVML.js +1 -0
- package/dist/gateway/static/root/assets/skills-page-CpXbQH4n.js +2 -0
- package/dist/gateway/static/root/assets/{system-settings-panel-D85fBBuI.js → system-settings-panel-CWxSvuwD.js} +1 -1
- package/dist/gateway/static/root/assets/{theme-store-Bt6vAkSB.js → theme-store-C9Dxcppm.js} +1 -1
- package/dist/gateway/static/root/assets/{typed-models-lib-CwEuYrHX.js → typed-models-lib-qROAvqTw.js} +1 -1
- package/dist/gateway/static/root/assets/use-channel-catalog-DE9cpn4G.js +1 -0
- package/dist/gateway/static/root/assets/user-context-page-DbNEK4Yp.js +1 -0
- package/dist/gateway/static/root/assets/user-profile-fields-editor-CWxc0ghP.js +1 -0
- package/dist/gateway/static/root/assets/{user-profile-settings-panel-CWM0fEpb.js → user-profile-settings-panel-DW-Up6vt.js} +1 -1
- package/dist/gateway/static/root/assets/{utils--BO6xT3u.js → utils-D53uRXVr.js} +1 -1
- package/dist/gateway/static/root/assets/{vendor-codemirror-C1WKjCk5.js → vendor-codemirror--gTq3YAI.js} +17 -17
- package/dist/gateway/static/root/assets/voice-api-key-field-Cs7MOrg0.js +1 -0
- package/dist/gateway/static/root/assets/work-item-detail-page-D1ziG1Pp.js +1 -0
- package/dist/gateway/static/root/assets/workflow-run-setup-panel-D_x9ELJp.js +3 -0
- package/dist/gateway/static/root/assets/workflows-page-DHGV_n6i.js +11 -0
- package/dist/gateway/static/root/index.html +10 -10
- package/dist/package.js +1 -1
- package/dist/src/agent/embedded/session-runner.js +3 -3
- package/dist/src/agent/embedded/session-runner.js.map +1 -1
- package/dist/src/agent/embedded/xopc-auth-storage.d.ts +3 -1
- package/dist/src/agent/embedded/xopc-auth-storage.js +11 -1
- package/dist/src/agent/embedded/xopc-auth-storage.js.map +1 -1
- package/dist/src/agent/memory/dreaming/config.js +1 -1
- package/dist/src/agent-runtime/index.js +1 -1
- package/dist/src/agent-runtime/runtime-profile.js +1 -1
- package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
- package/dist/src/config/agent-profile.js +1 -1
- package/dist/src/config/index.js +2 -2
- package/dist/src/config/schema.d.ts +60 -5
- package/dist/src/config/schema.js +30 -9
- package/dist/src/config/schema.js.map +1 -1
- package/dist/src/connectors/composio-catalog.js +11 -2
- package/dist/src/connectors/composio-catalog.js.map +1 -1
- package/dist/src/connectors/connector-memory-sync.js +135 -26
- package/dist/src/connectors/connector-memory-sync.js.map +1 -1
- package/dist/src/gateway/agents-admin.js +1 -1
- package/dist/src/gateway/heartbeat/service.js +1 -1
- package/dist/src/gateway/hono/app.js +1 -1
- package/dist/src/gateway/hono/app.js.map +1 -1
- package/dist/src/gateway/hono/lib/config-payload.d.ts +11 -0
- package/dist/src/gateway/hono/lib/config-payload.js +1 -0
- package/dist/src/gateway/hono/lib/config-payload.js.map +1 -1
- package/dist/src/gateway/hono/routes/config-patch/misc.js +6 -1
- package/dist/src/gateway/hono/routes/config-patch/misc.js.map +1 -1
- package/dist/src/gateway/hono/routes/memory.js +11 -1
- package/dist/src/gateway/hono/routes/memory.js.map +1 -1
- package/dist/src/gateway/hono/routes/status.js +3 -0
- package/dist/src/gateway/hono/routes/status.js.map +1 -1
- package/dist/src/gateway/hono/routes/voice.d.ts +5 -5
- package/dist/src/gateway/hono/routes/voice.js +288 -62
- package/dist/src/gateway/hono/routes/voice.js.map +1 -1
- package/dist/src/gateway/hono/routes/you.js +5 -1
- package/dist/src/gateway/hono/routes/you.js.map +1 -1
- package/dist/src/gateway/service.d.ts +8 -0
- package/dist/src/gateway/service.js +41 -11
- package/dist/src/gateway/service.js.map +1 -1
- package/dist/src/heartbeat/index.js +1 -1
- package/dist/src/knowledge/connected-knowledge-pipeline.d.ts +21 -0
- package/dist/src/knowledge/connected-knowledge-pipeline.js +267 -0
- package/dist/src/knowledge/connected-knowledge-pipeline.js.map +1 -0
- package/dist/src/knowledge/coordinator.d.ts +10 -0
- package/dist/src/knowledge/coordinator.js +52 -0
- package/dist/src/knowledge/coordinator.js.map +1 -0
- package/dist/src/knowledge/index.d.ts +2 -0
- package/dist/src/knowledge/index.js +3 -1
- package/dist/src/knowledge/ingestion-service.d.ts +7 -1
- package/dist/src/knowledge/ingestion-service.js +28 -0
- package/dist/src/knowledge/ingestion-service.js.map +1 -1
- package/dist/src/knowledge/types.d.ts +18 -0
- package/dist/src/media-understanding/audio-transcription-runner.js +5 -3
- package/dist/src/media-understanding/audio-transcription-runner.js.map +1 -1
- package/dist/src/media-understanding/runner.js +8 -3
- package/dist/src/media-understanding/runner.js.map +1 -1
- package/dist/src/media-understanding/types.d.ts +7 -0
- package/dist/src/storage/sqlite/index.d.ts +1 -1
- package/dist/src/storage/sqlite/index.js +2 -2
- package/dist/src/storage/sqlite/knowledge-repository.d.ts +26 -1
- package/dist/src/storage/sqlite/knowledge-repository.js +120 -6
- package/dist/src/storage/sqlite/knowledge-repository.js.map +1 -1
- package/dist/src/storage/sqlite/migrations/037_connected_knowledge_pipeline.sql +47 -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.js.map +1 -1
- package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
- package/dist/src/storage/sqlite/schema.js +1 -1
- package/dist/src/user-context/projection.d.ts +13 -4
- package/dist/src/user-context/projection.js +19 -7
- package/dist/src/user-context/projection.js.map +1 -1
- package/dist/src/voice/language-profile.d.ts +20 -0
- package/dist/src/voice/language-profile.js +156 -0
- package/dist/src/voice/language-profile.js.map +1 -0
- package/dist/src/voice/local/model-files.d.ts +14 -0
- package/dist/src/voice/local/model-files.js +94 -0
- package/dist/src/voice/local/model-files.js.map +1 -0
- package/dist/src/voice/local/model-manager.d.ts +18 -0
- package/dist/src/voice/local/model-manager.js +135 -0
- package/dist/src/voice/local/model-manager.js.map +1 -0
- package/dist/src/voice/local/models.d.ts +28 -0
- package/dist/src/voice/local/models.js +101 -0
- package/dist/src/voice/local/models.js.map +1 -0
- package/dist/src/voice/local/runtime-client.d.ts +26 -0
- package/dist/src/voice/local/runtime-client.js +166 -0
- package/dist/src/voice/local/runtime-client.js.map +1 -0
- package/dist/src/voice/local/runtime-dispatcher.d.ts +9 -0
- package/dist/src/voice/local/runtime-dispatcher.js +22 -0
- package/dist/src/voice/local/runtime-dispatcher.js.map +1 -0
- package/dist/src/voice/local/runtime-worker.d.ts +1 -0
- package/dist/src/voice/local/runtime-worker.js +249 -0
- package/dist/src/voice/local/runtime-worker.js.map +1 -0
- package/dist/src/voice/local/wav.d.ts +6 -0
- package/dist/src/voice/local/wav.js +69 -0
- package/dist/src/voice/local/wav.js.map +1 -0
- package/dist/src/voice/metadata/builtin.js +46 -0
- package/dist/src/voice/metadata/builtin.js.map +1 -1
- package/dist/src/voice/stt/factory.js +8 -2
- package/dist/src/voice/stt/factory.js.map +1 -1
- package/dist/src/voice/stt/providers/alibaba-transcription.js +14 -8
- package/dist/src/voice/stt/providers/alibaba-transcription.js.map +1 -1
- package/dist/src/voice/stt/providers/index.d.ts +1 -0
- package/dist/src/voice/stt/providers/index.js +2 -1
- package/dist/src/voice/stt/providers/local-transcription.d.ts +2 -0
- package/dist/src/voice/stt/providers/local-transcription.js +51 -0
- package/dist/src/voice/stt/providers/local-transcription.js.map +1 -0
- package/dist/src/voice/stt/providers/openai-transcription.js +5 -3
- package/dist/src/voice/stt/providers/openai-transcription.js.map +1 -1
- package/dist/src/voice/stt/transcribe-core.js +5 -4
- package/dist/src/voice/stt/transcribe-core.js.map +1 -1
- package/dist/src/voice/stt/types.d.ts +4 -0
- package/dist/src/voice/stt/types.js +5 -4
- package/dist/src/voice/stt/types.js.map +1 -1
- package/dist/src/voice/tts/providers/edge-speech.js +2 -2
- package/dist/src/voice/tts/providers/edge-speech.js.map +1 -1
- package/dist/src/voice/tts/types.js +3 -3
- package/dist/src/voice/tts/types.js.map +1 -1
- package/package.json +4 -2
- package/dist/gateway/static/root/assets/Combination-HAlzriaz.js +0 -41
- package/dist/gateway/static/root/assets/agents-DzBXhZXO.js +0 -2
- package/dist/gateway/static/root/assets/app-management-settings-panel-Cuo7SbUP.js +0 -1
- package/dist/gateway/static/root/assets/apps-page-CKuKK5AU.js +0 -1
- package/dist/gateway/static/root/assets/automations-page-D-FF_WKV.js +0 -4
- package/dist/gateway/static/root/assets/browser-settings-page-BvKLdEQt.js +0 -8
- package/dist/gateway/static/root/assets/capability-presets-api-BU-pVZiZ.js +0 -1
- package/dist/gateway/static/root/assets/capability-presets-settings-panel-DgyeEwzh.js +0 -2
- package/dist/gateway/static/root/assets/channel-recipient-api-DuulnhIj.js +0 -1
- package/dist/gateway/static/root/assets/channels-settings-BbVsdD_I.js +0 -1
- package/dist/gateway/static/root/assets/channels-status-swr-B5lVrUsN.js +0 -1
- package/dist/gateway/static/root/assets/connectors-page-B8QqVy3I.js +0 -2
- package/dist/gateway/static/root/assets/dist-Mys7eFJ5.js +0 -1
- package/dist/gateway/static/root/assets/dreaming-settings-BgT5KZVe.js +0 -3
- package/dist/gateway/static/root/assets/extension-settings-page-JUtwIhbF.js +0 -1
- package/dist/gateway/static/root/assets/fetch-8QNN25Sh.js +0 -1
- package/dist/gateway/static/root/assets/gateway-config-api-CxicF50_.js +0 -1
- package/dist/gateway/static/root/assets/gateway-settings-Bz2I_EO5.js +0 -1
- package/dist/gateway/static/root/assets/gateway-startup-retry-5YBnS9Fj.js +0 -1
- package/dist/gateway/static/root/assets/goal-create-dialog-VcoyuZGN.js +0 -1
- package/dist/gateway/static/root/assets/goal-detail-page-Cilkap2G.js +0 -4
- package/dist/gateway/static/root/assets/goals-config-api-CwcHRdC0.js +0 -1
- package/dist/gateway/static/root/assets/goals-page-C6eoni4z.js +0 -2
- package/dist/gateway/static/root/assets/goals-settings-o4i-qAOS.js +0 -1
- package/dist/gateway/static/root/assets/heartbeat-settings-DQQkETX8.js +0 -1
- package/dist/gateway/static/root/assets/index-BKRqeRHm.js +0 -257
- package/dist/gateway/static/root/assets/index-CphxClbs.css +0 -1
- package/dist/gateway/static/root/assets/keyboard-shortcuts-settings-LT6qB-ua.js +0 -1
- package/dist/gateway/static/root/assets/locale-store-Ba72bFmP.js +0 -1
- package/dist/gateway/static/root/assets/logs-page-Dli2e3fS.js +0 -7
- package/dist/gateway/static/root/assets/markdown-editor-jD1LPiP-.js +0 -2
- package/dist/gateway/static/root/assets/messages-CzKxM6I4.js +0 -5
- package/dist/gateway/static/root/assets/models-hub-panel-DVn2Q1Ik.js +0 -2
- package/dist/gateway/static/root/assets/notes-workbench-BxZRHLWx.js +0 -2
- package/dist/gateway/static/root/assets/project-detail-page-Doz53xLn.js +0 -1
- package/dist/gateway/static/root/assets/projects-page-DoMtcRLg.js +0 -1
- package/dist/gateway/static/root/assets/remote-access-hub-BeHVEgrm.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-Bk4bQsBe.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-DdFyaRMm.js +0 -1
- package/dist/gateway/static/root/assets/setup-status-panel-DJ1_MLgX.js +0 -1
- package/dist/gateway/static/root/assets/share-preview-page-BQBV7YrV.js +0 -2
- package/dist/gateway/static/root/assets/shares-settings-0VuFLBeU.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-DO87Ckeo.js +0 -1
- package/dist/gateway/static/root/assets/skills-page-D3VdtxAn.js +0 -2
- package/dist/gateway/static/root/assets/use-channel-catalog-COXIfXcl.js +0 -1
- package/dist/gateway/static/root/assets/user-context-page-m9JVQ73F.js +0 -1
- package/dist/gateway/static/root/assets/user-profile-fields-editor-DtQiuY7E.js +0 -1
- package/dist/gateway/static/root/assets/voice-api-key-field-LoT3wgZ9.js +0 -1
- package/dist/gateway/static/root/assets/work-item-detail-page-DSTMz_mO.js +0 -1
- package/dist/gateway/static/root/assets/workflow-run-setup-panel-B35lJzvm.js +0 -3
- package/dist/gateway/static/root/assets/workflows-page-BfSm9oR8.js +0 -11
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { upsertMemoryRecord } from "../storage/sqlite/memory-records-repository.js";
|
|
2
1
|
import { getConnectorInstallation, listConnectorConnections } from "../storage/sqlite/connector-repository.js";
|
|
3
2
|
import "../storage/sqlite/index.js";
|
|
4
3
|
import { ComposioSessionsAdapter } from "./composio-sessions.js";
|
|
5
4
|
import { scopeForComposioAction } from "./composio.js";
|
|
6
5
|
import { getConnectorDefinition } from "./catalog.js";
|
|
7
6
|
import { getWorkspacePath } from "../config/workspace-path-helpers.js";
|
|
7
|
+
import { KnowledgeIngestionService } from "../knowledge/ingestion-service.js";
|
|
8
|
+
import { ConnectedKnowledgePipeline } from "../knowledge/connected-knowledge-pipeline.js";
|
|
9
|
+
import "../knowledge/index.js";
|
|
8
10
|
import { createHash } from "node:crypto";
|
|
9
11
|
//#region src/connectors/connector-memory-sync.ts
|
|
10
12
|
const MEMORY_SOURCE_TOOLKITS = new Set([
|
|
@@ -13,6 +15,110 @@ const MEMORY_SOURCE_TOOLKITS = new Set([
|
|
|
13
15
|
"notion",
|
|
14
16
|
"slack"
|
|
15
17
|
]);
|
|
18
|
+
const EXTERNAL_ID_KEYS = [
|
|
19
|
+
"id",
|
|
20
|
+
"messageId",
|
|
21
|
+
"message_id",
|
|
22
|
+
"threadId",
|
|
23
|
+
"thread_id",
|
|
24
|
+
"eventId",
|
|
25
|
+
"event_id",
|
|
26
|
+
"pageId",
|
|
27
|
+
"page_id",
|
|
28
|
+
"fileId",
|
|
29
|
+
"file_id"
|
|
30
|
+
];
|
|
31
|
+
const OCCURRED_AT_KEYS = [
|
|
32
|
+
"occurredAt",
|
|
33
|
+
"createdAt",
|
|
34
|
+
"created_at",
|
|
35
|
+
"date",
|
|
36
|
+
"timestamp",
|
|
37
|
+
"startTime",
|
|
38
|
+
"start_time"
|
|
39
|
+
];
|
|
40
|
+
const UPDATED_AT_KEYS = [
|
|
41
|
+
"updatedAt",
|
|
42
|
+
"updated_at",
|
|
43
|
+
"modifiedTime",
|
|
44
|
+
"modified_time"
|
|
45
|
+
];
|
|
46
|
+
const SENSITIVE_KEY_RE = /(?:^|_)(?:api_?key|token|secret|password|passwd|authorization|credential|private_?key)(?:$|_)/i;
|
|
47
|
+
const INLINE_SECRET_PATTERNS = [
|
|
48
|
+
/\bsk-[A-Za-z0-9_-]{8,}\b/g,
|
|
49
|
+
/\bghp_[A-Za-z0-9]{20,}\b/g,
|
|
50
|
+
/\bxox[baprs]-[A-Za-z0-9-]{20,}\b/g,
|
|
51
|
+
/-----BEGIN [^-]*PRIVATE KEY-----[\s\S]*?-----END [^-]*PRIVATE KEY-----/g
|
|
52
|
+
];
|
|
53
|
+
function asRecord(value) {
|
|
54
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
55
|
+
}
|
|
56
|
+
function resultRows(value) {
|
|
57
|
+
if (Array.isArray(value)) return value;
|
|
58
|
+
const record = asRecord(value);
|
|
59
|
+
if (!record) return [value];
|
|
60
|
+
for (const key of [
|
|
61
|
+
"items",
|
|
62
|
+
"messages",
|
|
63
|
+
"results",
|
|
64
|
+
"events",
|
|
65
|
+
"files",
|
|
66
|
+
"pages",
|
|
67
|
+
"data"
|
|
68
|
+
]) if (Array.isArray(record[key])) return record[key];
|
|
69
|
+
return [value];
|
|
70
|
+
}
|
|
71
|
+
function stringField(record, keys) {
|
|
72
|
+
for (const key of keys) {
|
|
73
|
+
const value = record?.[key];
|
|
74
|
+
if (typeof value === "string" && value.trim()) return value.trim();
|
|
75
|
+
if (typeof value === "number" && Number.isFinite(value)) return String(value);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
function timestampField(record, keys) {
|
|
79
|
+
const value = stringField(record, keys);
|
|
80
|
+
if (!value) return void 0;
|
|
81
|
+
const parsed = Date.parse(value);
|
|
82
|
+
return Number.isFinite(parsed) ? new Date(parsed).toISOString() : void 0;
|
|
83
|
+
}
|
|
84
|
+
function sanitizeSourceValue(value, depth = 0) {
|
|
85
|
+
if (depth > 12) return "[MaxDepth]";
|
|
86
|
+
if (typeof value === "string") return INLINE_SECRET_PATTERNS.reduce((text, pattern) => text.replace(pattern, "[REDACTED]"), value);
|
|
87
|
+
if (Array.isArray(value)) return value.map((item) => sanitizeSourceValue(item, depth + 1));
|
|
88
|
+
const record = asRecord(value);
|
|
89
|
+
if (!record) return value;
|
|
90
|
+
return Object.fromEntries(Object.entries(record).map(([key, nested]) => [key, SENSITIVE_KEY_RE.test(key) ? "[REDACTED]" : sanitizeSourceValue(nested, depth + 1)]));
|
|
91
|
+
}
|
|
92
|
+
function resultToSourceItems(input) {
|
|
93
|
+
return resultRows(input.result).slice(0, 200).map((row, index) => {
|
|
94
|
+
const record = asRecord(row);
|
|
95
|
+
const serialized = JSON.stringify(sanitizeSourceValue(row), null, 2) ?? String(row);
|
|
96
|
+
const normalizedText = serialized.length > 8e3 ? `${serialized.slice(0, 8e3)}\n…` : serialized;
|
|
97
|
+
const contentHash = createHash("sha256").update(serialized).digest("hex");
|
|
98
|
+
const externalId = stringField(record, EXTERNAL_ID_KEYS) ?? `${input.actionId}:${index}`;
|
|
99
|
+
return {
|
|
100
|
+
sourceInstanceId: input.sourceInstanceId,
|
|
101
|
+
externalId,
|
|
102
|
+
itemType: `${input.connectorId}:${input.actionId}`,
|
|
103
|
+
authorRole: "third_party",
|
|
104
|
+
occurredAt: timestampField(record, OCCURRED_AT_KEYS),
|
|
105
|
+
sourceUpdatedAt: timestampField(record, UPDATED_AT_KEYS),
|
|
106
|
+
contentHash,
|
|
107
|
+
normalizedText,
|
|
108
|
+
metadata: {
|
|
109
|
+
connectorId: input.connectorId,
|
|
110
|
+
connectionId: input.connectionId,
|
|
111
|
+
actionId: input.actionId,
|
|
112
|
+
agentId: input.agentId,
|
|
113
|
+
workspaceId: input.workspaceId
|
|
114
|
+
},
|
|
115
|
+
sensitivity: "personal",
|
|
116
|
+
retentionClass: "bounded",
|
|
117
|
+
synthesisPipeline: "connected_knowledge",
|
|
118
|
+
synthesisStatus: "pending"
|
|
119
|
+
};
|
|
120
|
+
});
|
|
121
|
+
}
|
|
16
122
|
async function syncComposioResultToMemory(input) {
|
|
17
123
|
const definition = getConnectorDefinition(input.connectorId);
|
|
18
124
|
if (definition?.runtime.type !== "composio" || definition.runtime.role !== "toolkit") throw new Error("Memory sync requires a Composio toolkit connector.");
|
|
@@ -54,32 +160,35 @@ async function syncComposioResultToMemory(input) {
|
|
|
54
160
|
confirmed: true
|
|
55
161
|
});
|
|
56
162
|
if (execution.decision !== "allowed") throw new Error(execution.reason);
|
|
57
|
-
const
|
|
58
|
-
const
|
|
59
|
-
const
|
|
163
|
+
const workspaceId = getWorkspacePath(input.config);
|
|
164
|
+
const sourceInstanceId = `composio:${connectorId}:${connection.id}`;
|
|
165
|
+
const items = resultToSourceItems({
|
|
166
|
+
result: execution.result,
|
|
167
|
+
sourceInstanceId,
|
|
168
|
+
connectorId,
|
|
169
|
+
connectionId: connection.id,
|
|
170
|
+
actionId: input.actionId,
|
|
171
|
+
agentId: input.agentId,
|
|
172
|
+
workspaceId
|
|
173
|
+
});
|
|
174
|
+
new KnowledgeIngestionService(/* @__PURE__ */ new Map()).ingest({
|
|
175
|
+
instanceId: sourceInstanceId,
|
|
176
|
+
items,
|
|
177
|
+
cursorAfter: (/* @__PURE__ */ new Date()).toISOString()
|
|
178
|
+
});
|
|
179
|
+
const pipeline = new ConnectedKnowledgePipeline({
|
|
180
|
+
agentId: input.agentId,
|
|
181
|
+
workspaceId
|
|
182
|
+
});
|
|
183
|
+
let recordId;
|
|
184
|
+
for (let batch = 0; batch < 10; batch += 1) {
|
|
185
|
+
const synthesis = await pipeline.processPending(sourceInstanceId);
|
|
186
|
+
recordId ??= synthesis.recordIds[0];
|
|
187
|
+
if (synthesis.claimed === 0) break;
|
|
188
|
+
}
|
|
189
|
+
if (!recordId) throw new Error("Connector result did not contain any synthesizable knowledge.");
|
|
60
190
|
return {
|
|
61
|
-
recordId
|
|
62
|
-
providerId: "connector-composio",
|
|
63
|
-
kind: "workspace_fact",
|
|
64
|
-
agentId: input.agentId,
|
|
65
|
-
workspaceId: getWorkspacePath(input.config),
|
|
66
|
-
content,
|
|
67
|
-
canonicalKey,
|
|
68
|
-
source: { provider: connectorId },
|
|
69
|
-
confidence: .75,
|
|
70
|
-
tags: [
|
|
71
|
-
"connector",
|
|
72
|
-
"external",
|
|
73
|
-
toolkit,
|
|
74
|
-
"needs-review"
|
|
75
|
-
],
|
|
76
|
-
status: "needs_review",
|
|
77
|
-
sensitivity: "normal",
|
|
78
|
-
explicitness: "observed",
|
|
79
|
-
durability: "recurring",
|
|
80
|
-
importance: .5,
|
|
81
|
-
disclosurePolicy: "referenceable"
|
|
82
|
-
}).id,
|
|
191
|
+
recordId,
|
|
83
192
|
connectorId,
|
|
84
193
|
actionId: input.actionId
|
|
85
194
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connector-memory-sync.js","names":[],"sources":["../../../src/connectors/connector-memory-sync.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\n\nimport type { Config } from '../config/schema.js';\nimport { getWorkspacePath } from '../config/workspace-path-helpers.js';\nimport {\n getConnectorInstallation,\n listConnectorConnections,\n upsertMemoryRecord,\n} from '../storage/sqlite/index.js';\nimport { ComposioSessionsAdapter } from './composio-sessions.js';\nimport { scopeForComposioAction } from './composio.js';\nimport { getConnectorDefinition } from './catalog.js';\n\nconst MEMORY_SOURCE_TOOLKITS = new Set(['gmail', 'googledrive', 'notion', 'slack']);\n\nexport async function syncComposioResultToMemory(input: {\n config: Config;\n connectorId: string;\n actionId: string;\n arguments?: Record<string, unknown>;\n agentId: string;\n connectionId?: string;\n adapter?: ComposioSessionsAdapter;\n}): Promise<{ recordId: string; connectorId: string; actionId: string }> {\n const definition = getConnectorDefinition(input.connectorId);\n if (definition?.runtime.type !== 'composio' || definition.runtime.role !== 'toolkit') {\n throw new Error('Memory sync requires a Composio toolkit connector.');\n }\n const toolkit = definition.runtime.toolkit;\n if (!MEMORY_SOURCE_TOOLKITS.has(toolkit)) {\n throw new Error('Memory sync is supported for Gmail, Google Drive, Notion, and Slack.');\n }\n const risk = scopeForComposioAction(input.actionId);\n if (risk.toolkit !== toolkit || risk.scope !== 'read' || !risk.curated) {\n throw new Error('Memory sync only accepts a verified read action for the selected connector.');\n }\n const connectorId = definition.id;\n const installation = getConnectorInstallation(`${connectorId}-local-owner`);\n if (!installation?.enabled) throw new Error('Connector installation is not enabled.');\n if (installation.allowedAgentIds.length && !installation.allowedAgentIds.includes(input.agentId)) {\n throw new Error('This agent is not allowed to sync from the connector.');\n }\n const activeConnections = listConnectorConnections({ principalId: 'local-owner', connectorId })\n .filter((connection) => connection.status === 'active');\n const connection = input.connectionId\n ? activeConnections.find((candidate) => candidate.id === input.connectionId)\n : activeConnections.find((candidate) => candidate.isDefault) ?? activeConnections[0];\n if (!connection) throw new Error('No active connector account is available for memory sync.');\n const adapter = input.adapter ?? new ComposioSessionsAdapter();\n const execution = await adapter.executeWithPolicy({\n context: { principalId: 'local-owner', toolkits: [toolkit] },\n installation: { ...installation, maxScope: 'read', confirmationPolicy: 'never' },\n connection,\n agentId: input.agentId,\n action: {\n connectorId,\n actionId: input.actionId,\n toolkit,\n scope: 'read',\n curated: true,\n cachedAt: new Date().toISOString(),\n },\n args: input.arguments ?? {},\n confirmed: true,\n });\n if (execution.decision !== 'allowed') throw new Error(execution.reason);\n const serialized = JSON.stringify(execution.result, null, 2);\n const content = serialized.length > 20_000 ? `${serialized.slice(0, 20_000)}\\n…` : serialized;\n const canonicalKey = `connector:${connectorId}:${connection.id}:${input.actionId}:${createHash('sha256').update(JSON.stringify(input.arguments ?? {})).digest('hex').slice(0, 16)}`;\n const record = upsertMemoryRecord({\n providerId: 'connector-composio',\n kind: 'workspace_fact',\n agentId: input.agentId,\n workspaceId: getWorkspacePath(input.config),\n content,\n canonicalKey,\n source: { provider: connectorId },\n confidence: 0.75,\n tags: ['connector', 'external', toolkit, 'needs-review'],\n status: 'needs_review',\n sensitivity: 'normal',\n explicitness: 'observed',\n durability: 'recurring',\n importance: 0.5,\n disclosurePolicy: 'referenceable',\n });\n return { recordId: record.id, connectorId, actionId: input.actionId };\n}\n"],"mappings":";;;;;;;;;AAaA,MAAM,yBAAyB,IAAI,IAAI;CAAC;CAAS;CAAe;CAAU;CAAQ,CAAC;AAEnF,eAAsB,2BAA2B,OAQwB;CACvE,MAAM,aAAa,uBAAuB,MAAM,YAAY;AAC5D,KAAI,YAAY,QAAQ,SAAS,cAAc,WAAW,QAAQ,SAAS,UACzE,OAAM,IAAI,MAAM,qDAAqD;CAEvE,MAAM,UAAU,WAAW,QAAQ;AACnC,KAAI,CAAC,uBAAuB,IAAI,QAAQ,CACtC,OAAM,IAAI,MAAM,uEAAuE;CAEzF,MAAM,OAAO,uBAAuB,MAAM,SAAS;AACnD,KAAI,KAAK,YAAY,WAAW,KAAK,UAAU,UAAU,CAAC,KAAK,QAC7D,OAAM,IAAI,MAAM,8EAA8E;CAEhG,MAAM,cAAc,WAAW;CAC/B,MAAM,eAAe,yBAAyB,GAAG,YAAY,cAAc;AAC3E,KAAI,CAAC,cAAc,QAAS,OAAM,IAAI,MAAM,yCAAyC;AACrF,KAAI,aAAa,gBAAgB,UAAU,CAAC,aAAa,gBAAgB,SAAS,MAAM,QAAQ,CAC9F,OAAM,IAAI,MAAM,wDAAwD;CAE1E,MAAM,oBAAoB,yBAAyB;EAAE,aAAa;EAAe;EAAa,CAAC,CAC5F,QAAQ,eAAe,WAAW,WAAW,SAAS;CACzD,MAAM,aAAa,MAAM,eACrB,kBAAkB,MAAM,cAAc,UAAU,OAAO,MAAM,aAAa,GAC1E,kBAAkB,MAAM,cAAc,UAAU,UAAU,IAAI,kBAAkB;AACpF,KAAI,CAAC,WAAY,OAAM,IAAI,MAAM,4DAA4D;CAE7F,MAAM,YAAY,OADF,MAAM,WAAW,IAAI,yBAAyB,EAC9B,kBAAkB;EAChD,SAAS;GAAE,aAAa;GAAe,UAAU,CAAC,QAAQ;GAAE;EAC5D,cAAc;GAAE,GAAG;GAAc,UAAU;GAAQ,oBAAoB;GAAS;EAChF;EACA,SAAS,MAAM;EACf,QAAQ;GACN;GACA,UAAU,MAAM;GAChB;GACA,OAAO;GACP,SAAS;GACT,2BAAU,IAAI,MAAM,EAAC,aAAa;GACnC;EACD,MAAM,MAAM,aAAa,EAAE;EAC3B,WAAW;EACZ,CAAC;AACF,KAAI,UAAU,aAAa,UAAW,OAAM,IAAI,MAAM,UAAU,OAAO;CACvE,MAAM,aAAa,KAAK,UAAU,UAAU,QAAQ,MAAM,EAAE;CAC5D,MAAM,UAAU,WAAW,SAAS,MAAS,GAAG,WAAW,MAAM,GAAG,IAAO,CAAC,OAAO;CACnF,MAAM,eAAe,aAAa,YAAY,GAAG,WAAW,GAAG,GAAG,MAAM,SAAS,GAAG,WAAW,SAAS,CAAC,OAAO,KAAK,UAAU,MAAM,aAAa,EAAE,CAAC,CAAC,CAAC,OAAO,MAAM,CAAC,MAAM,GAAG,GAAG;AAkBjL,QAAO;EAAE,UAjBM,mBAAmB;GAChC,YAAY;GACZ,MAAM;GACN,SAAS,MAAM;GACf,aAAa,iBAAiB,MAAM,OAAO;GAC3C;GACA;GACA,QAAQ,EAAE,UAAU,aAAa;GACjC,YAAY;GACZ,MAAM;IAAC;IAAa;IAAY;IAAS;IAAe;GACxD,QAAQ;GACR,aAAa;GACb,cAAc;GACd,YAAY;GACZ,YAAY;GACZ,kBAAkB;GACnB,CACwB,CAAC;EAAI;EAAa,UAAU,MAAM;EAAU"}
|
|
1
|
+
{"version":3,"file":"connector-memory-sync.js","names":[],"sources":["../../../src/connectors/connector-memory-sync.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\n\nimport type { Config } from '../config/schema.js';\nimport { getWorkspacePath } from '../config/workspace-path-helpers.js';\nimport {\n getConnectorInstallation,\n listConnectorConnections,\n} from '../storage/sqlite/index.js';\nimport { ConnectedKnowledgePipeline, KnowledgeIngestionService, type KnowledgeSourceItemInput } from '../knowledge/index.js';\nimport { ComposioSessionsAdapter } from './composio-sessions.js';\nimport { scopeForComposioAction } from './composio.js';\nimport { getConnectorDefinition } from './catalog.js';\n\nconst MEMORY_SOURCE_TOOLKITS = new Set(['gmail', 'googledrive', 'notion', 'slack']);\nconst EXTERNAL_ID_KEYS = ['id', 'messageId', 'message_id', 'threadId', 'thread_id', 'eventId', 'event_id', 'pageId', 'page_id', 'fileId', 'file_id'];\nconst OCCURRED_AT_KEYS = ['occurredAt', 'createdAt', 'created_at', 'date', 'timestamp', 'startTime', 'start_time'];\nconst UPDATED_AT_KEYS = ['updatedAt', 'updated_at', 'modifiedTime', 'modified_time'];\nconst SENSITIVE_KEY_RE = /(?:^|_)(?:api_?key|token|secret|password|passwd|authorization|credential|private_?key)(?:$|_)/i;\nconst INLINE_SECRET_PATTERNS = [\n /\\bsk-[A-Za-z0-9_-]{8,}\\b/g,\n /\\bghp_[A-Za-z0-9]{20,}\\b/g,\n /\\bxox[baprs]-[A-Za-z0-9-]{20,}\\b/g,\n /-----BEGIN [^-]*PRIVATE KEY-----[\\s\\S]*?-----END [^-]*PRIVATE KEY-----/g,\n];\n\nfunction asRecord(value: unknown): Record<string, unknown> | null {\n return value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : null;\n}\n\nfunction resultRows(value: unknown): unknown[] {\n if (Array.isArray(value)) return value;\n const record = asRecord(value);\n if (!record) return [value];\n for (const key of ['items', 'messages', 'results', 'events', 'files', 'pages', 'data']) {\n if (Array.isArray(record[key])) return record[key] as unknown[];\n }\n return [value];\n}\n\nfunction stringField(record: Record<string, unknown> | null, keys: string[]): string | undefined {\n for (const key of keys) {\n const value = record?.[key];\n if (typeof value === 'string' && value.trim()) return value.trim();\n if (typeof value === 'number' && Number.isFinite(value)) return String(value);\n }\n return undefined;\n}\n\nfunction timestampField(record: Record<string, unknown> | null, keys: string[]): string | undefined {\n const value = stringField(record, keys);\n if (!value) return undefined;\n const parsed = Date.parse(value);\n return Number.isFinite(parsed) ? new Date(parsed).toISOString() : undefined;\n}\n\nfunction sanitizeSourceValue(value: unknown, depth = 0): unknown {\n if (depth > 12) return '[MaxDepth]';\n if (typeof value === 'string') {\n return INLINE_SECRET_PATTERNS.reduce((text, pattern) => text.replace(pattern, '[REDACTED]'), value);\n }\n if (Array.isArray(value)) return value.map((item) => sanitizeSourceValue(item, depth + 1));\n const record = asRecord(value);\n if (!record) return value;\n return Object.fromEntries(Object.entries(record).map(([key, nested]) => [\n key,\n SENSITIVE_KEY_RE.test(key) ? '[REDACTED]' : sanitizeSourceValue(nested, depth + 1),\n ]));\n}\n\nfunction resultToSourceItems(input: {\n result: unknown;\n sourceInstanceId: string;\n connectorId: string;\n connectionId: string;\n actionId: string;\n agentId: string;\n workspaceId: string;\n}): KnowledgeSourceItemInput[] {\n return resultRows(input.result).slice(0, 200).map((row, index) => {\n const record = asRecord(row);\n const serialized = JSON.stringify(sanitizeSourceValue(row), null, 2) ?? String(row);\n const normalizedText = serialized.length > 8_000 ? `${serialized.slice(0, 8_000)}\\n…` : serialized;\n const contentHash = createHash('sha256').update(serialized).digest('hex');\n const externalId = stringField(record, EXTERNAL_ID_KEYS) ?? `${input.actionId}:${index}`;\n return {\n sourceInstanceId: input.sourceInstanceId,\n externalId,\n itemType: `${input.connectorId}:${input.actionId}`,\n authorRole: 'third_party',\n occurredAt: timestampField(record, OCCURRED_AT_KEYS),\n sourceUpdatedAt: timestampField(record, UPDATED_AT_KEYS),\n contentHash,\n normalizedText,\n metadata: {\n connectorId: input.connectorId,\n connectionId: input.connectionId,\n actionId: input.actionId,\n agentId: input.agentId,\n workspaceId: input.workspaceId,\n },\n sensitivity: 'personal',\n retentionClass: 'bounded',\n synthesisPipeline: 'connected_knowledge',\n synthesisStatus: 'pending',\n };\n });\n}\n\nexport async function syncComposioResultToMemory(input: {\n config: Config;\n connectorId: string;\n actionId: string;\n arguments?: Record<string, unknown>;\n agentId: string;\n connectionId?: string;\n adapter?: ComposioSessionsAdapter;\n}): Promise<{ recordId: string; connectorId: string; actionId: string }> {\n const definition = getConnectorDefinition(input.connectorId);\n if (definition?.runtime.type !== 'composio' || definition.runtime.role !== 'toolkit') {\n throw new Error('Memory sync requires a Composio toolkit connector.');\n }\n const toolkit = definition.runtime.toolkit;\n if (!MEMORY_SOURCE_TOOLKITS.has(toolkit)) {\n throw new Error('Memory sync is supported for Gmail, Google Drive, Notion, and Slack.');\n }\n const risk = scopeForComposioAction(input.actionId);\n if (risk.toolkit !== toolkit || risk.scope !== 'read' || !risk.curated) {\n throw new Error('Memory sync only accepts a verified read action for the selected connector.');\n }\n const connectorId = definition.id;\n const installation = getConnectorInstallation(`${connectorId}-local-owner`);\n if (!installation?.enabled) throw new Error('Connector installation is not enabled.');\n if (installation.allowedAgentIds.length && !installation.allowedAgentIds.includes(input.agentId)) {\n throw new Error('This agent is not allowed to sync from the connector.');\n }\n const activeConnections = listConnectorConnections({ principalId: 'local-owner', connectorId })\n .filter((connection) => connection.status === 'active');\n const connection = input.connectionId\n ? activeConnections.find((candidate) => candidate.id === input.connectionId)\n : activeConnections.find((candidate) => candidate.isDefault) ?? activeConnections[0];\n if (!connection) throw new Error('No active connector account is available for memory sync.');\n const adapter = input.adapter ?? new ComposioSessionsAdapter();\n const execution = await adapter.executeWithPolicy({\n context: { principalId: 'local-owner', toolkits: [toolkit] },\n installation: { ...installation, maxScope: 'read', confirmationPolicy: 'never' },\n connection,\n agentId: input.agentId,\n action: {\n connectorId,\n actionId: input.actionId,\n toolkit,\n scope: 'read',\n curated: true,\n cachedAt: new Date().toISOString(),\n },\n args: input.arguments ?? {},\n confirmed: true,\n });\n if (execution.decision !== 'allowed') throw new Error(execution.reason);\n const workspaceId = getWorkspacePath(input.config);\n const sourceInstanceId = `composio:${connectorId}:${connection.id}`;\n const items = resultToSourceItems({\n result: execution.result,\n sourceInstanceId,\n connectorId,\n connectionId: connection.id,\n actionId: input.actionId,\n agentId: input.agentId,\n workspaceId,\n });\n new KnowledgeIngestionService(new Map()).ingest({\n instanceId: sourceInstanceId,\n items,\n cursorAfter: new Date().toISOString(),\n });\n const pipeline = new ConnectedKnowledgePipeline({ agentId: input.agentId, workspaceId });\n let recordId: string | undefined;\n for (let batch = 0; batch < 10; batch += 1) {\n const synthesis = await pipeline.processPending(sourceInstanceId);\n recordId ??= synthesis.recordIds[0];\n if (synthesis.claimed === 0) break;\n }\n if (!recordId) throw new Error('Connector result did not contain any synthesizable knowledge.');\n return { recordId, connectorId, actionId: input.actionId };\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { init_agent_scope, listAgentEntries, normalizeAgentId, resolveAgentDir, resolveAgentProfileDir, resolveAgentWorkspaceDir, resolveDefaultAgentId, resolveUserPath, validateAgentIdForNewAgent } from "../agent/agent-scope.js";
|
|
2
1
|
import { resolveEffectiveAgentManifest } from "../agent-manifest/resolver.js";
|
|
2
|
+
import { init_agent_scope, listAgentEntries, normalizeAgentId, resolveAgentDir, resolveAgentProfileDir, resolveAgentWorkspaceDir, resolveDefaultAgentId, resolveUserPath, validateAgentIdForNewAgent } from "../agent/agent-scope.js";
|
|
3
3
|
import { resolveEffectiveAgentProfile } from "../config/agent-profile.js";
|
|
4
4
|
import { WORKSPACE_FILES, init_paths, resolveUserProfilePath } from "../config/paths.js";
|
|
5
5
|
import { AGENT_PROFILE_MARKDOWN_SYSTEM_FILES, REQUIRED_AGENT_PROFILE_MARKDOWN_FILE_SET } from "../agent/context/workspace.js";
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { createLogger } from "../../utils/logger/index.js";
|
|
2
2
|
import { init_logger } from "../../utils/logger.js";
|
|
3
3
|
import { shouldSilence, stripHeartbeatToken } from "../../heartbeat/tokens.js";
|
|
4
|
-
import { appendCronEventLines } from "../../heartbeat/event-prompt.js";
|
|
5
4
|
import { isWithinActiveHours } from "../../heartbeat/active-hours.js";
|
|
6
5
|
import { isHeartbeatContentEmpty } from "../../heartbeat/content-check.js";
|
|
6
|
+
import { appendCronEventLines } from "../../heartbeat/event-prompt.js";
|
|
7
7
|
import { createHeartbeatWake } from "../../heartbeat/wake.js";
|
|
8
8
|
import { resolveHeartbeatMdPath } from "../workspace-heartbeat-path.js";
|
|
9
9
|
import { readFile } from "fs/promises";
|
|
@@ -173,7 +173,7 @@ function createHonoApp(config) {
|
|
|
173
173
|
}));
|
|
174
174
|
app.use("/api/*", async (c, next) => {
|
|
175
175
|
const contentType = c.req.header("content-type");
|
|
176
|
-
const maxSize = c.req.path === "/api/agent" ? WEBCHAT_AGENT_BODY_MAX : c.req.path === "/api/skills/upload" ? SKILL_UPLOAD_BODY_MAX : c.req.path === "/api/voice/transcribe" ? VOICE_TRANSCRIBE_BODY_MAX : isNoteMediaUploadRequest(c.req.path, c.req.method, contentType) ? NOTE_MEDIA_BODY_MAX : isWorkItemAttachmentUploadRequest(c.req.path, c.req.method, contentType) ? WORK_ITEM_ATTACHMENT_UPLOAD_BODY_MAX_BYTES : DEFAULT_API_BODY_MAX;
|
|
176
|
+
const maxSize = c.req.path === "/api/agent" ? WEBCHAT_AGENT_BODY_MAX : c.req.path === "/api/skills/upload" ? SKILL_UPLOAD_BODY_MAX : c.req.path === "/api/voice/transcribe" || c.req.path === "/api/voice/transcriptions" ? VOICE_TRANSCRIBE_BODY_MAX : isNoteMediaUploadRequest(c.req.path, c.req.method, contentType) ? NOTE_MEDIA_BODY_MAX : isWorkItemAttachmentUploadRequest(c.req.path, c.req.method, contentType) ? WORK_ITEM_ATTACHMENT_UPLOAD_BODY_MAX_BYTES : DEFAULT_API_BODY_MAX;
|
|
177
177
|
const maxSizeMb = Math.ceil(maxSize / (1024 * 1024));
|
|
178
178
|
return bodyLimit({
|
|
179
179
|
maxSize,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.js","names":[],"sources":["../../../../src/gateway/hono/app.ts"],"sourcesContent":["import { Hono } from 'hono';\nimport type { Context } from 'hono';\nimport { cors } from 'hono/cors';\nimport { createMiddleware } from 'hono/factory';\nimport { bodyLimit } from 'hono/body-limit';\nimport { getConnInfo } from '@hono/node-server/conninfo';\n\nimport { resolveGatewayEffectiveHost } from '../../config/gateway-bind.js';\nimport { createLogger } from '../../utils/logger.js';\nimport type { GatewayService } from '../service.js';\nimport { resolveAllowedBrowserOrigins, resolveGatewayServiceListenPort } from '../host.js';\nimport { loadTunnelState } from '../../tunnel/tunnel-state.js';\nimport { WORK_ITEM_ATTACHMENT_UPLOAD_BODY_MAX_BYTES } from '../../work-items/index.js';\nimport { maxWebchatAgentRequestBodyBytes } from '../chat-limits.js';\nimport { buildGatewayConsoleCspHeader } from '../security/csp.js';\nimport { checkBrowserOrigin } from '../security/origin-check.js';\nimport { isLoopbackIpAddress, isTrustedProxyAddress } from '../client-ip.js';\nimport { resolveReverseProxyPublicUrl } from '../public-url.js';\nimport { auth } from './middleware/auth.js';\nimport { operatorScopes } from './middleware/scopes.js';\nimport {\n createChannelRateLimitMiddleware,\n createStrictRateLimitMiddleware,\n} from './middleware/strict-rate-limit.js';\nimport { logContextMiddleware } from './middleware/log-context.js';\nimport { logger } from './middleware/logger.js';\nimport { routeErrorMiddleware } from './middleware/route-errors.js';\nimport { registerPublicExtensionAssetRoutes } from './routes/auth-registry-extensions.js';\nimport { registerAuthenticatedRoutes } from './routes/index.js';\nimport { registerPublicGatewayRoutes } from './routes/public-gateway.js';\nimport { resetLazyRouteBundlesForTests } from './routes/lazy-fallback.js';\nimport { prewarmStaticUiCache } from './lib/static-ui.js';\nimport { registerSiteShareMiddleware } from '../../share/site-share-router.js';\nconst log = createLogger('Gateway:App');\n\nexport interface HonoAppConfig {\n service: GatewayService;\n}\n\n/**\n * Extension sandbox HTML under `/api/extensions/:id/assets/*` ships its own CSP\n * (`frame-ancestors 'self'`). The global gateway middleware must not overwrite it\n * with `frame-ancestors 'none'` / `X-Frame-Options: DENY`, or the console cannot embed iframes.\n */\nexport function isExtensionGatewayUiAssetPath(path: string): boolean {\n return /^\\/api\\/extensions\\/[^/]+\\/assets\\//.test(path);\n}\n\nexport function createHonoApp(config: HonoAppConfig): Hono {\n if (process.env.VITEST) {\n resetLazyRouteBundlesForTests();\n }\n const { service } = config;\n const app = new Hono();\n\n const gatewayPort = resolveGatewayServiceListenPort(service);\n\n const resolveBrowserOrigins = (): string[] =>\n resolveAllowedBrowserOrigins({\n configuredOrigins: service.currentConfig.gateway.corsOrigins,\n port: gatewayPort,\n bindHost: resolveGatewayEffectiveHost(service.currentConfig),\n tunnelPublicUrl: loadTunnelState()?.publicUrl,\n reverseProxyPublicUrl: resolveReverseProxyPublicUrl(service.currentConfig),\n });\n\n /**\n * TCP source for the in-flight request, normalized for trusted-proxy checks.\n * Returns undefined when the runtime doesn't expose conninfo (tests, mocks).\n */\n const resolveRequestRemoteAddress = (c: Context): string | undefined => {\n try {\n return getConnInfo(c).remote.address;\n } catch {\n return undefined;\n }\n };\n\n /**\n * A request's TCP source qualifies as a \"trusted proxy hop\" when it's\n * loopback (the user's own machine, where any reverse proxy lives) or\n * listed in `gateway.trustedProxies`. We use this signal to safely\n * auto-allow same-host Origins through CSRF without requiring a manual\n * `corsOrigins` entry for every reverse-proxy hostname.\n */\n const isRequestFromTrustedProxy = (c: Context): boolean => {\n const remote = resolveRequestRemoteAddress(c);\n if (!remote) return false;\n if (isLoopbackIpAddress(remote)) return true;\n return isTrustedProxyAddress(remote, service.currentConfig.gateway?.trustedProxies);\n };\n\n app.use(logContextMiddleware());\n app.use(logger({\n trustedProxies: service.currentConfig.gateway?.trustedProxies,\n allowRealIpFallback: service.currentConfig.gateway?.allowRealIpFallback === true,\n }));\n\n // Site-share middleware runs BEFORE CORS/CSP — it owns the request when the\n // Host header matches `*.<publicHostSuffix>` (default `*.share.xopc.ai`) or\n // the path starts with `/site/:token/`. Otherwise it falls through.\n registerSiteShareMiddleware(app, service);\n app.use(\n cors({\n origin: (origin) => {\n const allowed = resolveBrowserOrigins();\n if (!origin) {\n return allowed[0] ?? `http://127.0.0.1:${gatewayPort}`;\n }\n const normalized = origin.toLowerCase();\n const hit = allowed.find((entry) => entry.toLowerCase() === normalized);\n if (hit) return origin;\n return allowed.includes('*') ? '*' : '';\n },\n allowMethods: ['GET', 'POST', 'PATCH', 'DELETE', 'OPTIONS'],\n allowHeaders: ['Content-Type', 'Authorization', 'Accept', 'X-Session-Id', 'Last-Event-ID'],\n credentials: true,\n maxAge: 86400,\n }),\n );\n\n // Build CSP header once at startup (no inline script hashes needed for SPA)\n const gatewayConsoleCsp = buildGatewayConsoleCspHeader();\n\n // Security headers middleware\n app.use(createMiddleware(async (c, next) => {\n await next();\n if (isExtensionGatewayUiAssetPath(c.req.path)) {\n return;\n }\n c.header('X-Frame-Options', 'DENY');\n c.header('X-Content-Type-Options', 'nosniff');\n c.header('Referrer-Policy', 'strict-origin-when-cross-origin');\n c.header('X-XSS-Protection', '1; mode=block');\n // microphone=(self): allow same-origin chat voice (composer). microphone=() breaks packaged Electron loading the gateway SPA.\n c.header('Permissions-Policy', 'camera=(), microphone=(self), geolocation=()');\n c.header('Content-Security-Policy', gatewayConsoleCsp);\n }));\n\n // Browser Origin check middleware for API routes (CSRF protection).\n // Non-browser requests (no Origin header) pass through — they are\n // authenticated by the token middleware instead.\n const allowHostHeaderOriginFallback =\n service.currentConfig.gateway?.dangerouslyAllowHostHeaderOriginFallback === true;\n app.use('/api/*', createMiddleware(async (c, next) => {\n // Sandboxed extension iframes (no allow-same-origin) send `Origin: null`.\n // `checkBrowserOrigin` rejects that; these routes rely on CSP instead\n // (`registerPublicExtensionAssetRoutes`).\n if (isExtensionGatewayUiAssetPath(c.req.path)) {\n return next();\n }\n\n const origin = c.req.header('origin');\n if (!origin || origin.trim().toLowerCase() === 'null') {\n // Native apps / opaque origins — authenticated via Bearer token\n return next();\n }\n\n const result = checkBrowserOrigin({\n requestHost: c.req.header('host'),\n origin,\n allowedOrigins: resolveBrowserOrigins(),\n allowHostHeaderOriginFallback,\n autoAllowSameHostFromTrustedProxy: isRequestFromTrustedProxy(c),\n isLocalClient: false,\n });\n\n if (!result.ok) {\n log.warn(\n {\n origin,\n requestHost: c.req.header('host'),\n reason: 'reason' in result ? result.reason : 'unknown',\n path: c.req.path,\n method: c.req.method,\n },\n `Browser origin check failed: ${origin} not in allowed list`,\n );\n return c.json({ error: 'Forbidden', message: 'Origin not allowed' }, 403);\n }\n\n return next();\n }));\n\n const DEFAULT_API_BODY_MAX = 1 * 1024 * 1024;\n const SKILL_UPLOAD_BODY_MAX = 10 * 1024 * 1024;\n const NOTE_MEDIA_BODY_MAX = 25 * 1024 * 1024;\n const VOICE_TRANSCRIBE_BODY_MAX = 35 * 1024 * 1024;\n const WEBCHAT_AGENT_BODY_MAX = maxWebchatAgentRequestBodyBytes();\n\n const isNoteMediaUploadRequest = (path: string, method: string, contentType: string | undefined): boolean => {\n if (method !== 'POST') return false;\n if (/^\\/api\\/notes\\/[^/]+\\/media$/.test(path)) return true;\n return path === '/api/notes' && contentType?.includes('multipart/form-data') === true;\n };\n\n const isWorkItemAttachmentUploadRequest = (path: string, method: string, contentType: string | undefined): boolean => {\n if (method !== 'POST' || contentType?.includes('multipart/form-data') !== true) return false;\n return /^\\/api\\/work-items\\/[^/]+\\/attachments$/.test(path)\n || /^\\/api\\/projects\\/[^/]+\\/work-items$/.test(path);\n };\n\n app.use('/api/skills/upload', bodyLimit({\n maxSize: SKILL_UPLOAD_BODY_MAX,\n onError: (c) => {\n log.warn({ path: c.req.path, maxSizeMb: 10 }, 'Request body too large: skills upload exceeds 10MB limit');\n return c.json({ error: 'Skill package too large', maxSize: '10MB' }, 413);\n },\n }));\n\n app.use('/api/*', async (c, next) => {\n const contentType = c.req.header('content-type');\n const maxSize = c.req.path === '/api/agent'\n ? WEBCHAT_AGENT_BODY_MAX\n : c.req.path === '/api/skills/upload'\n ? SKILL_UPLOAD_BODY_MAX\n : c.req.path === '/api/voice/transcribe'\n ? VOICE_TRANSCRIBE_BODY_MAX\n : isNoteMediaUploadRequest(c.req.path, c.req.method, contentType)\n ? NOTE_MEDIA_BODY_MAX\n : isWorkItemAttachmentUploadRequest(c.req.path, c.req.method, contentType)\n ? WORK_ITEM_ATTACHMENT_UPLOAD_BODY_MAX_BYTES\n : DEFAULT_API_BODY_MAX;\n const maxSizeMb = Math.ceil(maxSize / (1024 * 1024));\n return bodyLimit({\n maxSize,\n onError: (ctx) => {\n log.warn({ path: ctx.req.path, maxSizeMb }, `Request body too large: exceeds ${maxSizeMb}MB limit`);\n return ctx.json({ error: 'Request body too large', maxSize: `${maxSizeMb}MB` }, 413);\n },\n })(c, next);\n });\n\n registerPublicGatewayRoutes(app, service);\n\n // Extension UI assets are served without auth: sandboxed iframes (no allow-same-origin)\n // have an opaque origin of `null` and cannot forward the ?token= from the parent HTML URL.\n // Security is enforced by the strict CSP (frame-ancestors 'self') on every response.\n registerPublicExtensionAssetRoutes(app, service);\n\n const authenticated = new Hono();\n authenticated.use(routeErrorMiddleware());\n authenticated.use(\n auth({\n getGatewayAuth: () => service.currentConfig.gateway?.auth,\n getResolvedAuth: () => service.getResolvedAuth(),\n getTrustedProxyContext: () => ({\n trustedProxies: service.currentConfig.gateway?.trustedProxies,\n allowRealIpFallback: service.currentConfig.gateway?.allowRealIpFallback === true,\n }),\n }),\n );\n authenticated.use(operatorScopes());\n\n const strictRateLimitMiddleware = createStrictRateLimitMiddleware({\n getTrustedProxyContext: () => ({\n trustedProxies: service.currentConfig.gateway?.trustedProxies,\n allowRealIpFallback: service.currentConfig.gateway?.allowRealIpFallback === true,\n }),\n });\n const channelRateLimitMiddleware = createChannelRateLimitMiddleware({\n getTrustedProxyContext: () => ({\n trustedProxies: service.currentConfig.gateway?.trustedProxies,\n allowRealIpFallback: service.currentConfig.gateway?.allowRealIpFallback === true,\n }),\n });\n\n const sseConfig = {\n service,\n maxSseConnections: service.currentConfig.gateway.maxSseConnections,\n };\n\n registerAuthenticatedRoutes(app, authenticated, {\n service,\n strictRateLimitMiddleware,\n channelRateLimitMiddleware,\n sseConfig,\n });\n\n const prewarm = prewarmStaticUiCache();\n if (prewarm.loaded > 0) {\n log.debug({ loaded: prewarm.loaded, missing: prewarm.missing }, 'Static UI cache prewarmed');\n }\n\n app.route('/', authenticated);\n\n app.notFound((c) => {\n const isApiRoute = c.req.path.startsWith('/api/');\n const fields = { path: c.req.path, method: c.req.method };\n if (isApiRoute) {\n log.warn(fields, 'Route not found');\n } else {\n log.debug(fields, 'Route not found');\n }\n return c.json({ error: 'Not found' }, 404);\n });\n\n app.onError((err, c) => {\n log.error(\n {\n err,\n phase: 'gateway.http.unhandled',\n path: c.req.path,\n method: c.req.method,\n userAgent: c.req.header('user-agent'),\n },\n `Hono error on ${c.req.method} ${c.req.path}: ${err instanceof Error ? err.message : String(err)}`,\n );\n return c.json({ error: 'Internal server error' }, 500);\n });\n\n return app;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"app.js","names":[],"sources":["../../../../src/gateway/hono/app.ts"],"sourcesContent":["import { Hono } from 'hono';\nimport type { Context } from 'hono';\nimport { cors } from 'hono/cors';\nimport { createMiddleware } from 'hono/factory';\nimport { bodyLimit } from 'hono/body-limit';\nimport { getConnInfo } from '@hono/node-server/conninfo';\n\nimport { resolveGatewayEffectiveHost } from '../../config/gateway-bind.js';\nimport { createLogger } from '../../utils/logger.js';\nimport type { GatewayService } from '../service.js';\nimport { resolveAllowedBrowserOrigins, resolveGatewayServiceListenPort } from '../host.js';\nimport { loadTunnelState } from '../../tunnel/tunnel-state.js';\nimport { WORK_ITEM_ATTACHMENT_UPLOAD_BODY_MAX_BYTES } from '../../work-items/index.js';\nimport { maxWebchatAgentRequestBodyBytes } from '../chat-limits.js';\nimport { buildGatewayConsoleCspHeader } from '../security/csp.js';\nimport { checkBrowserOrigin } from '../security/origin-check.js';\nimport { isLoopbackIpAddress, isTrustedProxyAddress } from '../client-ip.js';\nimport { resolveReverseProxyPublicUrl } from '../public-url.js';\nimport { auth } from './middleware/auth.js';\nimport { operatorScopes } from './middleware/scopes.js';\nimport {\n createChannelRateLimitMiddleware,\n createStrictRateLimitMiddleware,\n} from './middleware/strict-rate-limit.js';\nimport { logContextMiddleware } from './middleware/log-context.js';\nimport { logger } from './middleware/logger.js';\nimport { routeErrorMiddleware } from './middleware/route-errors.js';\nimport { registerPublicExtensionAssetRoutes } from './routes/auth-registry-extensions.js';\nimport { registerAuthenticatedRoutes } from './routes/index.js';\nimport { registerPublicGatewayRoutes } from './routes/public-gateway.js';\nimport { resetLazyRouteBundlesForTests } from './routes/lazy-fallback.js';\nimport { prewarmStaticUiCache } from './lib/static-ui.js';\nimport { registerSiteShareMiddleware } from '../../share/site-share-router.js';\nconst log = createLogger('Gateway:App');\n\nexport interface HonoAppConfig {\n service: GatewayService;\n}\n\n/**\n * Extension sandbox HTML under `/api/extensions/:id/assets/*` ships its own CSP\n * (`frame-ancestors 'self'`). The global gateway middleware must not overwrite it\n * with `frame-ancestors 'none'` / `X-Frame-Options: DENY`, or the console cannot embed iframes.\n */\nexport function isExtensionGatewayUiAssetPath(path: string): boolean {\n return /^\\/api\\/extensions\\/[^/]+\\/assets\\//.test(path);\n}\n\nexport function createHonoApp(config: HonoAppConfig): Hono {\n if (process.env.VITEST) {\n resetLazyRouteBundlesForTests();\n }\n const { service } = config;\n const app = new Hono();\n\n const gatewayPort = resolveGatewayServiceListenPort(service);\n\n const resolveBrowserOrigins = (): string[] =>\n resolveAllowedBrowserOrigins({\n configuredOrigins: service.currentConfig.gateway.corsOrigins,\n port: gatewayPort,\n bindHost: resolveGatewayEffectiveHost(service.currentConfig),\n tunnelPublicUrl: loadTunnelState()?.publicUrl,\n reverseProxyPublicUrl: resolveReverseProxyPublicUrl(service.currentConfig),\n });\n\n /**\n * TCP source for the in-flight request, normalized for trusted-proxy checks.\n * Returns undefined when the runtime doesn't expose conninfo (tests, mocks).\n */\n const resolveRequestRemoteAddress = (c: Context): string | undefined => {\n try {\n return getConnInfo(c).remote.address;\n } catch {\n return undefined;\n }\n };\n\n /**\n * A request's TCP source qualifies as a \"trusted proxy hop\" when it's\n * loopback (the user's own machine, where any reverse proxy lives) or\n * listed in `gateway.trustedProxies`. We use this signal to safely\n * auto-allow same-host Origins through CSRF without requiring a manual\n * `corsOrigins` entry for every reverse-proxy hostname.\n */\n const isRequestFromTrustedProxy = (c: Context): boolean => {\n const remote = resolveRequestRemoteAddress(c);\n if (!remote) return false;\n if (isLoopbackIpAddress(remote)) return true;\n return isTrustedProxyAddress(remote, service.currentConfig.gateway?.trustedProxies);\n };\n\n app.use(logContextMiddleware());\n app.use(logger({\n trustedProxies: service.currentConfig.gateway?.trustedProxies,\n allowRealIpFallback: service.currentConfig.gateway?.allowRealIpFallback === true,\n }));\n\n // Site-share middleware runs BEFORE CORS/CSP — it owns the request when the\n // Host header matches `*.<publicHostSuffix>` (default `*.share.xopc.ai`) or\n // the path starts with `/site/:token/`. Otherwise it falls through.\n registerSiteShareMiddleware(app, service);\n app.use(\n cors({\n origin: (origin) => {\n const allowed = resolveBrowserOrigins();\n if (!origin) {\n return allowed[0] ?? `http://127.0.0.1:${gatewayPort}`;\n }\n const normalized = origin.toLowerCase();\n const hit = allowed.find((entry) => entry.toLowerCase() === normalized);\n if (hit) return origin;\n return allowed.includes('*') ? '*' : '';\n },\n allowMethods: ['GET', 'POST', 'PATCH', 'DELETE', 'OPTIONS'],\n allowHeaders: ['Content-Type', 'Authorization', 'Accept', 'X-Session-Id', 'Last-Event-ID'],\n credentials: true,\n maxAge: 86400,\n }),\n );\n\n // Build CSP header once at startup (no inline script hashes needed for SPA)\n const gatewayConsoleCsp = buildGatewayConsoleCspHeader();\n\n // Security headers middleware\n app.use(createMiddleware(async (c, next) => {\n await next();\n if (isExtensionGatewayUiAssetPath(c.req.path)) {\n return;\n }\n c.header('X-Frame-Options', 'DENY');\n c.header('X-Content-Type-Options', 'nosniff');\n c.header('Referrer-Policy', 'strict-origin-when-cross-origin');\n c.header('X-XSS-Protection', '1; mode=block');\n // microphone=(self): allow same-origin chat voice (composer). microphone=() breaks packaged Electron loading the gateway SPA.\n c.header('Permissions-Policy', 'camera=(), microphone=(self), geolocation=()');\n c.header('Content-Security-Policy', gatewayConsoleCsp);\n }));\n\n // Browser Origin check middleware for API routes (CSRF protection).\n // Non-browser requests (no Origin header) pass through — they are\n // authenticated by the token middleware instead.\n const allowHostHeaderOriginFallback =\n service.currentConfig.gateway?.dangerouslyAllowHostHeaderOriginFallback === true;\n app.use('/api/*', createMiddleware(async (c, next) => {\n // Sandboxed extension iframes (no allow-same-origin) send `Origin: null`.\n // `checkBrowserOrigin` rejects that; these routes rely on CSP instead\n // (`registerPublicExtensionAssetRoutes`).\n if (isExtensionGatewayUiAssetPath(c.req.path)) {\n return next();\n }\n\n const origin = c.req.header('origin');\n if (!origin || origin.trim().toLowerCase() === 'null') {\n // Native apps / opaque origins — authenticated via Bearer token\n return next();\n }\n\n const result = checkBrowserOrigin({\n requestHost: c.req.header('host'),\n origin,\n allowedOrigins: resolveBrowserOrigins(),\n allowHostHeaderOriginFallback,\n autoAllowSameHostFromTrustedProxy: isRequestFromTrustedProxy(c),\n isLocalClient: false,\n });\n\n if (!result.ok) {\n log.warn(\n {\n origin,\n requestHost: c.req.header('host'),\n reason: 'reason' in result ? result.reason : 'unknown',\n path: c.req.path,\n method: c.req.method,\n },\n `Browser origin check failed: ${origin} not in allowed list`,\n );\n return c.json({ error: 'Forbidden', message: 'Origin not allowed' }, 403);\n }\n\n return next();\n }));\n\n const DEFAULT_API_BODY_MAX = 1 * 1024 * 1024;\n const SKILL_UPLOAD_BODY_MAX = 10 * 1024 * 1024;\n const NOTE_MEDIA_BODY_MAX = 25 * 1024 * 1024;\n const VOICE_TRANSCRIBE_BODY_MAX = 35 * 1024 * 1024;\n const WEBCHAT_AGENT_BODY_MAX = maxWebchatAgentRequestBodyBytes();\n\n const isNoteMediaUploadRequest = (path: string, method: string, contentType: string | undefined): boolean => {\n if (method !== 'POST') return false;\n if (/^\\/api\\/notes\\/[^/]+\\/media$/.test(path)) return true;\n return path === '/api/notes' && contentType?.includes('multipart/form-data') === true;\n };\n\n const isWorkItemAttachmentUploadRequest = (path: string, method: string, contentType: string | undefined): boolean => {\n if (method !== 'POST' || contentType?.includes('multipart/form-data') !== true) return false;\n return /^\\/api\\/work-items\\/[^/]+\\/attachments$/.test(path)\n || /^\\/api\\/projects\\/[^/]+\\/work-items$/.test(path);\n };\n\n app.use('/api/skills/upload', bodyLimit({\n maxSize: SKILL_UPLOAD_BODY_MAX,\n onError: (c) => {\n log.warn({ path: c.req.path, maxSizeMb: 10 }, 'Request body too large: skills upload exceeds 10MB limit');\n return c.json({ error: 'Skill package too large', maxSize: '10MB' }, 413);\n },\n }));\n\n app.use('/api/*', async (c, next) => {\n const contentType = c.req.header('content-type');\n const maxSize = c.req.path === '/api/agent'\n ? WEBCHAT_AGENT_BODY_MAX\n : c.req.path === '/api/skills/upload'\n ? SKILL_UPLOAD_BODY_MAX\n : c.req.path === '/api/voice/transcribe' || c.req.path === '/api/voice/transcriptions'\n ? VOICE_TRANSCRIBE_BODY_MAX\n : isNoteMediaUploadRequest(c.req.path, c.req.method, contentType)\n ? NOTE_MEDIA_BODY_MAX\n : isWorkItemAttachmentUploadRequest(c.req.path, c.req.method, contentType)\n ? WORK_ITEM_ATTACHMENT_UPLOAD_BODY_MAX_BYTES\n : DEFAULT_API_BODY_MAX;\n const maxSizeMb = Math.ceil(maxSize / (1024 * 1024));\n return bodyLimit({\n maxSize,\n onError: (ctx) => {\n log.warn({ path: ctx.req.path, maxSizeMb }, `Request body too large: exceeds ${maxSizeMb}MB limit`);\n return ctx.json({ error: 'Request body too large', maxSize: `${maxSizeMb}MB` }, 413);\n },\n })(c, next);\n });\n\n registerPublicGatewayRoutes(app, service);\n\n // Extension UI assets are served without auth: sandboxed iframes (no allow-same-origin)\n // have an opaque origin of `null` and cannot forward the ?token= from the parent HTML URL.\n // Security is enforced by the strict CSP (frame-ancestors 'self') on every response.\n registerPublicExtensionAssetRoutes(app, service);\n\n const authenticated = new Hono();\n authenticated.use(routeErrorMiddleware());\n authenticated.use(\n auth({\n getGatewayAuth: () => service.currentConfig.gateway?.auth,\n getResolvedAuth: () => service.getResolvedAuth(),\n getTrustedProxyContext: () => ({\n trustedProxies: service.currentConfig.gateway?.trustedProxies,\n allowRealIpFallback: service.currentConfig.gateway?.allowRealIpFallback === true,\n }),\n }),\n );\n authenticated.use(operatorScopes());\n\n const strictRateLimitMiddleware = createStrictRateLimitMiddleware({\n getTrustedProxyContext: () => ({\n trustedProxies: service.currentConfig.gateway?.trustedProxies,\n allowRealIpFallback: service.currentConfig.gateway?.allowRealIpFallback === true,\n }),\n });\n const channelRateLimitMiddleware = createChannelRateLimitMiddleware({\n getTrustedProxyContext: () => ({\n trustedProxies: service.currentConfig.gateway?.trustedProxies,\n allowRealIpFallback: service.currentConfig.gateway?.allowRealIpFallback === true,\n }),\n });\n\n const sseConfig = {\n service,\n maxSseConnections: service.currentConfig.gateway.maxSseConnections,\n };\n\n registerAuthenticatedRoutes(app, authenticated, {\n service,\n strictRateLimitMiddleware,\n channelRateLimitMiddleware,\n sseConfig,\n });\n\n const prewarm = prewarmStaticUiCache();\n if (prewarm.loaded > 0) {\n log.debug({ loaded: prewarm.loaded, missing: prewarm.missing }, 'Static UI cache prewarmed');\n }\n\n app.route('/', authenticated);\n\n app.notFound((c) => {\n const isApiRoute = c.req.path.startsWith('/api/');\n const fields = { path: c.req.path, method: c.req.method };\n if (isApiRoute) {\n log.warn(fields, 'Route not found');\n } else {\n log.debug(fields, 'Route not found');\n }\n return c.json({ error: 'Not found' }, 404);\n });\n\n app.onError((err, c) => {\n log.error(\n {\n err,\n phase: 'gateway.http.unhandled',\n path: c.req.path,\n method: c.req.method,\n userAgent: c.req.header('user-agent'),\n },\n `Hono error on ${c.req.method} ${c.req.path}: ${err instanceof Error ? err.message : String(err)}`,\n );\n return c.json({ error: 'Internal server error' }, 500);\n });\n\n return app;\n}\n"],"mappings":"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"}
|
|
@@ -465,6 +465,17 @@ export declare function buildSafeWebConfigPayload(service: GatewayService, optio
|
|
|
465
465
|
};
|
|
466
466
|
stt: unknown;
|
|
467
467
|
tts: unknown;
|
|
468
|
+
voice: {
|
|
469
|
+
languageMode?: "auto" | "manual";
|
|
470
|
+
language?: "en" | "zh";
|
|
471
|
+
input?: {
|
|
472
|
+
refinement?: {
|
|
473
|
+
mode: "off" | "custom" | "light" | "punctuation";
|
|
474
|
+
model?: string;
|
|
475
|
+
customInstruction?: string;
|
|
476
|
+
};
|
|
477
|
+
};
|
|
478
|
+
};
|
|
468
479
|
tools: {
|
|
469
480
|
web: {
|
|
470
481
|
region: "cn" | "global" | null;
|
|
@@ -191,6 +191,7 @@ async function buildSafeWebConfigPayload(service, options = {}) {
|
|
|
191
191
|
update: { ...resolveUpdateConfigForWeb(config) },
|
|
192
192
|
stt: maskSttConfigForWeb(config.tools?.media?.audio),
|
|
193
193
|
tts: maskTtsConfigForWeb(config.messages?.tts),
|
|
194
|
+
voice: config.voice,
|
|
194
195
|
tools: safeToolsWebForGet(config),
|
|
195
196
|
bindings: Array.isArray(config.bindings) ? config.bindings : [],
|
|
196
197
|
mcp: buildSafeMcpConfigForWeb(config)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config-payload.js","names":["getModelsJsonPath"],"sources":["../../../../../src/gateway/hono/lib/config-payload.ts"],"sourcesContent":["import {\n listAgentEntries,\n normalizeAgentId,\n resolveDefaultAgentId,\n} from '../../../agent/agent-scope.js';\nimport {\n buildChannelCatalogForConfig,\n buildChannelCatalogFromSnapshot,\n getChannelSetupStatus,\n} from '../../../channels/catalog/channel-catalog-service.js';\nimport { normalizeConfiguredMcpServers } from '../../../config/mcp-config-normalize.js';\nimport type { Config } from '../../../config/schema.js';\nimport {\n GENERIC_MASKED_SECRET,\n maskSecretLength,\n} from './mask-secret-length.js';\nimport { maskTunnelSecretForWeb } from '../../../tunnel/env.js';\nimport { resolveShareConfig } from '../../../share/share-config.js';\nimport {\n resolveGoalsConfigForWeb,\n resolveSessionConfigForWeb,\n resolveUpdateConfigForWeb,\n} from '../../../config/web-patch.js';\nimport { CredentialResolver } from '../../../auth/credentials.js';\nimport { loadModelsJson, getModelsJsonPath } from '../../../config/models-json.js';\nimport { getAllProviders, isProviderConfigured } from '../../../providers/index.js';\nimport { getProviderRegistry } from '../../../providers/plugin-registry.js';\nimport type { GatewayService } from '../../service.js';\nimport { safeToolsWebForGet } from '../../config-tools-web.js';\nimport { buildSafeProvidersConfigForWeb } from './safe-providers-config.js';\nimport { maskSttConfigForWeb, maskTtsConfigForWeb } from './safe-voice-config.js';\n\nfunction readModelsJsonProviderApiKey(providerId: string): string | undefined {\n const { config } = loadModelsJson(getModelsJsonPath());\n const entry = config.providers?.[providerId];\n const key = entry?.apiKey;\n return typeof key === 'string' && key.trim() ? key.trim() : undefined;\n}\n\n/** Length-preserving mask for LLM provider keys in GET `/api/config`. */\nasync function maskLlmProviderApiKeyForWeb(provider: string): Promise<string> {\n const resolver = new CredentialResolver();\n const stored = await resolver.revealGatewayStoredApiKey(provider);\n if (stored) return maskSecretLength(stored);\n\n const fromModelsJson = readModelsJsonProviderApiKey(provider);\n if (fromModelsJson) return maskSecretLength(fromModelsJson);\n\n // Extension plugins manage their own auth; don't show a fake gateway key mask.\n if (getProviderRegistry().has(provider)) return '';\n\n if (await isProviderConfigured(provider)) return GENERIC_MASKED_SECRET;\n return '';\n}\n\n/** MCP block for GET/PATCH `/api/config` (authenticated console editing). */\nexport function buildSafeMcpConfigForWeb(config: Config) {\n const mcp = config.mcp;\n if (!mcp) {\n return { servers: {} as Record<string, Record<string, unknown>> };\n }\n return {\n ...(mcp.sessionIdleTtlMs !== undefined ? { sessionIdleTtlMs: mcp.sessionIdleTtlMs } : {}),\n servers: normalizeConfiguredMcpServers(mcp.servers),\n };\n}\n\nfunction maskBrowserCloudConfigForWeb(cloud: unknown): Record<string, unknown> | null {\n if (!cloud || typeof cloud !== 'object' || Array.isArray(cloud)) {\n return null;\n }\n const raw = cloud as Record<string, unknown>;\n const safe: Record<string, unknown> = {};\n if (typeof raw.apiKey === 'string' && raw.apiKey.trim()) {\n safe.apiKey = maskSecretLength(raw.apiKey);\n }\n if (typeof raw.projectId === 'string' && raw.projectId.trim()) {\n safe.projectId = raw.projectId.trim();\n }\n if (typeof raw.region === 'string' && raw.region.trim()) {\n safe.region = raw.region.trim();\n }\n return Object.keys(safe).length > 0 ? safe : null;\n}\n\nexport function buildSafeBrowserConfigForWeb(browser: unknown) {\n if (!browser || typeof browser !== 'object') {\n return {\n enabled: false,\n headless: false,\n allowPrivateUrls: false,\n commandTimeout: null,\n backend: null,\n cloudProvider: null,\n cloud: null,\n cdpUrl: null,\n extension: null,\n cloakbrowser: null,\n humanize: null,\n humanPreset: null,\n dialogPolicy: null,\n dialogTimeoutSeconds: null,\n };\n }\n\n return {\n enabled: (browser as Record<string, unknown>).enabled !== false,\n headless: (browser as Record<string, unknown>).headless === true,\n allowPrivateUrls: (browser as Record<string, unknown>).allowPrivateUrls === true,\n commandTimeout:\n typeof (browser as Record<string, unknown>).commandTimeout === 'number' &&\n Number.isFinite((browser as Record<string, unknown>).commandTimeout as number)\n ? Math.floor((browser as Record<string, unknown>).commandTimeout as number)\n : null,\n backend:\n (browser as Record<string, unknown>).backend === 'local' ||\n (browser as Record<string, unknown>).backend === 'cdp' ||\n (browser as Record<string, unknown>).backend === 'cloud' ||\n (browser as Record<string, unknown>).backend === 'extension' ||\n (browser as Record<string, unknown>).backend === 'cloakbrowser'\n ? (browser as Record<string, unknown>).backend\n : null,\n cloudProvider:\n (browser as Record<string, unknown>).cloudProvider === 'browserbase' ||\n (browser as Record<string, unknown>).cloudProvider === 'browser-use'\n ? (browser as Record<string, unknown>).cloudProvider\n : null,\n cloud: maskBrowserCloudConfigForWeb((browser as Record<string, unknown>).cloud),\n cdpUrl:\n typeof (browser as Record<string, unknown>).cdpUrl === 'string' &&\n ((browser as Record<string, unknown>).cdpUrl as string).trim()\n ? ((browser as Record<string, unknown>).cdpUrl as string).trim()\n : null,\n extension:\n (browser as Record<string, unknown>).extension &&\n typeof (browser as Record<string, unknown>).extension === 'object' &&\n !Array.isArray((browser as Record<string, unknown>).extension)\n ? (browser as Record<string, unknown>).extension\n : null,\n cloakbrowser:\n (browser as Record<string, unknown>).cloakbrowser &&\n typeof (browser as Record<string, unknown>).cloakbrowser === 'object' &&\n !Array.isArray((browser as Record<string, unknown>).cloakbrowser)\n ? (browser as Record<string, unknown>).cloakbrowser\n : null,\n humanize:\n typeof (browser as Record<string, unknown>).humanize === 'boolean'\n ? (browser as Record<string, unknown>).humanize\n : null,\n humanPreset:\n (browser as Record<string, unknown>).humanPreset === 'default' ||\n (browser as Record<string, unknown>).humanPreset === 'careful'\n ? (browser as Record<string, unknown>).humanPreset\n : null,\n dialogPolicy:\n (browser as Record<string, unknown>).dialogPolicy === 'must_respond' ||\n (browser as Record<string, unknown>).dialogPolicy === 'auto_accept' ||\n (browser as Record<string, unknown>).dialogPolicy === 'auto_dismiss'\n ? (browser as Record<string, unknown>).dialogPolicy\n : null,\n dialogTimeoutSeconds:\n typeof (browser as Record<string, unknown>).dialogTimeoutSeconds === 'number' &&\n Number.isFinite((browser as Record<string, unknown>).dialogTimeoutSeconds as number)\n ? Math.floor((browser as Record<string, unknown>).dialogTimeoutSeconds as number)\n : null,\n };\n}\n\n/** Sanitized config snapshot for GET/PATCH `/api/config` (matches persisted `service.currentConfig`). */\nexport async function buildSafeWebConfigPayload(service: GatewayService, options: { locale?: string } = {}) {\n const config = service.currentConfig;\n const extensionLoader =\n typeof service.getExtensionLoader === 'function' ? service.getExtensionLoader() : undefined;\n const snapshot = extensionLoader?.getManifestSnapshot();\n const catalog = snapshot\n ? buildChannelCatalogFromSnapshot(snapshot, { locale: options.locale })\n : buildChannelCatalogForConfig(config, { locale: options.locale });\n const channelsPayload = Object.fromEntries(\n catalog.entries.map((entry) => {\n const plugin =\n typeof service.getChannelRuntimePlugin === 'function'\n ? service.getChannelRuntimePlugin(entry.id)\n : undefined;\n const channelCfgKey = entry.configPath.startsWith('channels.')\n ? entry.configPath.slice('channels.'.length).split('.')[0] || entry.id\n : entry.id;\n const channelCfg = config.channels?.[channelCfgKey] as Record<string, unknown> | undefined;\n const setupStatus = getChannelSetupStatus(config, entry.id, entry);\n return [\n entry.id,\n {\n enabled: setupStatus.enabled,\n configured: setupStatus.ready,\n setupStatus,\n config: plugin?.configSurface?.buildConfigSurface(config) ?? channelCfg ?? {},\n schema: entry.configSchema,\n uiHints: entry.uiHints,\n },\n ];\n }),\n );\n return {\n agents: {\n defaultId: resolveDefaultAgentId(config),\n list: listAgentEntries(config)\n .filter((e) => e.enabled !== false)\n .map((e) => ({\n id: normalizeAgentId(e.id),\n identity: e.identity,\n workspace: e.workspace,\n models: e.models,\n tools: e.tools,\n skills: e.skills,\n memory: e.memory,\n workflows: e.workflows,\n boundaries: e.boundaries,\n })),\n capabilityPresets: config.agents?.capabilityPresets ?? {},\n },\n channels: channelsPayload,\n providers: Object.fromEntries(\n await Promise.all(\n getAllProviders().map(async (provider) => [\n provider,\n await maskLlmProviderApiKeyForWeb(provider),\n ]),\n ),\n ),\n /** Masked `cfg.providers` for capability keys (image / STT / etc.). */\n providersConfig: buildSafeProvidersConfigForWeb(config.providers),\n gateway: {\n bind: config.gateway?.bind ?? 'loopback',\n customBindHost: config.gateway?.customBindHost,\n port: config.gateway?.port,\n corsOrigins: Array.isArray(config.gateway?.corsOrigins) ? config.gateway.corsOrigins : [],\n trustedProxies: Array.isArray(config.gateway?.trustedProxies)\n ? config.gateway.trustedProxies\n : [],\n allowRealIpFallback: config.gateway?.allowRealIpFallback === true,\n dangerouslyAllowHostHeaderOriginFallback:\n config.gateway?.dangerouslyAllowHostHeaderOriginFallback === true,\n security: {\n strict: config.gateway?.security?.strict === true,\n },\n auth: {\n mode: config.gateway?.auth?.mode || 'token',\n token: config.gateway?.auth?.token ? maskSecretLength(config.gateway.auth.token) : '',\n password: config.gateway?.auth?.password\n ? maskSecretLength(config.gateway.auth.password)\n : '',\n trustedProxy: config.gateway?.auth?.trustedProxy\n ? {\n userHeader: config.gateway.auth.trustedProxy.userHeader,\n requiredHeaders: config.gateway.auth.trustedProxy.requiredHeaders ?? [],\n allowUsers: config.gateway.auth.trustedProxy.allowUsers ?? [],\n allowLoopback: config.gateway.auth.trustedProxy.allowLoopback === true,\n }\n : undefined,\n rateLimit: {\n enabled: config.gateway?.auth?.rateLimit?.enabled !== false,\n maxAttempts:\n typeof config.gateway?.auth?.rateLimit?.maxAttempts === 'number'\n ? config.gateway.auth.rateLimit.maxAttempts\n : 5,\n windowMs:\n typeof config.gateway?.auth?.rateLimit?.windowMs === 'number'\n ? config.gateway.auth.rateLimit.windowMs\n : 900_000,\n blockDurationMs:\n typeof config.gateway?.auth?.rateLimit?.blockDurationMs === 'number'\n ? config.gateway.auth.rateLimit.blockDurationMs\n : typeof config.gateway?.auth?.rateLimit?.lockoutMs === 'number'\n ? config.gateway.auth.rateLimit.lockoutMs\n : 300_000,\n exemptLoopback: config.gateway?.auth?.rateLimit?.exemptLoopback !== false,\n },\n },\n heartbeat: {\n enabled: config.gateway?.heartbeat?.enabled,\n intervalMs: config.gateway?.heartbeat?.intervalMs,\n includeSystemPromptSection: config.gateway?.heartbeat?.includeSystemPromptSection === true,\n target: config.gateway?.heartbeat?.target,\n targetChatId: config.gateway?.heartbeat?.targetChatId,\n prompt: config.gateway?.heartbeat?.prompt,\n ackMaxChars: config.gateway?.heartbeat?.ackMaxChars,\n isolatedSession: config.gateway?.heartbeat?.isolatedSession,\n activeHours: config.gateway?.heartbeat?.activeHours,\n },\n maxSseConnections:\n typeof config.gateway?.maxSseConnections === 'number'\n ? config.gateway.maxSseConnections\n : 100,\n channelConnectDeferMode: config.gateway?.channelConnectDeferMode ?? 'auto',\n channelConnectDeferIds: Array.isArray(config.gateway?.channelConnectDeferIds)\n ? config.gateway.channelConnectDeferIds\n : [],\n channelConnectDeferSkipIds: Array.isArray(config.gateway?.channelConnectDeferSkipIds)\n ? config.gateway.channelConnectDeferSkipIds\n : [],\n share: resolveShareConfig(config.gateway?.share),\n publicUrl: config.gateway?.publicUrl ?? null,\n skillsMarketplaceProvider: config.gateway?.skillsMarketplaceProvider ?? 'store',\n skillsStoreBaseUrl: config.gateway?.skillsStoreBaseUrl ?? 'https://store.xopc.ai',\n },\n browser: buildSafeBrowserConfigForWeb(config.browser),\n goals: resolveGoalsConfigForWeb(config),\n session: resolveSessionConfigForWeb(config),\n tui: {\n defaultAgent: config.tui?.defaultAgent ?? 'coder',\n },\n tunnel: {\n enabled: config.tunnel?.enabled === true,\n autoStart: config.tunnel?.autoStart === true,\n brokerUrl: config.tunnel?.brokerUrl ?? 'https://frp.xopc.ai/api',\n registrationSecret: config.tunnel?.registrationSecret\n ? maskTunnelSecretForWeb(config.tunnel.registrationSecret)\n : '',\n consent: config.tunnel?.consent\n ? {\n version: config.tunnel.consent.version,\n acceptedAt: config.tunnel.consent.acceptedAt,\n }\n : undefined,\n transport: { tls: 'broker_terminated' as const },\n },\n update: {\n ...resolveUpdateConfigForWeb(config),\n },\n stt: maskSttConfigForWeb(config.tools?.media?.audio),\n tts: maskTtsConfigForWeb(config.messages?.tts),\n tools: safeToolsWebForGet(config),\n bindings: Array.isArray(config.bindings) ? config.bindings : [],\n mcp: buildSafeMcpConfigForWeb(config),\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"config-payload.js","names":["getModelsJsonPath"],"sources":["../../../../../src/gateway/hono/lib/config-payload.ts"],"sourcesContent":["import {\n listAgentEntries,\n normalizeAgentId,\n resolveDefaultAgentId,\n} from '../../../agent/agent-scope.js';\nimport {\n buildChannelCatalogForConfig,\n buildChannelCatalogFromSnapshot,\n getChannelSetupStatus,\n} from '../../../channels/catalog/channel-catalog-service.js';\nimport { normalizeConfiguredMcpServers } from '../../../config/mcp-config-normalize.js';\nimport type { Config } from '../../../config/schema.js';\nimport {\n GENERIC_MASKED_SECRET,\n maskSecretLength,\n} from './mask-secret-length.js';\nimport { maskTunnelSecretForWeb } from '../../../tunnel/env.js';\nimport { resolveShareConfig } from '../../../share/share-config.js';\nimport {\n resolveGoalsConfigForWeb,\n resolveSessionConfigForWeb,\n resolveUpdateConfigForWeb,\n} from '../../../config/web-patch.js';\nimport { CredentialResolver } from '../../../auth/credentials.js';\nimport { loadModelsJson, getModelsJsonPath } from '../../../config/models-json.js';\nimport { getAllProviders, isProviderConfigured } from '../../../providers/index.js';\nimport { getProviderRegistry } from '../../../providers/plugin-registry.js';\nimport type { GatewayService } from '../../service.js';\nimport { safeToolsWebForGet } from '../../config-tools-web.js';\nimport { buildSafeProvidersConfigForWeb } from './safe-providers-config.js';\nimport { maskSttConfigForWeb, maskTtsConfigForWeb } from './safe-voice-config.js';\n\nfunction readModelsJsonProviderApiKey(providerId: string): string | undefined {\n const { config } = loadModelsJson(getModelsJsonPath());\n const entry = config.providers?.[providerId];\n const key = entry?.apiKey;\n return typeof key === 'string' && key.trim() ? key.trim() : undefined;\n}\n\n/** Length-preserving mask for LLM provider keys in GET `/api/config`. */\nasync function maskLlmProviderApiKeyForWeb(provider: string): Promise<string> {\n const resolver = new CredentialResolver();\n const stored = await resolver.revealGatewayStoredApiKey(provider);\n if (stored) return maskSecretLength(stored);\n\n const fromModelsJson = readModelsJsonProviderApiKey(provider);\n if (fromModelsJson) return maskSecretLength(fromModelsJson);\n\n // Extension plugins manage their own auth; don't show a fake gateway key mask.\n if (getProviderRegistry().has(provider)) return '';\n\n if (await isProviderConfigured(provider)) return GENERIC_MASKED_SECRET;\n return '';\n}\n\n/** MCP block for GET/PATCH `/api/config` (authenticated console editing). */\nexport function buildSafeMcpConfigForWeb(config: Config) {\n const mcp = config.mcp;\n if (!mcp) {\n return { servers: {} as Record<string, Record<string, unknown>> };\n }\n return {\n ...(mcp.sessionIdleTtlMs !== undefined ? { sessionIdleTtlMs: mcp.sessionIdleTtlMs } : {}),\n servers: normalizeConfiguredMcpServers(mcp.servers),\n };\n}\n\nfunction maskBrowserCloudConfigForWeb(cloud: unknown): Record<string, unknown> | null {\n if (!cloud || typeof cloud !== 'object' || Array.isArray(cloud)) {\n return null;\n }\n const raw = cloud as Record<string, unknown>;\n const safe: Record<string, unknown> = {};\n if (typeof raw.apiKey === 'string' && raw.apiKey.trim()) {\n safe.apiKey = maskSecretLength(raw.apiKey);\n }\n if (typeof raw.projectId === 'string' && raw.projectId.trim()) {\n safe.projectId = raw.projectId.trim();\n }\n if (typeof raw.region === 'string' && raw.region.trim()) {\n safe.region = raw.region.trim();\n }\n return Object.keys(safe).length > 0 ? safe : null;\n}\n\nexport function buildSafeBrowserConfigForWeb(browser: unknown) {\n if (!browser || typeof browser !== 'object') {\n return {\n enabled: false,\n headless: false,\n allowPrivateUrls: false,\n commandTimeout: null,\n backend: null,\n cloudProvider: null,\n cloud: null,\n cdpUrl: null,\n extension: null,\n cloakbrowser: null,\n humanize: null,\n humanPreset: null,\n dialogPolicy: null,\n dialogTimeoutSeconds: null,\n };\n }\n\n return {\n enabled: (browser as Record<string, unknown>).enabled !== false,\n headless: (browser as Record<string, unknown>).headless === true,\n allowPrivateUrls: (browser as Record<string, unknown>).allowPrivateUrls === true,\n commandTimeout:\n typeof (browser as Record<string, unknown>).commandTimeout === 'number' &&\n Number.isFinite((browser as Record<string, unknown>).commandTimeout as number)\n ? Math.floor((browser as Record<string, unknown>).commandTimeout as number)\n : null,\n backend:\n (browser as Record<string, unknown>).backend === 'local' ||\n (browser as Record<string, unknown>).backend === 'cdp' ||\n (browser as Record<string, unknown>).backend === 'cloud' ||\n (browser as Record<string, unknown>).backend === 'extension' ||\n (browser as Record<string, unknown>).backend === 'cloakbrowser'\n ? (browser as Record<string, unknown>).backend\n : null,\n cloudProvider:\n (browser as Record<string, unknown>).cloudProvider === 'browserbase' ||\n (browser as Record<string, unknown>).cloudProvider === 'browser-use'\n ? (browser as Record<string, unknown>).cloudProvider\n : null,\n cloud: maskBrowserCloudConfigForWeb((browser as Record<string, unknown>).cloud),\n cdpUrl:\n typeof (browser as Record<string, unknown>).cdpUrl === 'string' &&\n ((browser as Record<string, unknown>).cdpUrl as string).trim()\n ? ((browser as Record<string, unknown>).cdpUrl as string).trim()\n : null,\n extension:\n (browser as Record<string, unknown>).extension &&\n typeof (browser as Record<string, unknown>).extension === 'object' &&\n !Array.isArray((browser as Record<string, unknown>).extension)\n ? (browser as Record<string, unknown>).extension\n : null,\n cloakbrowser:\n (browser as Record<string, unknown>).cloakbrowser &&\n typeof (browser as Record<string, unknown>).cloakbrowser === 'object' &&\n !Array.isArray((browser as Record<string, unknown>).cloakbrowser)\n ? (browser as Record<string, unknown>).cloakbrowser\n : null,\n humanize:\n typeof (browser as Record<string, unknown>).humanize === 'boolean'\n ? (browser as Record<string, unknown>).humanize\n : null,\n humanPreset:\n (browser as Record<string, unknown>).humanPreset === 'default' ||\n (browser as Record<string, unknown>).humanPreset === 'careful'\n ? (browser as Record<string, unknown>).humanPreset\n : null,\n dialogPolicy:\n (browser as Record<string, unknown>).dialogPolicy === 'must_respond' ||\n (browser as Record<string, unknown>).dialogPolicy === 'auto_accept' ||\n (browser as Record<string, unknown>).dialogPolicy === 'auto_dismiss'\n ? (browser as Record<string, unknown>).dialogPolicy\n : null,\n dialogTimeoutSeconds:\n typeof (browser as Record<string, unknown>).dialogTimeoutSeconds === 'number' &&\n Number.isFinite((browser as Record<string, unknown>).dialogTimeoutSeconds as number)\n ? Math.floor((browser as Record<string, unknown>).dialogTimeoutSeconds as number)\n : null,\n };\n}\n\n/** Sanitized config snapshot for GET/PATCH `/api/config` (matches persisted `service.currentConfig`). */\nexport async function buildSafeWebConfigPayload(service: GatewayService, options: { locale?: string } = {}) {\n const config = service.currentConfig;\n const extensionLoader =\n typeof service.getExtensionLoader === 'function' ? service.getExtensionLoader() : undefined;\n const snapshot = extensionLoader?.getManifestSnapshot();\n const catalog = snapshot\n ? buildChannelCatalogFromSnapshot(snapshot, { locale: options.locale })\n : buildChannelCatalogForConfig(config, { locale: options.locale });\n const channelsPayload = Object.fromEntries(\n catalog.entries.map((entry) => {\n const plugin =\n typeof service.getChannelRuntimePlugin === 'function'\n ? service.getChannelRuntimePlugin(entry.id)\n : undefined;\n const channelCfgKey = entry.configPath.startsWith('channels.')\n ? entry.configPath.slice('channels.'.length).split('.')[0] || entry.id\n : entry.id;\n const channelCfg = config.channels?.[channelCfgKey] as Record<string, unknown> | undefined;\n const setupStatus = getChannelSetupStatus(config, entry.id, entry);\n return [\n entry.id,\n {\n enabled: setupStatus.enabled,\n configured: setupStatus.ready,\n setupStatus,\n config: plugin?.configSurface?.buildConfigSurface(config) ?? channelCfg ?? {},\n schema: entry.configSchema,\n uiHints: entry.uiHints,\n },\n ];\n }),\n );\n return {\n agents: {\n defaultId: resolveDefaultAgentId(config),\n list: listAgentEntries(config)\n .filter((e) => e.enabled !== false)\n .map((e) => ({\n id: normalizeAgentId(e.id),\n identity: e.identity,\n workspace: e.workspace,\n models: e.models,\n tools: e.tools,\n skills: e.skills,\n memory: e.memory,\n workflows: e.workflows,\n boundaries: e.boundaries,\n })),\n capabilityPresets: config.agents?.capabilityPresets ?? {},\n },\n channels: channelsPayload,\n providers: Object.fromEntries(\n await Promise.all(\n getAllProviders().map(async (provider) => [\n provider,\n await maskLlmProviderApiKeyForWeb(provider),\n ]),\n ),\n ),\n /** Masked `cfg.providers` for capability keys (image / STT / etc.). */\n providersConfig: buildSafeProvidersConfigForWeb(config.providers),\n gateway: {\n bind: config.gateway?.bind ?? 'loopback',\n customBindHost: config.gateway?.customBindHost,\n port: config.gateway?.port,\n corsOrigins: Array.isArray(config.gateway?.corsOrigins) ? config.gateway.corsOrigins : [],\n trustedProxies: Array.isArray(config.gateway?.trustedProxies)\n ? config.gateway.trustedProxies\n : [],\n allowRealIpFallback: config.gateway?.allowRealIpFallback === true,\n dangerouslyAllowHostHeaderOriginFallback:\n config.gateway?.dangerouslyAllowHostHeaderOriginFallback === true,\n security: {\n strict: config.gateway?.security?.strict === true,\n },\n auth: {\n mode: config.gateway?.auth?.mode || 'token',\n token: config.gateway?.auth?.token ? maskSecretLength(config.gateway.auth.token) : '',\n password: config.gateway?.auth?.password\n ? maskSecretLength(config.gateway.auth.password)\n : '',\n trustedProxy: config.gateway?.auth?.trustedProxy\n ? {\n userHeader: config.gateway.auth.trustedProxy.userHeader,\n requiredHeaders: config.gateway.auth.trustedProxy.requiredHeaders ?? [],\n allowUsers: config.gateway.auth.trustedProxy.allowUsers ?? [],\n allowLoopback: config.gateway.auth.trustedProxy.allowLoopback === true,\n }\n : undefined,\n rateLimit: {\n enabled: config.gateway?.auth?.rateLimit?.enabled !== false,\n maxAttempts:\n typeof config.gateway?.auth?.rateLimit?.maxAttempts === 'number'\n ? config.gateway.auth.rateLimit.maxAttempts\n : 5,\n windowMs:\n typeof config.gateway?.auth?.rateLimit?.windowMs === 'number'\n ? config.gateway.auth.rateLimit.windowMs\n : 900_000,\n blockDurationMs:\n typeof config.gateway?.auth?.rateLimit?.blockDurationMs === 'number'\n ? config.gateway.auth.rateLimit.blockDurationMs\n : typeof config.gateway?.auth?.rateLimit?.lockoutMs === 'number'\n ? config.gateway.auth.rateLimit.lockoutMs\n : 300_000,\n exemptLoopback: config.gateway?.auth?.rateLimit?.exemptLoopback !== false,\n },\n },\n heartbeat: {\n enabled: config.gateway?.heartbeat?.enabled,\n intervalMs: config.gateway?.heartbeat?.intervalMs,\n includeSystemPromptSection: config.gateway?.heartbeat?.includeSystemPromptSection === true,\n target: config.gateway?.heartbeat?.target,\n targetChatId: config.gateway?.heartbeat?.targetChatId,\n prompt: config.gateway?.heartbeat?.prompt,\n ackMaxChars: config.gateway?.heartbeat?.ackMaxChars,\n isolatedSession: config.gateway?.heartbeat?.isolatedSession,\n activeHours: config.gateway?.heartbeat?.activeHours,\n },\n maxSseConnections:\n typeof config.gateway?.maxSseConnections === 'number'\n ? config.gateway.maxSseConnections\n : 100,\n channelConnectDeferMode: config.gateway?.channelConnectDeferMode ?? 'auto',\n channelConnectDeferIds: Array.isArray(config.gateway?.channelConnectDeferIds)\n ? config.gateway.channelConnectDeferIds\n : [],\n channelConnectDeferSkipIds: Array.isArray(config.gateway?.channelConnectDeferSkipIds)\n ? config.gateway.channelConnectDeferSkipIds\n : [],\n share: resolveShareConfig(config.gateway?.share),\n publicUrl: config.gateway?.publicUrl ?? null,\n skillsMarketplaceProvider: config.gateway?.skillsMarketplaceProvider ?? 'store',\n skillsStoreBaseUrl: config.gateway?.skillsStoreBaseUrl ?? 'https://store.xopc.ai',\n },\n browser: buildSafeBrowserConfigForWeb(config.browser),\n goals: resolveGoalsConfigForWeb(config),\n session: resolveSessionConfigForWeb(config),\n tui: {\n defaultAgent: config.tui?.defaultAgent ?? 'coder',\n },\n tunnel: {\n enabled: config.tunnel?.enabled === true,\n autoStart: config.tunnel?.autoStart === true,\n brokerUrl: config.tunnel?.brokerUrl ?? 'https://frp.xopc.ai/api',\n registrationSecret: config.tunnel?.registrationSecret\n ? maskTunnelSecretForWeb(config.tunnel.registrationSecret)\n : '',\n consent: config.tunnel?.consent\n ? {\n version: config.tunnel.consent.version,\n acceptedAt: config.tunnel.consent.acceptedAt,\n }\n : undefined,\n transport: { tls: 'broker_terminated' as const },\n },\n update: {\n ...resolveUpdateConfigForWeb(config),\n },\n stt: maskSttConfigForWeb(config.tools?.media?.audio),\n tts: maskTtsConfigForWeb(config.messages?.tts),\n voice: config.voice,\n tools: safeToolsWebForGet(config),\n bindings: Array.isArray(config.bindings) ? config.bindings : [],\n mcp: buildSafeMcpConfigForWeb(config),\n };\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BindingsConfigSchema, BrowserConfigSchema, McpConfigSchema, init_schema } from "../../../../config/schema.js";
|
|
1
|
+
import { BindingsConfigSchema, BrowserConfigSchema, McpConfigSchema, VoiceConfigSchema, init_schema } from "../../../../config/schema.js";
|
|
2
2
|
import { CredentialResolver, init_credentials } from "../../../../auth/credentials.js";
|
|
3
3
|
import { canonicalizeConfiguredMcpServer } from "../../../../config/mcp-config-normalize.js";
|
|
4
4
|
import { setTuiDefaultAgentConfig } from "../../../../commands/agents.config.js";
|
|
@@ -106,6 +106,11 @@ async function applyMiscPatch(config, body) {
|
|
|
106
106
|
config.messages = config.messages ?? {};
|
|
107
107
|
config.messages.tts = mergeTtsConfigPatch(config.messages.tts, body.tts);
|
|
108
108
|
}
|
|
109
|
+
if (body.voice !== void 0) {
|
|
110
|
+
const parsed = VoiceConfigSchema.safeParse(body.voice);
|
|
111
|
+
if (!parsed.success) return patchError(parsed.error.issues.map((i) => i.message).join("; "));
|
|
112
|
+
config.voice = parsed.data;
|
|
113
|
+
}
|
|
109
114
|
const toolsPatchErr = applyToolsWebPatch(config, body);
|
|
110
115
|
if (toolsPatchErr) return patchError(toolsPatchErr);
|
|
111
116
|
if (body.tunnel !== void 0) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"misc.js","names":[],"sources":["../../../../../../src/gateway/hono/routes/config-patch/misc.ts"],"sourcesContent":["/**\n * `PATCH /api/config` — tail sections that are each <50 lines:\n * update / goals / session / gateway.{skillsMarketplaceProvider,\n * skillsStoreBaseUrl} / providers / providersConfig / stt / tts / tools /\n * tunnel / bindings / mcp.\n *\n * Most of these delegate to a `mergeXxxConfigPatch(config, body) → { ok,\n * message? }` helper that lives next to the schema, so this file is mostly\n * \"validate object shape, call helper, surface 400 on failure\". The async\n * `providers` branch saves into the credential resolver rather than config.\n *\n * Final validation (resolveGatewayAuth + assertGatewayRuntimeConfig) runs\n * after every gateway-touching patch lands, so it sees the merged shape.\n */\nimport type { Config } from '../../../../config/schema.js';\nimport { BindingsConfigSchema, BrowserConfigSchema, McpConfigSchema } from '../../../../config/schema.js';\nimport { CredentialResolver } from '../../../../auth/credentials.js';\nimport { isMaskedSecretPatchValue } from '../../lib/mask-secret-length.js';\nimport { applyToolsWebPatch } from '../../../config-tools-web.js';\nimport { mergeTunnelConfigPatch } from '../../../../tunnel/tunnel-config.js';\nimport { canonicalizeConfiguredMcpServer } from '../../../../config/mcp-config-normalize.js';\nimport { setTuiDefaultAgentConfig } from '../../../../commands/agents.config.js';\nimport {\n mergeGatewaySkillsMarketplacePatch,\n mergeGoalsConfigPatch,\n mergeSessionConfigPatch,\n mergeUpdateConfigPatch,\n} from '../../../../config/web-patch.js';\nimport { mergeSttConfigPatch, mergeTtsConfigPatch } from '../../lib/safe-voice-config.js';\nimport { assertGatewayRuntimeConfig } from '../../../runtime-config.js';\nimport { resolveGatewayAuth, assertGatewayAuthConfigured } from '../../../auth.js';\nimport { type PatchResult, PATCH_OK, patchError } from './result.js';\n\nexport async function applyMiscPatch(config: Config, body: any): Promise<PatchResult> {\n if (body.update !== undefined && typeof body.update === 'object' && body.update !== null) {\n const updateResult = mergeUpdateConfigPatch(config, body.update as Record<string, unknown>);\n if (updateResult.ok === false) {\n return patchError(updateResult.message);\n }\n }\n\n if (body.goals !== undefined) {\n if (typeof body.goals !== 'object' || body.goals === null || Array.isArray(body.goals)) {\n return patchError('goals must be an object');\n }\n const goalsResult = mergeGoalsConfigPatch(config, body.goals as Record<string, unknown>);\n if (goalsResult.ok === false) {\n return patchError(goalsResult.message);\n }\n }\n\n if (body.session !== undefined) {\n if (typeof body.session !== 'object' || body.session === null || Array.isArray(body.session)) {\n return patchError('session must be an object');\n }\n const sessionResult = mergeSessionConfigPatch(config, body.session as Record<string, unknown>);\n if (sessionResult.ok === false) {\n return patchError(sessionResult.message);\n }\n }\n\n if (body.tui !== undefined) {\n if (typeof body.tui !== 'object' || body.tui === null || Array.isArray(body.tui)) {\n return patchError('tui must be an object');\n }\n const tuiPatch = body.tui as Record<string, unknown>;\n if (tuiPatch.defaultAgent !== undefined) {\n if (tuiPatch.defaultAgent === null) {\n config.tui = { ...config.tui };\n delete config.tui.defaultAgent;\n } else {\n if (typeof tuiPatch.defaultAgent !== 'string' || !tuiPatch.defaultAgent.trim()) {\n return patchError('tui.defaultAgent must be a non-empty string');\n }\n const result = setTuiDefaultAgentConfig(config, tuiPatch.defaultAgent);\n if (result.ok === false) {\n return patchError(result.message);\n }\n config.tui = result.config.tui;\n }\n }\n }\n\n if (body.browser !== undefined) {\n if (typeof body.browser !== 'object' || body.browser === null || Array.isArray(body.browser)) {\n return patchError('browser must be an object');\n }\n const parsed = BrowserConfigSchema.safeParse(body.browser);\n if (!parsed.success) {\n return patchError(parsed.error.issues.map((i) => i.message).join('; '));\n }\n config.browser = parsed.data;\n }\n\n if (\n body.gateway !== undefined &&\n typeof body.gateway === 'object' &&\n body.gateway !== null &&\n !Array.isArray(body.gateway)\n ) {\n const gwPatch = body.gateway as Record<string, unknown>;\n if (gwPatch.skillsMarketplaceProvider !== undefined || gwPatch.skillsStoreBaseUrl !== undefined) {\n const skillsResult = mergeGatewaySkillsMarketplacePatch(config, {\n ...(gwPatch.skillsMarketplaceProvider !== undefined\n ? { skillsMarketplaceProvider: gwPatch.skillsMarketplaceProvider }\n : {}),\n ...(gwPatch.skillsStoreBaseUrl !== undefined\n ? { skillsStoreBaseUrl: gwPatch.skillsStoreBaseUrl }\n : {}),\n });\n if (skillsResult.ok === false) {\n return patchError(skillsResult.message);\n }\n }\n }\n\n // LLM provider credentials — saved into the credential system (not config).\n if (body.providers) {\n const resolver = new CredentialResolver();\n for (const [key, apiKey] of Object.entries(body.providers)) {\n if (\n apiKey !== undefined &&\n typeof apiKey === 'string' &&\n apiKey.trim() &&\n !isMaskedSecretPatchValue(apiKey)\n ) {\n await resolver.saveApiKey(key, apiKey, { profileName: 'default' });\n }\n }\n }\n\n // Structured per-vendor provider config (cfg.providers.<id>) for capability\n // providers (image / audio / video). Distinct from `body.providers` above\n // which targets the LLM-side credential resolver.\n if (body.providersConfig && typeof body.providersConfig === 'object' && !Array.isArray(body.providersConfig)) {\n const cfgProviders = (config as { providers?: Record<string, Record<string, unknown>> }).providers ?? {};\n for (const [vendorId, raw] of Object.entries(body.providersConfig as Record<string, unknown>)) {\n if (!vendorId || typeof vendorId !== 'string') continue;\n if (raw === null) {\n delete cfgProviders[vendorId];\n continue;\n }\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) continue;\n const next = (cfgProviders[vendorId] ?? {}) as Record<string, unknown>;\n const patch = raw as Record<string, unknown>;\n for (const field of ['apiKey', 'baseUrl', 'region', 'imageBaseUrl'] as const) {\n if (patch[field] === null || patch[field] === '') {\n delete next[field];\n } else if (typeof patch[field] === 'string') {\n const trimmed = (patch[field] as string).trim();\n if (field === 'apiKey' && isMaskedSecretPatchValue(trimmed)) {\n continue;\n }\n next[field] = trimmed;\n }\n }\n if (patch.azure === null) {\n delete next.azure;\n } else if (patch.azure && typeof patch.azure === 'object' && !Array.isArray(patch.azure)) {\n next.azure = { ...(next.azure as Record<string, unknown> ?? {}), ...(patch.azure as Record<string, unknown>) };\n }\n if (patch.request === null) {\n delete next.request;\n } else if (patch.request && typeof patch.request === 'object' && !Array.isArray(patch.request)) {\n next.request = { ...(next.request as Record<string, unknown> ?? {}), ...(patch.request as Record<string, unknown>) };\n }\n cfgProviders[vendorId] = next;\n }\n (config as { providers?: Record<string, Record<string, unknown>> }).providers = cfgProviders;\n }\n\n // PATCH `stt` writes to tools.media.audio; PATCH `tts` writes to messages.tts.\n if (body.stt !== undefined) {\n config.tools = config.tools ?? {};\n config.tools.media = config.tools.media ?? {};\n (config.tools.media as Record<string, unknown>).audio = mergeSttConfigPatch(\n config.tools.media.audio,\n body.stt,\n );\n }\n if (body.tts !== undefined) {\n config.messages = config.messages ?? {};\n (config.messages as Record<string, unknown>).tts = mergeTtsConfigPatch(\n config.messages.tts,\n body.tts,\n );\n }\n\n const toolsPatchErr = applyToolsWebPatch(config, body as Record<string, unknown>);\n if (toolsPatchErr) {\n return patchError(toolsPatchErr);\n }\n\n if (body.tunnel !== undefined) {\n if (!body.tunnel || typeof body.tunnel !== 'object' || Array.isArray(body.tunnel)) {\n return patchError('tunnel must be an object');\n }\n const tunnelResult = mergeTunnelConfigPatch(config, body.tunnel as Record<string, unknown>);\n if (tunnelResult.ok === false) {\n return patchError(tunnelResult.message);\n }\n }\n\n if (body.bindings !== undefined) {\n if (!Array.isArray(body.bindings)) {\n return patchError('bindings must be an array');\n }\n const parsed = BindingsConfigSchema.safeParse(body.bindings);\n if (!parsed.success) {\n return patchError(parsed.error.issues.map((i) => i.message).join('; '));\n }\n config.bindings = parsed.data;\n }\n\n if (body.mcp !== undefined) {\n if (body.mcp === null) {\n delete config.mcp;\n } else if (typeof body.mcp !== 'object' || Array.isArray(body.mcp)) {\n return patchError('mcp must be an object');\n } else {\n const parsed = McpConfigSchema.safeParse(body.mcp);\n if (!parsed.success) {\n return patchError(parsed.error.issues.map((i) => i.message).join('; '));\n }\n if (parsed.data === undefined) {\n delete config.mcp;\n } else {\n const next = { ...parsed.data };\n if (next.servers) {\n next.servers = Object.fromEntries(\n Object.entries(next.servers).map(([name, server]) => [\n name,\n canonicalizeConfiguredMcpServer(server as Record<string, unknown>),\n ]),\n );\n }\n config.mcp = next;\n }\n }\n }\n\n return PATCH_OK;\n}\n\n/**\n * Re-validate gateway runtime config when `body.gateway` touched anything that\n * could break the bind/auth contract. Runs *after* all per-section patches\n * land so it sees the fully merged shape.\n */\nexport function validateGatewayAfterPatch(config: Config, body: any): PatchResult {\n if (body.gateway === undefined) return PATCH_OK;\n try {\n const auth = resolveGatewayAuth({ authConfig: config.gateway?.auth });\n assertGatewayAuthConfigured(auth);\n assertGatewayRuntimeConfig({\n cfg: config,\n auth,\n port: config.gateway?.port ?? 18790,\n });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return patchError(message);\n }\n return PATCH_OK;\n}\n"],"mappings":";;;;;;;;;;;;;aAe0G;kBACrC;AAiBrE,eAAsB,eAAe,QAAgB,MAAiC;AACpF,KAAI,KAAK,WAAW,KAAA,KAAa,OAAO,KAAK,WAAW,YAAY,KAAK,WAAW,MAAM;EACxF,MAAM,eAAe,uBAAuB,QAAQ,KAAK,OAAkC;AAC3F,MAAI,aAAa,OAAO,MACtB,QAAO,WAAW,aAAa,QAAQ;;AAI3C,KAAI,KAAK,UAAU,KAAA,GAAW;AAC5B,MAAI,OAAO,KAAK,UAAU,YAAY,KAAK,UAAU,QAAQ,MAAM,QAAQ,KAAK,MAAM,CACpF,QAAO,WAAW,0BAA0B;EAE9C,MAAM,cAAc,sBAAsB,QAAQ,KAAK,MAAiC;AACxF,MAAI,YAAY,OAAO,MACrB,QAAO,WAAW,YAAY,QAAQ;;AAI1C,KAAI,KAAK,YAAY,KAAA,GAAW;AAC9B,MAAI,OAAO,KAAK,YAAY,YAAY,KAAK,YAAY,QAAQ,MAAM,QAAQ,KAAK,QAAQ,CAC1F,QAAO,WAAW,4BAA4B;EAEhD,MAAM,gBAAgB,wBAAwB,QAAQ,KAAK,QAAmC;AAC9F,MAAI,cAAc,OAAO,MACvB,QAAO,WAAW,cAAc,QAAQ;;AAI5C,KAAI,KAAK,QAAQ,KAAA,GAAW;AAC1B,MAAI,OAAO,KAAK,QAAQ,YAAY,KAAK,QAAQ,QAAQ,MAAM,QAAQ,KAAK,IAAI,CAC9E,QAAO,WAAW,wBAAwB;EAE5C,MAAM,WAAW,KAAK;AACtB,MAAI,SAAS,iBAAiB,KAAA,EAC5B,KAAI,SAAS,iBAAiB,MAAM;AAClC,UAAO,MAAM,EAAE,GAAG,OAAO,KAAK;AAC9B,UAAO,OAAO,IAAI;SACb;AACL,OAAI,OAAO,SAAS,iBAAiB,YAAY,CAAC,SAAS,aAAa,MAAM,CAC5E,QAAO,WAAW,8CAA8C;GAElE,MAAM,SAAS,yBAAyB,QAAQ,SAAS,aAAa;AACtE,OAAI,OAAO,OAAO,MAChB,QAAO,WAAW,OAAO,QAAQ;AAEnC,UAAO,MAAM,OAAO,OAAO;;;AAKjC,KAAI,KAAK,YAAY,KAAA,GAAW;AAC9B,MAAI,OAAO,KAAK,YAAY,YAAY,KAAK,YAAY,QAAQ,MAAM,QAAQ,KAAK,QAAQ,CAC1F,QAAO,WAAW,4BAA4B;EAEhD,MAAM,SAAS,oBAAoB,UAAU,KAAK,QAAQ;AAC1D,MAAI,CAAC,OAAO,QACV,QAAO,WAAW,OAAO,MAAM,OAAO,KAAK,MAAM,EAAE,QAAQ,CAAC,KAAK,KAAK,CAAC;AAEzE,SAAO,UAAU,OAAO;;AAG1B,KACE,KAAK,YAAY,KAAA,KACjB,OAAO,KAAK,YAAY,YACxB,KAAK,YAAY,QACjB,CAAC,MAAM,QAAQ,KAAK,QAAQ,EAC5B;EACA,MAAM,UAAU,KAAK;AACrB,MAAI,QAAQ,8BAA8B,KAAA,KAAa,QAAQ,uBAAuB,KAAA,GAAW;GAC/F,MAAM,eAAe,mCAAmC,QAAQ;IAC9D,GAAI,QAAQ,8BAA8B,KAAA,IACtC,EAAE,2BAA2B,QAAQ,2BAA2B,GAChE,EAAE;IACN,GAAI,QAAQ,uBAAuB,KAAA,IAC/B,EAAE,oBAAoB,QAAQ,oBAAoB,GAClD,EAAE;IACP,CAAC;AACF,OAAI,aAAa,OAAO,MACtB,QAAO,WAAW,aAAa,QAAQ;;;AAM7C,KAAI,KAAK,WAAW;EAClB,MAAM,WAAW,IAAI,oBAAoB;AACzC,OAAK,MAAM,CAAC,KAAK,WAAW,OAAO,QAAQ,KAAK,UAAU,CACxD,KACE,WAAW,KAAA,KACX,OAAO,WAAW,YAClB,OAAO,MAAM,IACb,CAAC,yBAAyB,OAAO,CAEjC,OAAM,SAAS,WAAW,KAAK,QAAQ,EAAE,aAAa,WAAW,CAAC;;AAQxE,KAAI,KAAK,mBAAmB,OAAO,KAAK,oBAAoB,YAAY,CAAC,MAAM,QAAQ,KAAK,gBAAgB,EAAE;EAC5G,MAAM,eAAgB,OAAmE,aAAa,EAAE;AACxG,OAAK,MAAM,CAAC,UAAU,QAAQ,OAAO,QAAQ,KAAK,gBAA2C,EAAE;AAC7F,OAAI,CAAC,YAAY,OAAO,aAAa,SAAU;AAC/C,OAAI,QAAQ,MAAM;AAChB,WAAO,aAAa;AACpB;;AAEF,OAAI,CAAC,OAAO,OAAO,QAAQ,YAAY,MAAM,QAAQ,IAAI,CAAE;GAC3D,MAAM,OAAQ,aAAa,aAAa,EAAE;GAC1C,MAAM,QAAQ;AACd,QAAK,MAAM,SAAS;IAAC;IAAU;IAAW;IAAU;IAAe,CACjE,KAAI,MAAM,WAAW,QAAQ,MAAM,WAAW,GAC5C,QAAO,KAAK;YACH,OAAO,MAAM,WAAW,UAAU;IAC3C,MAAM,UAAW,MAAM,OAAkB,MAAM;AAC/C,QAAI,UAAU,YAAY,yBAAyB,QAAQ,CACzD;AAEF,SAAK,SAAS;;AAGlB,OAAI,MAAM,UAAU,KAClB,QAAO,KAAK;YACH,MAAM,SAAS,OAAO,MAAM,UAAU,YAAY,CAAC,MAAM,QAAQ,MAAM,MAAM,CACtF,MAAK,QAAQ;IAAE,GAAI,KAAK,SAAoC,EAAE;IAAG,GAAI,MAAM;IAAmC;AAEhH,OAAI,MAAM,YAAY,KACpB,QAAO,KAAK;YACH,MAAM,WAAW,OAAO,MAAM,YAAY,YAAY,CAAC,MAAM,QAAQ,MAAM,QAAQ,CAC5F,MAAK,UAAU;IAAE,GAAI,KAAK,WAAsC,EAAE;IAAG,GAAI,MAAM;IAAqC;AAEtH,gBAAa,YAAY;;AAE1B,SAAmE,YAAY;;AAIlF,KAAI,KAAK,QAAQ,KAAA,GAAW;AAC1B,SAAO,QAAQ,OAAO,SAAS,EAAE;AACjC,SAAO,MAAM,QAAQ,OAAO,MAAM,SAAS,EAAE;AAC5C,SAAO,MAAM,MAAkC,QAAQ,oBACtD,OAAO,MAAM,MAAM,OACnB,KAAK,IACN;;AAEH,KAAI,KAAK,QAAQ,KAAA,GAAW;AAC1B,SAAO,WAAW,OAAO,YAAY,EAAE;AACtC,SAAO,SAAqC,MAAM,oBACjD,OAAO,SAAS,KAChB,KAAK,IACN;;CAGH,MAAM,gBAAgB,mBAAmB,QAAQ,KAAgC;AACjF,KAAI,cACF,QAAO,WAAW,cAAc;AAGlC,KAAI,KAAK,WAAW,KAAA,GAAW;AAC7B,MAAI,CAAC,KAAK,UAAU,OAAO,KAAK,WAAW,YAAY,MAAM,QAAQ,KAAK,OAAO,CAC/E,QAAO,WAAW,2BAA2B;EAE/C,MAAM,eAAe,uBAAuB,QAAQ,KAAK,OAAkC;AAC3F,MAAI,aAAa,OAAO,MACtB,QAAO,WAAW,aAAa,QAAQ;;AAI3C,KAAI,KAAK,aAAa,KAAA,GAAW;AAC/B,MAAI,CAAC,MAAM,QAAQ,KAAK,SAAS,CAC/B,QAAO,WAAW,4BAA4B;EAEhD,MAAM,SAAS,qBAAqB,UAAU,KAAK,SAAS;AAC5D,MAAI,CAAC,OAAO,QACV,QAAO,WAAW,OAAO,MAAM,OAAO,KAAK,MAAM,EAAE,QAAQ,CAAC,KAAK,KAAK,CAAC;AAEzE,SAAO,WAAW,OAAO;;AAG3B,KAAI,KAAK,QAAQ,KAAA,EACf,KAAI,KAAK,QAAQ,KACf,QAAO,OAAO;UACL,OAAO,KAAK,QAAQ,YAAY,MAAM,QAAQ,KAAK,IAAI,CAChE,QAAO,WAAW,wBAAwB;MACrC;EACL,MAAM,SAAS,gBAAgB,UAAU,KAAK,IAAI;AAClD,MAAI,CAAC,OAAO,QACV,QAAO,WAAW,OAAO,MAAM,OAAO,KAAK,MAAM,EAAE,QAAQ,CAAC,KAAK,KAAK,CAAC;AAEzE,MAAI,OAAO,SAAS,KAAA,EAClB,QAAO,OAAO;OACT;GACL,MAAM,OAAO,EAAE,GAAG,OAAO,MAAM;AAC/B,OAAI,KAAK,QACP,MAAK,UAAU,OAAO,YACpB,OAAO,QAAQ,KAAK,QAAQ,CAAC,KAAK,CAAC,MAAM,YAAY,CACnD,MACA,gCAAgC,OAAkC,CACnE,CAAC,CACH;AAEH,UAAO,MAAM;;;AAKnB,QAAO;;;;;;;AAQT,SAAgB,0BAA0B,QAAgB,MAAwB;AAChF,KAAI,KAAK,YAAY,KAAA,EAAW,QAAO;AACvC,KAAI;EACF,MAAM,OAAO,mBAAmB,EAAE,YAAY,OAAO,SAAS,MAAM,CAAC;AACrE,8BAA4B,KAAK;AACjC,6BAA2B;GACzB,KAAK;GACL;GACA,MAAM,OAAO,SAAS,QAAQ;GAC/B,CAAC;UACK,KAAK;AAEZ,SAAO,WADS,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,CACtC;;AAE5B,QAAO"}
|
|
1
|
+
{"version":3,"file":"misc.js","names":[],"sources":["../../../../../../src/gateway/hono/routes/config-patch/misc.ts"],"sourcesContent":["/**\n * `PATCH /api/config` — tail sections that are each <50 lines:\n * update / goals / session / gateway.{skillsMarketplaceProvider,\n * skillsStoreBaseUrl} / providers / providersConfig / stt / tts / tools /\n * tunnel / bindings / mcp.\n *\n * Most of these delegate to a `mergeXxxConfigPatch(config, body) → { ok,\n * message? }` helper that lives next to the schema, so this file is mostly\n * \"validate object shape, call helper, surface 400 on failure\". The async\n * `providers` branch saves into the credential resolver rather than config.\n *\n * Final validation (resolveGatewayAuth + assertGatewayRuntimeConfig) runs\n * after every gateway-touching patch lands, so it sees the merged shape.\n */\nimport type { Config } from '../../../../config/schema.js';\nimport { BindingsConfigSchema, BrowserConfigSchema, McpConfigSchema, VoiceConfigSchema } from '../../../../config/schema.js';\nimport { CredentialResolver } from '../../../../auth/credentials.js';\nimport { isMaskedSecretPatchValue } from '../../lib/mask-secret-length.js';\nimport { applyToolsWebPatch } from '../../../config-tools-web.js';\nimport { mergeTunnelConfigPatch } from '../../../../tunnel/tunnel-config.js';\nimport { canonicalizeConfiguredMcpServer } from '../../../../config/mcp-config-normalize.js';\nimport { setTuiDefaultAgentConfig } from '../../../../commands/agents.config.js';\nimport {\n mergeGatewaySkillsMarketplacePatch,\n mergeGoalsConfigPatch,\n mergeSessionConfigPatch,\n mergeUpdateConfigPatch,\n} from '../../../../config/web-patch.js';\nimport { mergeSttConfigPatch, mergeTtsConfigPatch } from '../../lib/safe-voice-config.js';\nimport { assertGatewayRuntimeConfig } from '../../../runtime-config.js';\nimport { resolveGatewayAuth, assertGatewayAuthConfigured } from '../../../auth.js';\nimport { type PatchResult, PATCH_OK, patchError } from './result.js';\n\nexport async function applyMiscPatch(config: Config, body: any): Promise<PatchResult> {\n if (body.update !== undefined && typeof body.update === 'object' && body.update !== null) {\n const updateResult = mergeUpdateConfigPatch(config, body.update as Record<string, unknown>);\n if (updateResult.ok === false) {\n return patchError(updateResult.message);\n }\n }\n\n if (body.goals !== undefined) {\n if (typeof body.goals !== 'object' || body.goals === null || Array.isArray(body.goals)) {\n return patchError('goals must be an object');\n }\n const goalsResult = mergeGoalsConfigPatch(config, body.goals as Record<string, unknown>);\n if (goalsResult.ok === false) {\n return patchError(goalsResult.message);\n }\n }\n\n if (body.session !== undefined) {\n if (typeof body.session !== 'object' || body.session === null || Array.isArray(body.session)) {\n return patchError('session must be an object');\n }\n const sessionResult = mergeSessionConfigPatch(config, body.session as Record<string, unknown>);\n if (sessionResult.ok === false) {\n return patchError(sessionResult.message);\n }\n }\n\n if (body.tui !== undefined) {\n if (typeof body.tui !== 'object' || body.tui === null || Array.isArray(body.tui)) {\n return patchError('tui must be an object');\n }\n const tuiPatch = body.tui as Record<string, unknown>;\n if (tuiPatch.defaultAgent !== undefined) {\n if (tuiPatch.defaultAgent === null) {\n config.tui = { ...config.tui };\n delete config.tui.defaultAgent;\n } else {\n if (typeof tuiPatch.defaultAgent !== 'string' || !tuiPatch.defaultAgent.trim()) {\n return patchError('tui.defaultAgent must be a non-empty string');\n }\n const result = setTuiDefaultAgentConfig(config, tuiPatch.defaultAgent);\n if (result.ok === false) {\n return patchError(result.message);\n }\n config.tui = result.config.tui;\n }\n }\n }\n\n if (body.browser !== undefined) {\n if (typeof body.browser !== 'object' || body.browser === null || Array.isArray(body.browser)) {\n return patchError('browser must be an object');\n }\n const parsed = BrowserConfigSchema.safeParse(body.browser);\n if (!parsed.success) {\n return patchError(parsed.error.issues.map((i) => i.message).join('; '));\n }\n config.browser = parsed.data;\n }\n\n if (\n body.gateway !== undefined &&\n typeof body.gateway === 'object' &&\n body.gateway !== null &&\n !Array.isArray(body.gateway)\n ) {\n const gwPatch = body.gateway as Record<string, unknown>;\n if (gwPatch.skillsMarketplaceProvider !== undefined || gwPatch.skillsStoreBaseUrl !== undefined) {\n const skillsResult = mergeGatewaySkillsMarketplacePatch(config, {\n ...(gwPatch.skillsMarketplaceProvider !== undefined\n ? { skillsMarketplaceProvider: gwPatch.skillsMarketplaceProvider }\n : {}),\n ...(gwPatch.skillsStoreBaseUrl !== undefined\n ? { skillsStoreBaseUrl: gwPatch.skillsStoreBaseUrl }\n : {}),\n });\n if (skillsResult.ok === false) {\n return patchError(skillsResult.message);\n }\n }\n }\n\n // LLM provider credentials — saved into the credential system (not config).\n if (body.providers) {\n const resolver = new CredentialResolver();\n for (const [key, apiKey] of Object.entries(body.providers)) {\n if (\n apiKey !== undefined &&\n typeof apiKey === 'string' &&\n apiKey.trim() &&\n !isMaskedSecretPatchValue(apiKey)\n ) {\n await resolver.saveApiKey(key, apiKey, { profileName: 'default' });\n }\n }\n }\n\n // Structured per-vendor provider config (cfg.providers.<id>) for capability\n // providers (image / audio / video). Distinct from `body.providers` above\n // which targets the LLM-side credential resolver.\n if (body.providersConfig && typeof body.providersConfig === 'object' && !Array.isArray(body.providersConfig)) {\n const cfgProviders = (config as { providers?: Record<string, Record<string, unknown>> }).providers ?? {};\n for (const [vendorId, raw] of Object.entries(body.providersConfig as Record<string, unknown>)) {\n if (!vendorId || typeof vendorId !== 'string') continue;\n if (raw === null) {\n delete cfgProviders[vendorId];\n continue;\n }\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) continue;\n const next = (cfgProviders[vendorId] ?? {}) as Record<string, unknown>;\n const patch = raw as Record<string, unknown>;\n for (const field of ['apiKey', 'baseUrl', 'region', 'imageBaseUrl'] as const) {\n if (patch[field] === null || patch[field] === '') {\n delete next[field];\n } else if (typeof patch[field] === 'string') {\n const trimmed = (patch[field] as string).trim();\n if (field === 'apiKey' && isMaskedSecretPatchValue(trimmed)) {\n continue;\n }\n next[field] = trimmed;\n }\n }\n if (patch.azure === null) {\n delete next.azure;\n } else if (patch.azure && typeof patch.azure === 'object' && !Array.isArray(patch.azure)) {\n next.azure = { ...(next.azure as Record<string, unknown> ?? {}), ...(patch.azure as Record<string, unknown>) };\n }\n if (patch.request === null) {\n delete next.request;\n } else if (patch.request && typeof patch.request === 'object' && !Array.isArray(patch.request)) {\n next.request = { ...(next.request as Record<string, unknown> ?? {}), ...(patch.request as Record<string, unknown>) };\n }\n cfgProviders[vendorId] = next;\n }\n (config as { providers?: Record<string, Record<string, unknown>> }).providers = cfgProviders;\n }\n\n // PATCH `stt` writes to tools.media.audio; PATCH `tts` writes to messages.tts.\n if (body.stt !== undefined) {\n config.tools = config.tools ?? {};\n config.tools.media = config.tools.media ?? {};\n (config.tools.media as Record<string, unknown>).audio = mergeSttConfigPatch(\n config.tools.media.audio,\n body.stt,\n );\n }\n if (body.tts !== undefined) {\n config.messages = config.messages ?? {};\n (config.messages as Record<string, unknown>).tts = mergeTtsConfigPatch(\n config.messages.tts,\n body.tts,\n );\n }\n if (body.voice !== undefined) {\n const parsed = VoiceConfigSchema.safeParse(body.voice);\n if (!parsed.success) {\n return patchError(parsed.error.issues.map((i) => i.message).join('; '));\n }\n config.voice = parsed.data;\n }\n\n const toolsPatchErr = applyToolsWebPatch(config, body as Record<string, unknown>);\n if (toolsPatchErr) {\n return patchError(toolsPatchErr);\n }\n\n if (body.tunnel !== undefined) {\n if (!body.tunnel || typeof body.tunnel !== 'object' || Array.isArray(body.tunnel)) {\n return patchError('tunnel must be an object');\n }\n const tunnelResult = mergeTunnelConfigPatch(config, body.tunnel as Record<string, unknown>);\n if (tunnelResult.ok === false) {\n return patchError(tunnelResult.message);\n }\n }\n\n if (body.bindings !== undefined) {\n if (!Array.isArray(body.bindings)) {\n return patchError('bindings must be an array');\n }\n const parsed = BindingsConfigSchema.safeParse(body.bindings);\n if (!parsed.success) {\n return patchError(parsed.error.issues.map((i) => i.message).join('; '));\n }\n config.bindings = parsed.data;\n }\n\n if (body.mcp !== undefined) {\n if (body.mcp === null) {\n delete config.mcp;\n } else if (typeof body.mcp !== 'object' || Array.isArray(body.mcp)) {\n return patchError('mcp must be an object');\n } else {\n const parsed = McpConfigSchema.safeParse(body.mcp);\n if (!parsed.success) {\n return patchError(parsed.error.issues.map((i) => i.message).join('; '));\n }\n if (parsed.data === undefined) {\n delete config.mcp;\n } else {\n const next = { ...parsed.data };\n if (next.servers) {\n next.servers = Object.fromEntries(\n Object.entries(next.servers).map(([name, server]) => [\n name,\n canonicalizeConfiguredMcpServer(server as Record<string, unknown>),\n ]),\n );\n }\n config.mcp = next;\n }\n }\n }\n\n return PATCH_OK;\n}\n\n/**\n * Re-validate gateway runtime config when `body.gateway` touched anything that\n * could break the bind/auth contract. Runs *after* all per-section patches\n * land so it sees the fully merged shape.\n */\nexport function validateGatewayAfterPatch(config: Config, body: any): PatchResult {\n if (body.gateway === undefined) return PATCH_OK;\n try {\n const auth = resolveGatewayAuth({ authConfig: config.gateway?.auth });\n assertGatewayAuthConfigured(auth);\n assertGatewayRuntimeConfig({\n cfg: config,\n auth,\n port: config.gateway?.port ?? 18790,\n });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return patchError(message);\n }\n return PATCH_OK;\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { appendMemorySignal, appendMemoryTraceEvent, deleteMemoryRecord, findLatestMemoryInjectTrace, getMemoryRecord, listMemoryRecords, listMemorySignals, listMemoryTraceEvents, searchMemoryRecords, setLatestMemoryInjectFeedback, setMemoryTraceFeedback, summarizeMemoryRecallFeedback, summarizeUserUnderstandingQuality, upsertMemoryRecord } from "../../../storage/sqlite/memory-records-repository.js";
|
|
2
|
-
import { listKnowledgeSourceItems, listKnowledgeSyncRuns } from "../../../storage/sqlite/knowledge-repository.js";
|
|
2
|
+
import { listKnowledgeSourceChanges, listKnowledgeSourceItems, listKnowledgeSyncRuns } from "../../../storage/sqlite/knowledge-repository.js";
|
|
3
3
|
import "../../../storage/sqlite/index.js";
|
|
4
4
|
import { MemoryPolicySchema, init_schema } from "../../../agent-manifest/schema.js";
|
|
5
5
|
import { init_agent_scope, resolveDefaultAgentId } from "../../../agent/agent-scope.js";
|
|
@@ -294,6 +294,16 @@ function registerMemoryRoutes(authenticated, deps) {
|
|
|
294
294
|
});
|
|
295
295
|
return c.json({ sourceItems });
|
|
296
296
|
});
|
|
297
|
+
authenticated.get("/api/knowledge/source-changes", (c) => {
|
|
298
|
+
const afterSequenceQuery = c.req.query("afterSequence");
|
|
299
|
+
const afterSequence = afterSequenceQuery ? Number.parseInt(afterSequenceQuery, 10) : void 0;
|
|
300
|
+
const sourceChanges = listKnowledgeSourceChanges({
|
|
301
|
+
sourceInstanceId: c.req.query("sourceInstanceId") || void 0,
|
|
302
|
+
afterSequence: Number.isFinite(afterSequence) ? afterSequence : void 0,
|
|
303
|
+
limit: parseLimit(c.req.query("limit"))
|
|
304
|
+
});
|
|
305
|
+
return c.json({ sourceChanges });
|
|
306
|
+
});
|
|
297
307
|
authenticated.get("/api/knowledge/sync-runs", (c) => {
|
|
298
308
|
const syncRuns = listKnowledgeSyncRuns({
|
|
299
309
|
sourceInstanceId: c.req.query("sourceInstanceId") || void 0,
|