@xopcai/xopc 0.0.196 → 0.0.199
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/{activity-store-BwmdJ5pv.js → activity-store-DYVN1hRp.js} +1 -1
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-B1oxPli_.js → agent-avatar-dicebear-Bt70bZjv.js} +1 -1
- package/dist/gateway/static/root/assets/agents-D19uNmJf.js +68 -0
- package/dist/gateway/static/root/assets/{api-BM1hNzjw.js → api-B8mzbAle.js} +1 -1
- package/dist/gateway/static/root/assets/app-management-settings-panel-B4x7JfL8.js +1 -0
- package/dist/gateway/static/root/assets/appearance-settings-Dwdagf-Q.js +1 -0
- package/dist/gateway/static/root/assets/apps-page-DkTiesxe.js +1 -0
- package/dist/gateway/static/root/assets/{archive-plugin-BdE4Mati.js → archive-plugin-Cv0VXKLm.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-CTzaQcLV.js → attachment-load-CgidKrXn.js} +1 -1
- package/dist/gateway/static/root/assets/automations-page-BR6qMOVr.js +6 -0
- package/dist/gateway/static/root/assets/{binary-plugins-B25zTNxl.js → binary-plugins-DN3CQgIq.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-Cdrf2awF.js → block-editor-BwkSl1F_.js} +46 -46
- package/dist/gateway/static/root/assets/browser-settings-page-CDFQWAxI.js +8 -0
- package/dist/gateway/static/root/assets/browser-workflow-inputs-_ndaecsf.js +1 -0
- package/dist/gateway/static/root/assets/{browser-workflows-page-Wx9FrbK-.js → browser-workflows-page-C_I31wLm.js} +2 -2
- package/dist/gateway/static/root/assets/button-CSEbl4zT.js +1 -0
- package/dist/gateway/static/root/assets/capabilities-settings-panel-D41mssOq.js +7 -0
- package/dist/gateway/static/root/assets/{capability-presets-api-BoOzFotX.js → capability-presets-api-Dy1jZJKs.js} +1 -1
- package/dist/gateway/static/root/assets/capability-presets-settings-panel-BXgCyuu2.js +3 -0
- package/dist/gateway/static/root/assets/{channel-recipient-api-_8IiTxRB.js → channel-recipient-api-D-9qiuio.js} +1 -1
- package/dist/gateway/static/root/assets/channels-settings-DV5BhAeu.js +1 -0
- package/dist/gateway/static/root/assets/channels-status-swr-B4Rbh3bM.js +1 -0
- package/dist/gateway/static/root/assets/connectors-api-5FEBYguL.js +1 -0
- package/dist/gateway/static/root/assets/connectors-page-C_qkEN0T.js +2 -0
- package/dist/gateway/static/root/assets/copy-Dv6d4Dvw.js +1 -0
- package/dist/gateway/static/root/assets/cron-expression-SEFx5rnr.js +2 -0
- package/dist/gateway/static/root/assets/date-picker-D4zCUQJL.js +1 -0
- package/dist/gateway/static/root/assets/dependency-picker-DpQAxS2k.js +1 -0
- package/dist/gateway/static/root/assets/{desktop-pet-BOb8sEhN.js → desktop-pet-B_5G3lQl.js} +1 -1
- package/dist/gateway/static/root/assets/desktop-pet-settings-ClEyoUJe.js +1 -0
- package/dist/gateway/static/root/assets/directory-picker-path-field-AsFWmOjG.js +1 -0
- package/dist/gateway/static/root/assets/dist-BPoKqzeH.js +1 -0
- package/dist/gateway/static/root/assets/dist-DY4Af0fY.js +1 -0
- package/dist/gateway/static/root/assets/{extension-debug-page-p83TmZFd.js → extension-debug-page-C22Y2o5l.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-DMQ3ovxA.js → extension-page-C61Ikawk.js} +1 -1
- package/dist/gateway/static/root/assets/extension-settings-page-WlWWcDFr.js +1 -0
- package/dist/gateway/static/root/assets/external-link-DjqjBmc3.js +1 -0
- package/dist/gateway/static/root/assets/form-field-width-DJw_jULe.js +1 -0
- package/dist/gateway/static/root/assets/gateway-config-api-ByIOMQdc.js +1 -0
- package/dist/gateway/static/root/assets/gateway-config-swr-jB0hAhWO.js +1 -0
- package/dist/gateway/static/root/assets/gateway-settings-CzzS9A17.js +1 -0
- package/dist/gateway/static/root/assets/{gateway-startup-retry--2njcDpC.js → gateway-startup-retry-C-fRlHCL.js} +1 -1
- package/dist/gateway/static/root/assets/globe-DSSGFWFn.js +1 -0
- package/dist/gateway/static/root/assets/heartbeat-settings-B4A_CXUL.js +1 -0
- package/dist/gateway/static/root/assets/home-page-FVGYYPWo.js +3 -0
- package/dist/gateway/static/root/assets/index-CGbKTCST.js +152 -0
- package/dist/gateway/static/root/assets/index-CR233S0Q.css +1 -0
- package/dist/gateway/static/root/assets/interaction-BmQogeqz.js +1 -0
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-BQb06_jl.js → keyboard-shortcuts-settings-BD_TFz0_.js} +1 -1
- package/dist/gateway/static/root/assets/loader-circle-CifKLtgG.js +1 -0
- package/dist/gateway/static/root/assets/local-app-workbench-page-BNpkigNb.js +2 -0
- package/dist/gateway/static/root/assets/local-apps-page-BwbUeLfm.js +1 -0
- package/dist/gateway/static/root/assets/{locale-store-Bjvw7jtg.js → locale-store-xULEcFVs.js} +1 -1
- package/dist/gateway/static/root/assets/logs-page-DXP9OQQt.js +2 -0
- package/dist/gateway/static/root/assets/management-settings-CW2VpqtT.js +1 -0
- package/dist/gateway/static/root/assets/{markdown-split-Db-KrpOQ.js → markdown-split-B1M9KO16.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-CHN66qs0.js → media-plugins-B_QlNdqZ.js} +1 -1
- package/dist/gateway/static/root/assets/messages-3qRsLZuX.js +3 -0
- package/dist/gateway/static/root/assets/{notes-page-CJtKZM0P.js → notes-page-BTbD4GmH.js} +1 -1
- package/dist/gateway/static/root/assets/notes-workbench-JRiq7QDW.js +4 -0
- package/dist/gateway/static/root/assets/page-tabs-BaEQMYm_.js +1 -0
- package/dist/gateway/static/root/assets/popover-select-BzdnTy3h.js +1 -0
- package/dist/gateway/static/root/assets/preview-open-alternatives-CeqtArlZ.js +1 -0
- package/dist/gateway/static/root/assets/{product-open-page--RnCc9bb.js → product-open-page-DnalBZZs.js} +1 -1
- package/dist/gateway/static/root/assets/project-detail-page-CjWA13aE.js +1 -0
- package/dist/gateway/static/root/assets/projects-page-jsCkScbG.js +1 -0
- package/dist/gateway/static/root/assets/remote-access-hub-CGPJTEkM.js +1 -0
- package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-Bg1W2__k.js → runtime-tools-settings-panel-cGkBEfg4.js} +4 -4
- package/dist/gateway/static/root/assets/schema-form-CmsrLlxu.js +1 -0
- package/dist/gateway/static/root/assets/sessions-page-BBHzziMd.js +1 -0
- package/dist/gateway/static/root/assets/settings-advanced-gate-2cGg8-9h.js +1 -0
- package/dist/gateway/static/root/assets/settings-form-draft-Dy10ldo8.js +1 -0
- package/dist/gateway/static/root/assets/settings-form-section-DUWCbaMJ.js +1 -0
- package/dist/gateway/static/root/assets/{settings-loading-skeleton-BS36Mjzz.js → settings-loading-skeleton-Bvy5rdMY.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-BDSd7F9Y.js → settings-page-cZSAwsRR.js} +1 -1
- package/dist/gateway/static/root/assets/settings-page-layout-BRQNel6r.js +1 -0
- package/dist/gateway/static/root/assets/settings-shell-dialog-layer-BU3UuiJr.js +1 -0
- package/dist/gateway/static/root/assets/setup-status-panel-Cf06b66-.js +1 -0
- package/dist/gateway/static/root/assets/share-preview-page-BzA2NCAH.js +2 -0
- package/dist/gateway/static/root/assets/shares-settings-xlNKydYY.js +1 -0
- package/dist/gateway/static/root/assets/skill-reload-api-C5Xu93c_.js +1 -0
- package/dist/gateway/static/root/assets/skills-page-Bvde7_i5.js +2 -0
- package/dist/gateway/static/root/assets/{skills-page.utils-CnbJncdL.js → skills-page.utils-vXSqHR7g.js} +1 -1
- package/dist/gateway/static/root/assets/system-settings-panel-9cQGOGCK.js +1 -0
- package/dist/gateway/static/root/assets/task-detail-page-Bkno4tTe.js +1 -0
- package/dist/gateway/static/root/assets/{theme-store-DtxpMC5S.js → theme-store-CHQRiuCF.js} +1 -1
- package/dist/gateway/static/root/assets/tool-activity-R8V9l6nk.js +1 -0
- package/dist/gateway/static/root/assets/typed-models-lib-q5-Vfqd8.js +1 -0
- package/dist/gateway/static/root/assets/use-autosave-7XfKULPI.js +1 -0
- package/dist/gateway/static/root/assets/user-context-page-CZ9m9aC_.js +1 -0
- package/dist/gateway/static/root/assets/voice-api-key-field-DXr21v-3.js +1 -0
- package/dist/gateway/static/root/assets/work-discovery-overlay-CeG7g_DZ.js +1 -0
- package/dist/gateway/static/root/assets/work-discovery-page-CQWZD03N.js +1 -0
- package/dist/gateway/static/root/assets/{workflow-run-setup-panel-BEmugHfL.js → workflow-run-setup-panel-DSQQZP9T.js} +2 -2
- package/dist/gateway/static/root/assets/workflows-page-Cs-MXFJ1.js +5 -0
- package/dist/gateway/static/root/assets/working-directory-picker-modal-D-IgcHPp.js +1 -0
- package/dist/gateway/static/root/index.html +23 -8
- package/dist/package.js +1 -1
- package/dist/packages/gateway-contract/src/tasks.js +54 -2
- package/dist/src/agent/agent-manager.d.ts +1 -0
- package/dist/src/agent/agent-manager.js +1 -0
- package/dist/src/agent/embedded/execution-context.d.ts +2 -0
- package/dist/src/agent/embedded/execution-context.js +11 -0
- package/dist/src/agent/embedded/index.d.ts +3 -1
- package/dist/src/agent/embedded/index.js +5 -3
- package/dist/src/agent/embedded/run-turn.js +15 -9
- package/dist/src/agent/embedded/session-runner.d.ts +6 -11
- package/dist/src/agent/embedded/session-runner.js +25 -36
- package/dist/src/agent/embedded/transcript-runtime.d.ts +35 -0
- package/dist/src/agent/embedded/transcript-runtime.js +66 -0
- package/dist/src/agent/embedded/types.d.ts +3 -1
- package/dist/src/agent/service.d.ts +15 -0
- package/dist/src/agent/service.js +31 -0
- package/dist/src/agent/service.types.d.ts +1 -0
- package/dist/src/agent/tools/factory.d.ts +2 -0
- package/dist/src/agent/tools/factory.js +2 -1
- package/dist/src/agent/tools/xopc-use-tool.d.ts +1 -0
- package/dist/src/agent/tools/xopc-use-tool.js +56 -6
- package/dist/src/automations/domain/validation.d.ts +40 -0
- package/dist/src/automations/draft/automation-draft-validator.d.ts +4 -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/rules.js +2 -2
- package/dist/src/gateway/agents-admin.js +1 -1
- package/dist/src/gateway/hono/app.d.ts +2 -0
- package/dist/src/gateway/hono/app.js +10 -3
- package/dist/src/gateway/hono/middleware/strict-rate-limit.d.ts +4 -0
- package/dist/src/gateway/hono/middleware/strict-rate-limit.js +8 -1
- package/dist/src/gateway/hono/routes/config-patch/misc.js +2 -1
- package/dist/src/gateway/hono/routes/config.js +9 -0
- package/dist/src/gateway/hono/routes/deps.d.ts +1 -0
- package/dist/src/gateway/hono/routes/home.d.ts +1 -1
- package/dist/src/gateway/hono/routes/home.js +2 -2
- package/dist/src/gateway/hono/routes/index.js +2 -0
- package/dist/src/gateway/hono/routes/projects.js +67 -1
- package/dist/src/gateway/hono/routes/side-chats.d.ts +3 -0
- package/dist/src/gateway/hono/routes/side-chats.js +173 -0
- package/dist/src/gateway/hono/routes/tasks.js +112 -19
- package/dist/src/gateway/host.d.ts +9 -1
- package/dist/src/gateway/host.js +22 -5
- package/dist/src/gateway/rate-limit/buckets.d.ts +3 -0
- package/dist/src/gateway/rate-limit/buckets.js +12 -0
- package/dist/src/gateway/runtime-config.js +6 -5
- package/dist/src/gateway/security/audit.js +0 -7
- package/dist/src/gateway/server.js +4 -1
- package/dist/src/gateway/service/agent-runner.d.ts +4 -0
- package/dist/src/gateway/service/agent-runner.js +15 -1
- package/dist/src/gateway/service/run-gateway-agent.js +1 -1
- package/dist/src/gateway/service.d.ts +5 -0
- package/dist/src/gateway/service.js +38 -8
- package/dist/src/gateway/side-chat/context-snapshot.d.ts +11 -0
- package/dist/src/gateway/side-chat/context-snapshot.js +108 -0
- package/dist/src/gateway/side-chat/index.d.ts +5 -0
- package/dist/src/gateway/side-chat/index.js +4 -0
- package/dist/src/gateway/side-chat/manager.d.ts +45 -0
- package/dist/src/gateway/side-chat/manager.js +198 -0
- package/dist/src/gateway/side-chat/run-service.d.ts +22 -0
- package/dist/src/gateway/side-chat/run-service.js +110 -0
- package/dist/src/gateway/side-chat/types.d.ts +61 -0
- package/dist/src/gateway/side-chat/types.js +1 -0
- package/dist/src/proactive/actions/service.d.ts +2 -0
- package/dist/src/proactive/actions/service.js +80 -0
- package/dist/src/proactive/execution/insight.js +20 -0
- package/dist/src/proactive/execution/repository.js +8 -2
- package/dist/src/proactive/execution/types.d.ts +14 -0
- package/dist/src/proactive/inbox/repository.js +59 -9
- package/dist/src/proactive/inbox/service.d.ts +2 -2
- package/dist/src/proactive/inbox/service.js +7 -2
- package/dist/src/proactive/inbox/types.d.ts +14 -0
- package/dist/src/proactive/inbox/worker.js +2 -0
- package/dist/src/proactive/scenarios/prompt-composer.js +2 -2
- package/dist/src/routing/session-key-utils.d.ts +0 -1
- package/dist/src/routing/session-key-utils.js +1 -6
- package/dist/src/routing/session-key.js +2 -2
- package/dist/src/storage/sqlite/migrations/118_task_board_rank.sql +6 -0
- package/dist/src/storage/sqlite/migrations/119_proactive_disposition.sql +13 -0
- package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
- package/dist/src/storage/sqlite/migrations/runner.js +3 -3
- package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
- package/dist/src/storage/sqlite/schema.js +1 -1
- package/dist/src/tasks/home-query-service.d.ts +29 -0
- package/dist/src/tasks/home-query-service.js +291 -7
- package/dist/src/tasks/index.d.ts +1 -0
- package/dist/src/tasks/index.js +5 -4
- package/dist/src/tasks/project-monitoring-service.d.ts +9 -0
- package/dist/src/tasks/project-monitoring-service.js +41 -3
- package/dist/src/tasks/project-operating-view-service.d.ts +2 -2
- package/dist/src/tasks/project-operating-view-service.js +47 -21
- package/dist/src/tasks/task-application-service.js +99 -6
- package/dist/src/tasks/task-change-events.d.ts +13 -0
- package/dist/src/tasks/task-change-events.js +29 -0
- package/dist/src/tasks/task-read-model-projector.js +2 -1
- package/dist/src/tasks/task-repository.d.ts +7 -0
- package/dist/src/tasks/task-repository.js +29 -6
- package/dist/src/tunnel/enable-lan-pairing.js +15 -26
- package/dist/src/tunnel/pair-context.js +1 -1
- package/dist/src/tunnel/pair-url.js +1 -1
- package/dist/src/tunnel/tunnel-qr.d.ts +0 -7
- package/dist/src/tunnel/tunnel-qr.js +2 -19
- package/package.json +4 -2
- package/dist/gateway/static/root/assets/agents-VoFNMGL3.js +0 -68
- package/dist/gateway/static/root/assets/app-management-settings-panel-vmuJ_Z6i.js +0 -1
- package/dist/gateway/static/root/assets/appearance-settings-BMlEoe-G.js +0 -1
- package/dist/gateway/static/root/assets/apps-page-CsNB6mv_.js +0 -1
- package/dist/gateway/static/root/assets/automations-page-CW1YWgiX.js +0 -6
- package/dist/gateway/static/root/assets/browser-settings-page-BMTmGMM6.js +0 -8
- package/dist/gateway/static/root/assets/browser-workflow-inputs-BsmHy0ks.js +0 -1
- package/dist/gateway/static/root/assets/capabilities-settings-panel-C5l85__e.js +0 -7
- package/dist/gateway/static/root/assets/capability-presets-settings-panel-9T2KXAs1.js +0 -3
- package/dist/gateway/static/root/assets/channels-settings-DquD4BPQ.js +0 -1
- package/dist/gateway/static/root/assets/channels-status-swr-DPaJSYX_.js +0 -1
- package/dist/gateway/static/root/assets/connectors-api-BC-4ftOM.js +0 -1
- package/dist/gateway/static/root/assets/connectors-page-7zUxVHQg.js +0 -2
- package/dist/gateway/static/root/assets/cron-expression-BycQlU8Q.js +0 -2
- package/dist/gateway/static/root/assets/dependency-picker-BvYqHRz0.js +0 -1
- package/dist/gateway/static/root/assets/desktop-pet-settings-DStgPb82.js +0 -1
- package/dist/gateway/static/root/assets/directory-picker-path-field-DUUr9sq0.js +0 -1
- package/dist/gateway/static/root/assets/extension-settings-page-BcSKEU8s.js +0 -1
- package/dist/gateway/static/root/assets/gateway-config-api-C-MmtCc7.js +0 -1
- package/dist/gateway/static/root/assets/gateway-settings-CuaRDWbg.js +0 -1
- package/dist/gateway/static/root/assets/heartbeat-settings-Dm8TrfGa.js +0 -1
- package/dist/gateway/static/root/assets/home-page-BYhDbUvI.js +0 -3
- package/dist/gateway/static/root/assets/index-BzPwnXho.js +0 -150
- package/dist/gateway/static/root/assets/index-CFFYvilx.css +0 -1
- package/dist/gateway/static/root/assets/local-app-workbench-page-CHk4x0kR.js +0 -2
- package/dist/gateway/static/root/assets/local-apps-page-DQtsxjGs.js +0 -1
- package/dist/gateway/static/root/assets/logs-page-CDqIY72S.js +0 -2
- package/dist/gateway/static/root/assets/management-settings-D7itDEHl.js +0 -1
- package/dist/gateway/static/root/assets/messages-DQ5X6cXp.js +0 -3
- package/dist/gateway/static/root/assets/notes-workbench-epHGeKd5.js +0 -4
- package/dist/gateway/static/root/assets/page-tabs-HXweXqYE.js +0 -1
- package/dist/gateway/static/root/assets/preview-open-alternatives-Dob6Oe2l.js +0 -1
- package/dist/gateway/static/root/assets/project-detail-page-BxOD5-zj.js +0 -1
- package/dist/gateway/static/root/assets/projects-page-D-9AhrH5.js +0 -1
- package/dist/gateway/static/root/assets/remote-access-hub-CSUNXLKM.js +0 -1
- package/dist/gateway/static/root/assets/schema-form-BI4rVt3u.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-BxOUBMca.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-DAvTIbgw.js +0 -1
- package/dist/gateway/static/root/assets/settings-form-section-C7rzhi7x.js +0 -1
- package/dist/gateway/static/root/assets/settings-page-layout-CTMmZcwj.js +0 -1
- package/dist/gateway/static/root/assets/setup-status-panel-D1v-2Ci-.js +0 -1
- package/dist/gateway/static/root/assets/share-preview-page-BeCxg6eb.js +0 -2
- package/dist/gateway/static/root/assets/shares-settings-CXL1EldF.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-BxwkKgVw.js +0 -1
- package/dist/gateway/static/root/assets/skills-page-CyfjEM4D.js +0 -2
- package/dist/gateway/static/root/assets/system-settings-panel-CAv0Yk0Y.js +0 -1
- package/dist/gateway/static/root/assets/task-detail-page-DGTVJs_N.js +0 -1
- package/dist/gateway/static/root/assets/typed-models-lib-DomX3Szl.js +0 -1
- package/dist/gateway/static/root/assets/use-autosave-BLc4gfG0.js +0 -1
- package/dist/gateway/static/root/assets/user-context-page-C2kQENTK.js +0 -1
- package/dist/gateway/static/root/assets/voice-api-key-field-BijmpPjb.js +0 -1
- package/dist/gateway/static/root/assets/work-discovery-overlay-D7w2JFFq.js +0 -1
- package/dist/gateway/static/root/assets/work-discovery-page-XtoPgPUS.js +0 -1
- package/dist/gateway/static/root/assets/workflows-page-D_K2xOWq.js +0 -5
- package/dist/src/gateway/ensure-network-cors.d.ts +0 -3
- package/dist/src/gateway/ensure-network-cors.js +0 -23
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { validateWebchatContent } from "../../chat-limits.js";
|
|
2
|
+
import { validateSideChatSelections } from "../../side-chat/context-snapshot.js";
|
|
3
|
+
import { SideChatError, parseThinkingLevel } from "../../side-chat/manager.js";
|
|
4
|
+
import "../../side-chat/index.js";
|
|
5
|
+
import { createGatewayRouteLogger, logRouteError } from "../lib/route-logger.js";
|
|
6
|
+
//#region src/gateway/hono/routes/side-chats.ts
|
|
7
|
+
const CLIENT_HEADER = "x-xopc-client-instance-id";
|
|
8
|
+
const log = createGatewayRouteLogger("SideChats");
|
|
9
|
+
function registerSideChatRoutes(authenticated, deps) {
|
|
10
|
+
const { service } = deps;
|
|
11
|
+
authenticated.post("/api/sessions/:parentSessionKey/side-chats", deps.chatRateLimitMiddleware, async (c) => {
|
|
12
|
+
try {
|
|
13
|
+
const body = await c.req.json().catch(() => ({}));
|
|
14
|
+
const clientInstanceId = readClientInstanceId(c, body.clientInstanceId);
|
|
15
|
+
if (body.modelRef !== void 0 && typeof body.modelRef !== "string") throw new SideChatError("modelRef must be a string", "INVALID_REQUEST");
|
|
16
|
+
const modelRef = typeof body.modelRef === "string" ? body.modelRef : void 0;
|
|
17
|
+
let selections;
|
|
18
|
+
try {
|
|
19
|
+
selections = validateSideChatSelections(body.selections);
|
|
20
|
+
} catch (error) {
|
|
21
|
+
throw new SideChatError(error instanceof Error ? error.message : "Invalid selections", "INVALID_REQUEST");
|
|
22
|
+
}
|
|
23
|
+
const sideChat = await service.sideChats.create({
|
|
24
|
+
parentSessionKey: c.req.param("parentSessionKey"),
|
|
25
|
+
clientInstanceId,
|
|
26
|
+
selections,
|
|
27
|
+
config: {
|
|
28
|
+
modelRef,
|
|
29
|
+
thinkingLevel: parseThinkingLevel(body.thinkingLevel)
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
return c.json({
|
|
33
|
+
ok: true,
|
|
34
|
+
sideChat
|
|
35
|
+
}, 201);
|
|
36
|
+
} catch (error) {
|
|
37
|
+
return respondSideChatError(c, error);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
authenticated.get("/api/side-chats/:sideChatId", (c) => {
|
|
41
|
+
try {
|
|
42
|
+
const sideChat = service.sideChats.get(c.req.param("sideChatId"), readClientInstanceId(c));
|
|
43
|
+
return c.json({
|
|
44
|
+
ok: true,
|
|
45
|
+
sideChat
|
|
46
|
+
});
|
|
47
|
+
} catch (error) {
|
|
48
|
+
return respondSideChatError(c, error);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
authenticated.get("/api/side-chats/:sideChatId/messages", (c) => {
|
|
52
|
+
try {
|
|
53
|
+
const messages = service.sideChats.getMessages(c.req.param("sideChatId"), readClientInstanceId(c));
|
|
54
|
+
return c.json({
|
|
55
|
+
ok: true,
|
|
56
|
+
messages
|
|
57
|
+
});
|
|
58
|
+
} catch (error) {
|
|
59
|
+
return respondSideChatError(c, error);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
authenticated.patch("/api/side-chats/:sideChatId/config", async (c) => {
|
|
63
|
+
try {
|
|
64
|
+
const body = await c.req.json().catch(() => ({}));
|
|
65
|
+
if (body.modelRef !== void 0 && typeof body.modelRef !== "string") throw new SideChatError("modelRef must be a string", "INVALID_REQUEST");
|
|
66
|
+
const modelRef = typeof body.modelRef === "string" ? body.modelRef : void 0;
|
|
67
|
+
const sideChat = service.sideChats.updateConfig(c.req.param("sideChatId"), readClientInstanceId(c, body.clientInstanceId), {
|
|
68
|
+
modelRef,
|
|
69
|
+
thinkingLevel: parseThinkingLevel(body.thinkingLevel)
|
|
70
|
+
});
|
|
71
|
+
return c.json({
|
|
72
|
+
ok: true,
|
|
73
|
+
sideChat
|
|
74
|
+
});
|
|
75
|
+
} catch (error) {
|
|
76
|
+
return respondSideChatError(c, error);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
authenticated.post("/api/side-chats/:sideChatId/heartbeat", (c) => {
|
|
80
|
+
try {
|
|
81
|
+
const sideChat = service.sideChats.heartbeat(c.req.param("sideChatId"), readClientInstanceId(c));
|
|
82
|
+
return c.json({
|
|
83
|
+
ok: true,
|
|
84
|
+
sideChat
|
|
85
|
+
});
|
|
86
|
+
} catch (error) {
|
|
87
|
+
return respondSideChatError(c, error);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
authenticated.post("/api/side-chats/:sideChatId/inputs", deps.chatRateLimitMiddleware, async (c) => {
|
|
91
|
+
try {
|
|
92
|
+
const body = await c.req.json().catch(() => ({}));
|
|
93
|
+
const content = typeof body.content === "string" ? body.content : "";
|
|
94
|
+
const contentError = validateWebchatContent(content);
|
|
95
|
+
if (contentError) throw new SideChatError(contentError, "INVALID_REQUEST");
|
|
96
|
+
const result = service.sideChatRuns.submit(c.req.param("sideChatId"), readClientInstanceId(c, body.clientInstanceId), content);
|
|
97
|
+
return c.json({
|
|
98
|
+
ok: true,
|
|
99
|
+
payload: result
|
|
100
|
+
}, 202);
|
|
101
|
+
} catch (error) {
|
|
102
|
+
return respondSideChatError(c, error);
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
authenticated.post("/api/side-chats/:sideChatId/abort", deps.chatRateLimitMiddleware, async (c) => {
|
|
106
|
+
try {
|
|
107
|
+
const body = await c.req.json().catch(() => ({}));
|
|
108
|
+
const aborted = await service.sideChatRuns.abort(c.req.param("sideChatId"), readClientInstanceId(c, body.clientInstanceId), typeof body.runId === "string" ? body.runId : void 0);
|
|
109
|
+
return c.json({
|
|
110
|
+
ok: true,
|
|
111
|
+
payload: { aborted }
|
|
112
|
+
});
|
|
113
|
+
} catch (error) {
|
|
114
|
+
return respondSideChatError(c, error);
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
authenticated.post("/api/side-chats/:sideChatId/clarify/:requestId", deps.chatRateLimitMiddleware, async (c) => {
|
|
118
|
+
try {
|
|
119
|
+
const body = await c.req.json().catch(() => ({}));
|
|
120
|
+
const answer = body.skip === true ? "" : typeof body.answer === "string" ? body.answer.trim() : "";
|
|
121
|
+
if (body.skip !== true && !answer) throw new SideChatError("answer is required", "INVALID_REQUEST");
|
|
122
|
+
if (!service.sideChatRuns.submitClarification(c.req.param("sideChatId"), readClientInstanceId(c, body.clientInstanceId), c.req.param("requestId"), answer)) throw new SideChatError("Clarification request not found", "NOT_FOUND");
|
|
123
|
+
return c.json({ ok: true });
|
|
124
|
+
} catch (error) {
|
|
125
|
+
return respondSideChatError(c, error);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
authenticated.delete("/api/side-chats/:sideChatId", async (c) => {
|
|
129
|
+
try {
|
|
130
|
+
return await service.sideChatRuns.dispose(c.req.param("sideChatId"), readClientInstanceId(c)) ? c.json({ ok: true }) : c.json({
|
|
131
|
+
ok: false,
|
|
132
|
+
error: "Side chat not found",
|
|
133
|
+
code: "NOT_FOUND"
|
|
134
|
+
}, 404);
|
|
135
|
+
} catch (error) {
|
|
136
|
+
return respondSideChatError(c, error);
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
authenticated.delete("/api/side-chats", async (c) => {
|
|
140
|
+
try {
|
|
141
|
+
const removed = await service.sideChats.disposeClient(readClientInstanceId(c, c.req.query("clientInstanceId")));
|
|
142
|
+
return c.json({
|
|
143
|
+
ok: true,
|
|
144
|
+
removed
|
|
145
|
+
});
|
|
146
|
+
} catch (error) {
|
|
147
|
+
return respondSideChatError(c, error);
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
function readClientInstanceId(c, fallback) {
|
|
152
|
+
const value = c.req.header(CLIENT_HEADER) || (typeof fallback === "string" ? fallback : "");
|
|
153
|
+
if (!value.trim()) throw new SideChatError("clientInstanceId is required", "INVALID_REQUEST");
|
|
154
|
+
return value.trim();
|
|
155
|
+
}
|
|
156
|
+
function respondSideChatError(c, error) {
|
|
157
|
+
if (!(error instanceof SideChatError)) {
|
|
158
|
+
logRouteError(log, c, error, "Side chat request failed");
|
|
159
|
+
return c.json({
|
|
160
|
+
ok: false,
|
|
161
|
+
error: "Side chat request failed",
|
|
162
|
+
code: "INTERNAL_ERROR"
|
|
163
|
+
}, 500);
|
|
164
|
+
}
|
|
165
|
+
const status = error.code === "NOT_FOUND" ? 404 : error.code === "CONFLICT" ? 409 : error.code === "LIMIT_REACHED" ? 429 : 400;
|
|
166
|
+
return c.json({
|
|
167
|
+
ok: false,
|
|
168
|
+
error: error.message,
|
|
169
|
+
code: error.code
|
|
170
|
+
}, status);
|
|
171
|
+
}
|
|
172
|
+
//#endregion
|
|
173
|
+
export { registerSideChatRoutes };
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
import { runSqliteWriteTransaction } from "../../../storage/sqlite/transaction.js";
|
|
1
2
|
import { TaskRepository } from "../../../tasks/task-repository.js";
|
|
2
3
|
import { TaskRunRepository } from "../../../tasks/task-run-repository.js";
|
|
3
4
|
import { TaskReadModelProjector } from "../../../tasks/task-read-model-projector.js";
|
|
4
5
|
import { TaskPhaseSchema } from "../../../../packages/gateway-contract/src/task-lifecycle.js";
|
|
5
|
-
import { TaskCommandRequestSchema, TaskContextInputSchema, TaskCreateRequestSchema, TaskDependencyUpdateRequestSchema, TaskPatchRequestSchema } from "../../../../packages/gateway-contract/src/tasks.js";
|
|
6
|
+
import { TaskBoardPositionRequestSchema, TaskCommandRequestSchema, TaskContextInputSchema, TaskCreateRequestSchema, TaskDependencyUpdateRequestSchema, TaskPatchRequestSchema } from "../../../../packages/gateway-contract/src/tasks.js";
|
|
6
7
|
import { ProjectMonitoringUpdateSchema } from "../../../../packages/gateway-contract/src/project-monitoring.js";
|
|
7
8
|
import { TaskContextRepository } from "../../../tasks/task-context-repository.js";
|
|
9
|
+
import { enqueueTaskChangedEvent } from "../../../tasks/task-change-events.js";
|
|
8
10
|
import { TaskDependencyError, TaskDependencyService } from "../../../tasks/task-dependency-service.js";
|
|
9
11
|
import { TaskApplicationService } from "../../../tasks/task-application-service.js";
|
|
10
12
|
import { ProjectMonitoringService } from "../../../tasks/project-monitoring-service.js";
|
|
@@ -13,6 +15,7 @@ import { TaskValueMetricsService } from "../../../tasks/task-value-metrics-servi
|
|
|
13
15
|
import { TaskSignalService } from "../../../tasks/task-signal-service.js";
|
|
14
16
|
//#region src/gateway/hono/routes/tasks.ts
|
|
15
17
|
function registerTaskRoutes(authenticated, deps) {
|
|
18
|
+
const taskRateLimit = deps.taskRateLimitMiddleware ?? deps.strictRateLimitMiddleware;
|
|
16
19
|
const application = new TaskApplicationService();
|
|
17
20
|
const operatingViews = new ProjectOperatingViewService(deps.service.projects);
|
|
18
21
|
const monitoring = new ProjectMonitoringService();
|
|
@@ -42,7 +45,7 @@ function registerTaskRoutes(authenticated, deps) {
|
|
|
42
45
|
})
|
|
43
46
|
});
|
|
44
47
|
});
|
|
45
|
-
authenticated.post("/api/tasks",
|
|
48
|
+
authenticated.post("/api/tasks", taskRateLimit, async (c) => {
|
|
46
49
|
const body = await c.req.json().catch(() => ({}));
|
|
47
50
|
const parsed = TaskCreateRequestSchema.safeParse(body);
|
|
48
51
|
if (!parsed.success) return c.json({
|
|
@@ -57,6 +60,7 @@ function registerTaskRoutes(authenticated, deps) {
|
|
|
57
60
|
error: created.reason
|
|
58
61
|
}, 409);
|
|
59
62
|
if (created.runId) deps.service.dispatchTaskRuns();
|
|
63
|
+
else deps.service.dispatchTaskEvents();
|
|
60
64
|
return c.json({
|
|
61
65
|
ok: true,
|
|
62
66
|
task: created.model.task,
|
|
@@ -97,33 +101,57 @@ function registerTaskRoutes(authenticated, deps) {
|
|
|
97
101
|
allowedCommands: model.allowedCommands
|
|
98
102
|
});
|
|
99
103
|
});
|
|
100
|
-
authenticated.patch("/api/tasks/:id",
|
|
104
|
+
authenticated.patch("/api/tasks/:id", taskRateLimit, async (c) => {
|
|
101
105
|
const parsed = TaskPatchRequestSchema.safeParse(await c.req.json().catch(() => ({})));
|
|
102
106
|
if (!parsed.success) return c.json({
|
|
103
107
|
ok: false,
|
|
104
108
|
error: "Invalid task patch"
|
|
105
109
|
}, 400);
|
|
106
|
-
const updated =
|
|
110
|
+
const updated = runSqliteWriteTransaction((db) => {
|
|
111
|
+
const task = tasks.update(c.req.param("id"), parsed.data);
|
|
112
|
+
if (!task) return void 0;
|
|
113
|
+
const changedFields = Object.keys(parsed.data).filter((field) => field !== "expectedVersion");
|
|
114
|
+
enqueueTaskChangedEvent(db, {
|
|
115
|
+
taskId: task.id,
|
|
116
|
+
projectId: task.projectId,
|
|
117
|
+
version: task.version,
|
|
118
|
+
changedFields,
|
|
119
|
+
actor: { kind: "user" }
|
|
120
|
+
});
|
|
121
|
+
return task;
|
|
122
|
+
});
|
|
107
123
|
if (!updated) return c.json({
|
|
108
124
|
ok: false,
|
|
109
125
|
error: "Task changed or was not found"
|
|
110
126
|
}, 409);
|
|
127
|
+
deps.service.dispatchTaskEvents();
|
|
111
128
|
return c.json({
|
|
112
129
|
ok: true,
|
|
113
130
|
task: updated
|
|
114
131
|
});
|
|
115
132
|
});
|
|
116
|
-
authenticated.put("/api/tasks/:id/dependencies",
|
|
133
|
+
authenticated.put("/api/tasks/:id/dependencies", taskRateLimit, async (c) => {
|
|
117
134
|
const parsed = TaskDependencyUpdateRequestSchema.safeParse(await c.req.json().catch(() => ({})));
|
|
118
135
|
if (!parsed.success) return c.json({
|
|
119
136
|
ok: false,
|
|
120
137
|
error: "Invalid task dependencies"
|
|
121
138
|
}, 400);
|
|
122
139
|
try {
|
|
123
|
-
const task =
|
|
124
|
-
|
|
125
|
-
|
|
140
|
+
const task = runSqliteWriteTransaction((db) => {
|
|
141
|
+
const changed = dependencies.replace({
|
|
142
|
+
taskId: c.req.param("id"),
|
|
143
|
+
...parsed.data
|
|
144
|
+
});
|
|
145
|
+
enqueueTaskChangedEvent(db, {
|
|
146
|
+
taskId: changed.id,
|
|
147
|
+
projectId: changed.projectId,
|
|
148
|
+
version: changed.version,
|
|
149
|
+
changedFields: ["dependencies"],
|
|
150
|
+
actor: { kind: "user" }
|
|
151
|
+
});
|
|
152
|
+
return changed;
|
|
126
153
|
});
|
|
154
|
+
deps.service.dispatchTaskEvents();
|
|
127
155
|
return c.json({
|
|
128
156
|
ok: true,
|
|
129
157
|
task,
|
|
@@ -140,7 +168,45 @@ function registerTaskRoutes(authenticated, deps) {
|
|
|
140
168
|
}, status);
|
|
141
169
|
}
|
|
142
170
|
});
|
|
143
|
-
authenticated.
|
|
171
|
+
authenticated.put("/api/tasks/:id/board-position", taskRateLimit, async (c) => {
|
|
172
|
+
const parsed = TaskBoardPositionRequestSchema.safeParse(await c.req.json().catch(() => ({})));
|
|
173
|
+
if (!parsed.success) return c.json({
|
|
174
|
+
ok: false,
|
|
175
|
+
error: "Invalid task board position"
|
|
176
|
+
}, 400);
|
|
177
|
+
try {
|
|
178
|
+
const task = runSqliteWriteTransaction((db) => {
|
|
179
|
+
const reordered = tasks.reorder({
|
|
180
|
+
taskId: c.req.param("id"),
|
|
181
|
+
...parsed.data
|
|
182
|
+
});
|
|
183
|
+
if (!reordered) return void 0;
|
|
184
|
+
enqueueTaskChangedEvent(db, {
|
|
185
|
+
taskId: reordered.id,
|
|
186
|
+
projectId: reordered.projectId,
|
|
187
|
+
version: reordered.version,
|
|
188
|
+
changedFields: ["boardRank"],
|
|
189
|
+
actor: { kind: "user" }
|
|
190
|
+
});
|
|
191
|
+
return reordered;
|
|
192
|
+
});
|
|
193
|
+
if (!task) return c.json({
|
|
194
|
+
ok: false,
|
|
195
|
+
error: "Task changed or was not found"
|
|
196
|
+
}, 409);
|
|
197
|
+
deps.service.dispatchTaskEvents();
|
|
198
|
+
return c.json({
|
|
199
|
+
ok: true,
|
|
200
|
+
task
|
|
201
|
+
});
|
|
202
|
+
} catch (error) {
|
|
203
|
+
return c.json({
|
|
204
|
+
ok: false,
|
|
205
|
+
error: error instanceof Error ? error.message : String(error)
|
|
206
|
+
}, 400);
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
authenticated.post("/api/tasks/:id/commands", taskRateLimit, async (c) => {
|
|
144
210
|
const parsed = TaskCommandRequestSchema.safeParse(await c.req.json().catch(() => ({})));
|
|
145
211
|
if (!parsed.success) return c.json({
|
|
146
212
|
ok: false,
|
|
@@ -165,6 +231,7 @@ function registerTaskRoutes(authenticated, deps) {
|
|
|
165
231
|
}, 409);
|
|
166
232
|
}
|
|
167
233
|
if (result.runId || parsed.data.command?.type === "resolve_wait") deps.service.dispatchTaskRuns();
|
|
234
|
+
else deps.service.dispatchTaskEvents();
|
|
168
235
|
if (parsed.data.command?.type === "close" && parsed.data.command.resolution === "done") signals.dependencyClosed(c.req.param("id"));
|
|
169
236
|
return c.json({
|
|
170
237
|
ok: true,
|
|
@@ -172,7 +239,7 @@ function registerTaskRoutes(authenticated, deps) {
|
|
|
172
239
|
...result.runId ? { run: runs.get(result.runId) } : {}
|
|
173
240
|
});
|
|
174
241
|
});
|
|
175
|
-
authenticated.post("/api/tasks/:id/context",
|
|
242
|
+
authenticated.post("/api/tasks/:id/context", taskRateLimit, async (c) => {
|
|
176
243
|
const task = tasks.get(c.req.param("id"));
|
|
177
244
|
if (!task) return c.json({
|
|
178
245
|
ok: false,
|
|
@@ -183,17 +250,42 @@ function registerTaskRoutes(authenticated, deps) {
|
|
|
183
250
|
ok: false,
|
|
184
251
|
error: "Invalid task context edge"
|
|
185
252
|
}, 400);
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
edge: context.add({
|
|
253
|
+
const edge = runSqliteWriteTransaction((db) => {
|
|
254
|
+
const created = context.add({
|
|
189
255
|
taskId: task.id,
|
|
190
256
|
...parsed.data,
|
|
191
257
|
createdBy: { kind: "user" }
|
|
192
|
-
})
|
|
258
|
+
});
|
|
259
|
+
enqueueTaskChangedEvent(db, {
|
|
260
|
+
taskId: task.id,
|
|
261
|
+
projectId: task.projectId,
|
|
262
|
+
version: task.version,
|
|
263
|
+
changedFields: ["context"],
|
|
264
|
+
actor: { kind: "user" }
|
|
265
|
+
});
|
|
266
|
+
return created;
|
|
267
|
+
});
|
|
268
|
+
deps.service.dispatchTaskEvents();
|
|
269
|
+
return c.json({
|
|
270
|
+
ok: true,
|
|
271
|
+
edge
|
|
193
272
|
}, 201);
|
|
194
273
|
});
|
|
195
|
-
authenticated.delete("/api/tasks/:id/context/:edgeId",
|
|
196
|
-
|
|
274
|
+
authenticated.delete("/api/tasks/:id/context/:edgeId", taskRateLimit, (c) => {
|
|
275
|
+
const task = tasks.get(c.req.param("id"));
|
|
276
|
+
const removed = task ? runSqliteWriteTransaction((db) => {
|
|
277
|
+
const didRemove = context.remove(task.id, c.req.param("edgeId"));
|
|
278
|
+
if (didRemove) enqueueTaskChangedEvent(db, {
|
|
279
|
+
taskId: task.id,
|
|
280
|
+
projectId: task.projectId,
|
|
281
|
+
version: task.version,
|
|
282
|
+
changedFields: ["context"],
|
|
283
|
+
actor: { kind: "user" }
|
|
284
|
+
});
|
|
285
|
+
return didRemove;
|
|
286
|
+
}) : false;
|
|
287
|
+
if (removed) deps.service.dispatchTaskEvents();
|
|
288
|
+
return removed ? c.json({ ok: true }) : c.json({
|
|
197
289
|
ok: false,
|
|
198
290
|
error: "Task context edge not found"
|
|
199
291
|
}, 404);
|
|
@@ -219,7 +311,7 @@ function registerTaskRoutes(authenticated, deps) {
|
|
|
219
311
|
error: "TaskRun not found"
|
|
220
312
|
}, 404);
|
|
221
313
|
});
|
|
222
|
-
authenticated.post("/api/task-runs/:runId/cancel",
|
|
314
|
+
authenticated.post("/api/task-runs/:runId/cancel", taskRateLimit, async (c) => {
|
|
223
315
|
const body = await c.req.json().catch(() => ({}));
|
|
224
316
|
const run = runs.get(c.req.param("runId"));
|
|
225
317
|
if (!run) return c.json({
|
|
@@ -256,13 +348,14 @@ function registerTaskRoutes(authenticated, deps) {
|
|
|
256
348
|
ok: false,
|
|
257
349
|
error: result.reason
|
|
258
350
|
}, 409);
|
|
351
|
+
deps.service.dispatchTaskEvents();
|
|
259
352
|
return c.json({
|
|
260
353
|
ok: true,
|
|
261
354
|
run: runs.get(run.id),
|
|
262
355
|
receipt: runs.getReceipt(run.id)
|
|
263
356
|
});
|
|
264
357
|
});
|
|
265
|
-
authenticated.post("/api/task-runs/:runId/feedback",
|
|
358
|
+
authenticated.post("/api/task-runs/:runId/feedback", taskRateLimit, async (c) => {
|
|
266
359
|
const body = await c.req.json().catch(() => ({}));
|
|
267
360
|
if (body.rating !== "helpful" && body.rating !== "not_helpful") return c.json({
|
|
268
361
|
ok: false,
|
|
@@ -306,7 +399,7 @@ function registerTaskRoutes(authenticated, deps) {
|
|
|
306
399
|
policy: monitoring.get(projectId)
|
|
307
400
|
});
|
|
308
401
|
});
|
|
309
|
-
authenticated.patch("/api/projects/:projectId/monitoring",
|
|
402
|
+
authenticated.patch("/api/projects/:projectId/monitoring", taskRateLimit, async (c) => {
|
|
310
403
|
const projectId = c.req.param("projectId");
|
|
311
404
|
if (!deps.service.projects.get(projectId)) return c.json({
|
|
312
405
|
ok: false,
|
|
@@ -6,6 +6,13 @@ export declare function isAllInterfacesHost(host: string | undefined): boolean;
|
|
|
6
6
|
export declare const GATEWAY_DEV_CONSOLE_ORIGINS: readonly ["http://localhost:3000", "http://127.0.0.1:3000"];
|
|
7
7
|
/** Expo / React Native web dev server (Metro defaults to port 8081). */
|
|
8
8
|
export declare const GATEWAY_EXPO_DEV_ORIGINS: readonly ["http://localhost:8081", "http://127.0.0.1:8081"];
|
|
9
|
+
export type LanGatewayCandidate = {
|
|
10
|
+
url: string;
|
|
11
|
+
address: string;
|
|
12
|
+
interfaceName: string;
|
|
13
|
+
};
|
|
14
|
+
/** Non-internal IPv4 addresses that can serve this gateway on the local network. */
|
|
15
|
+
export declare function enumerateLanGatewayCandidates(port: number): LanGatewayCandidate[];
|
|
9
16
|
/** Effective HTTP listen port: CLI `--port` override wins over config (default 18790). */
|
|
10
17
|
export declare function resolveEffectiveGatewayPort(config: {
|
|
11
18
|
gateway?: {
|
|
@@ -27,7 +34,8 @@ export declare function buildDefaultCorsOrigins(params: {
|
|
|
27
34
|
}): string[];
|
|
28
35
|
/**
|
|
29
36
|
* Effective browser origins for CORS and CSRF checks.
|
|
30
|
-
*
|
|
37
|
+
* Configured origins extend the gateway's own browser origins instead of replacing them.
|
|
38
|
+
* This keeps the bundled console reachable while allowing explicit external frontends.
|
|
31
39
|
*/
|
|
32
40
|
export declare function resolveGatewayCorsOrigins(params: {
|
|
33
41
|
configuredOrigins?: string[];
|
package/dist/src/gateway/host.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import "../daemon/constants.js";
|
|
2
|
+
import { networkInterfaces } from "node:os";
|
|
2
3
|
//#region src/gateway/host.ts
|
|
3
4
|
/** True when the bind address is local-only (127.x, localhost, ::1). */
|
|
4
5
|
function isLoopbackHost(host) {
|
|
@@ -17,6 +18,21 @@ const GATEWAY_DEV_CONSOLE_ORIGINS = ["http://localhost:3000", "http://127.0.0.1:
|
|
|
17
18
|
/** Expo / React Native web dev server (Metro defaults to port 8081). */
|
|
18
19
|
const GATEWAY_EXPO_DEV_ORIGINS = ["http://localhost:8081", "http://127.0.0.1:8081"];
|
|
19
20
|
const GATEWAY_LOOPBACK_DEV_ORIGINS = [...GATEWAY_DEV_CONSOLE_ORIGINS, ...GATEWAY_EXPO_DEV_ORIGINS];
|
|
21
|
+
/** Non-internal IPv4 addresses that can serve this gateway on the local network. */
|
|
22
|
+
function enumerateLanGatewayCandidates(port) {
|
|
23
|
+
const seen = /* @__PURE__ */ new Set();
|
|
24
|
+
const candidates = [];
|
|
25
|
+
for (const [interfaceName, addresses] of Object.entries(networkInterfaces())) for (const address of addresses ?? []) {
|
|
26
|
+
if (address.family !== "IPv4" || address.internal || seen.has(address.address)) continue;
|
|
27
|
+
seen.add(address.address);
|
|
28
|
+
candidates.push({
|
|
29
|
+
address: address.address,
|
|
30
|
+
interfaceName,
|
|
31
|
+
url: `http://${address.address}:${port}`
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
return candidates;
|
|
35
|
+
}
|
|
20
36
|
/** Effective HTTP listen port: CLI `--port` override wins over config (default 18790). */
|
|
21
37
|
function resolveEffectiveGatewayPort(config, listenPortOverride) {
|
|
22
38
|
if (typeof listenPortOverride === "number" && Number.isFinite(listenPortOverride)) return listenPortOverride;
|
|
@@ -35,19 +51,20 @@ function buildDefaultCorsOrigins(params) {
|
|
|
35
51
|
]);
|
|
36
52
|
const bindHost = params.bindHost?.trim();
|
|
37
53
|
if (bindHost && !isLoopbackHost(bindHost) && !isAllInterfacesHost(bindHost)) origins.add(`http://${bindHost}:${params.port}`);
|
|
54
|
+
if (isAllInterfacesHost(bindHost)) for (const candidate of enumerateLanGatewayCandidates(params.port)) origins.add(candidate.url);
|
|
38
55
|
return [...origins];
|
|
39
56
|
}
|
|
40
57
|
/**
|
|
41
58
|
* Effective browser origins for CORS and CSRF checks.
|
|
42
|
-
*
|
|
59
|
+
* Configured origins extend the gateway's own browser origins instead of replacing them.
|
|
60
|
+
* This keeps the bundled console reachable while allowing explicit external frontends.
|
|
43
61
|
*/
|
|
44
62
|
function resolveGatewayCorsOrigins(params) {
|
|
45
63
|
const configured = (params.configuredOrigins ?? []).map((origin) => origin.trim()).filter(Boolean);
|
|
46
|
-
|
|
64
|
+
return [...new Set([...configured, ...buildDefaultCorsOrigins({
|
|
47
65
|
port: params.port,
|
|
48
66
|
bindHost: params.bindHost
|
|
49
|
-
});
|
|
50
|
-
return [...new Set([...configured, ...GATEWAY_LOOPBACK_DEV_ORIGINS])];
|
|
67
|
+
})])];
|
|
51
68
|
}
|
|
52
69
|
/** Browser origin (`https://host`) from a gateway public/tunnel root URL. */
|
|
53
70
|
function originFromGatewayPublicUrl(publicUrl) {
|
|
@@ -73,4 +90,4 @@ function resolveAllowedBrowserOrigins(params) {
|
|
|
73
90
|
return origins;
|
|
74
91
|
}
|
|
75
92
|
//#endregion
|
|
76
|
-
export { GATEWAY_DEV_CONSOLE_ORIGINS, GATEWAY_EXPO_DEV_ORIGINS, buildDefaultCorsOrigins, isAllInterfacesHost, isLoopbackHost, originFromGatewayPublicUrl, resolveAllowedBrowserOrigins, resolveEffectiveGatewayPort, resolveGatewayCorsOrigins, resolveGatewayServiceListenPort };
|
|
93
|
+
export { GATEWAY_DEV_CONSOLE_ORIGINS, GATEWAY_EXPO_DEV_ORIGINS, buildDefaultCorsOrigins, enumerateLanGatewayCandidates, isAllInterfacesHost, isLoopbackHost, originFromGatewayPublicUrl, resolveAllowedBrowserOrigins, resolveEffectiveGatewayPort, resolveGatewayCorsOrigins, resolveGatewayServiceListenPort };
|
|
@@ -36,6 +36,7 @@ declare class BucketRegistry {
|
|
|
36
36
|
private authFailureSignature?;
|
|
37
37
|
private strictApiLimiter?;
|
|
38
38
|
private chatApiLimiter?;
|
|
39
|
+
private taskApiLimiter?;
|
|
39
40
|
private xopcCloudPollLimiter?;
|
|
40
41
|
private channelApiLimiter?;
|
|
41
42
|
private tunnelMutateLimiter?;
|
|
@@ -52,6 +53,8 @@ declare class BucketRegistry {
|
|
|
52
53
|
strictApi(): RateLimiter;
|
|
53
54
|
/** Interactive chat actions — 180 req / 60 s per client IP. */
|
|
54
55
|
chatApi(): RateLimiter;
|
|
56
|
+
/** Interactive task and board mutations — 300 req / 60 s per client IP. */
|
|
57
|
+
taskApi(): RateLimiter;
|
|
55
58
|
/** XOPC Cloud authorization polling — 60 req / 60 s per client IP. */
|
|
56
59
|
xopcCloudPoll(): RateLimiter;
|
|
57
60
|
/** Channel mutation/action endpoints — 20 req / 60 s per client IP. */
|
|
@@ -48,6 +48,7 @@ var BucketRegistry = class {
|
|
|
48
48
|
authFailureSignature;
|
|
49
49
|
strictApiLimiter;
|
|
50
50
|
chatApiLimiter;
|
|
51
|
+
taskApiLimiter;
|
|
51
52
|
xopcCloudPollLimiter;
|
|
52
53
|
channelApiLimiter;
|
|
53
54
|
tunnelMutateLimiter;
|
|
@@ -89,6 +90,14 @@ var BucketRegistry = class {
|
|
|
89
90
|
});
|
|
90
91
|
return this.chatApiLimiter;
|
|
91
92
|
}
|
|
93
|
+
/** Interactive task and board mutations — 300 req / 60 s per client IP. */
|
|
94
|
+
taskApi() {
|
|
95
|
+
if (!this.taskApiLimiter) this.taskApiLimiter = new RateLimiter({
|
|
96
|
+
maxRequests: 300,
|
|
97
|
+
windowMs: 6e4
|
|
98
|
+
});
|
|
99
|
+
return this.taskApiLimiter;
|
|
100
|
+
}
|
|
92
101
|
/** XOPC Cloud authorization polling — 60 req / 60 s per client IP. */
|
|
93
102
|
xopcCloudPoll() {
|
|
94
103
|
if (!this.xopcCloudPollLimiter) this.xopcCloudPollLimiter = new RateLimiter({
|
|
@@ -142,6 +151,7 @@ var BucketRegistry = class {
|
|
|
142
151
|
this.authFailureLimiter?.destroy();
|
|
143
152
|
this.strictApiLimiter?.destroy();
|
|
144
153
|
this.chatApiLimiter?.destroy();
|
|
154
|
+
this.taskApiLimiter?.destroy();
|
|
145
155
|
this.xopcCloudPollLimiter?.destroy();
|
|
146
156
|
this.channelApiLimiter?.destroy();
|
|
147
157
|
this.tunnelMutateLimiter?.destroy();
|
|
@@ -151,6 +161,7 @@ var BucketRegistry = class {
|
|
|
151
161
|
this.authFailureLimiter = void 0;
|
|
152
162
|
this.strictApiLimiter = void 0;
|
|
153
163
|
this.chatApiLimiter = void 0;
|
|
164
|
+
this.taskApiLimiter = void 0;
|
|
154
165
|
this.xopcCloudPollLimiter = void 0;
|
|
155
166
|
this.channelApiLimiter = void 0;
|
|
156
167
|
this.tunnelMutateLimiter = void 0;
|
|
@@ -164,6 +175,7 @@ var BucketRegistry = class {
|
|
|
164
175
|
this.authFailureLimiter?.resetForTests();
|
|
165
176
|
this.strictApiLimiter?.resetForTests();
|
|
166
177
|
this.chatApiLimiter?.resetForTests();
|
|
178
|
+
this.taskApiLimiter?.resetForTests();
|
|
167
179
|
this.xopcCloudPollLimiter?.resetForTests();
|
|
168
180
|
this.channelApiLimiter?.resetForTests();
|
|
169
181
|
this.tunnelMutateLimiter?.resetForTests();
|
|
@@ -1,12 +1,9 @@
|
|
|
1
|
-
import { isAllInterfacesHost, isLoopbackHost } from "./host.js";
|
|
1
|
+
import { isAllInterfacesHost, isLoopbackHost, resolveGatewayCorsOrigins } from "./host.js";
|
|
2
2
|
import { isAuthRateLimitGloballyDisabled, isGatewayStrictSecurityEnabled } from "./rate-limit/env-flags.js";
|
|
3
3
|
import "./rate-limit/index.js";
|
|
4
4
|
import { resolveGatewayListenPlan } from "./listen.js";
|
|
5
5
|
import { assertTailscaleExposureCompatible } from "./tailscale-lifecycle.js";
|
|
6
6
|
//#region src/gateway/runtime-config.ts
|
|
7
|
-
function normalizeCorsOrigins(cfg) {
|
|
8
|
-
return (cfg.gateway?.corsOrigins ?? []).map((value) => value.trim()).filter(Boolean);
|
|
9
|
-
}
|
|
10
7
|
function hasSharedSecret(auth) {
|
|
11
8
|
if (auth.mode === "trusted-proxy") return true;
|
|
12
9
|
if (auth.mode === "token") return typeof auth.token === "string" && auth.token.trim().length > 0;
|
|
@@ -23,7 +20,11 @@ function assertGatewayRuntimeConfig(params) {
|
|
|
23
20
|
bindOverride: params.bindOverride
|
|
24
21
|
});
|
|
25
22
|
const loopback = isLoopbackHost(bindHost);
|
|
26
|
-
const corsOrigins =
|
|
23
|
+
const corsOrigins = resolveGatewayCorsOrigins({
|
|
24
|
+
configuredOrigins: params.cfg.gateway?.corsOrigins,
|
|
25
|
+
port: params.port,
|
|
26
|
+
bindHost
|
|
27
|
+
});
|
|
27
28
|
const dangerouslyAllowHostHeaderOriginFallback = params.cfg.gateway?.dangerouslyAllowHostHeaderOriginFallback === true;
|
|
28
29
|
if (bindMode === "loopback" && !loopback) throw new Error(`gateway bind=loopback resolved to non-loopback host ${bindHost}; refusing fallback to a network bind`);
|
|
29
30
|
if (bindMode === "custom") {
|
|
@@ -110,13 +110,6 @@ function collectGatewayConfigFindings(params) {
|
|
|
110
110
|
title: "Large number of CORS origins configured",
|
|
111
111
|
detail: `${params.corsOrigins.length} CORS origins configured. Review whether all are necessary.`
|
|
112
112
|
});
|
|
113
|
-
if (!loopback && (!params.corsOrigins || params.corsOrigins.length === 0) && params.dangerouslyAllowHostHeaderOriginFallback !== true) findings.push({
|
|
114
|
-
checkId: "gateway.cors.no_explicit_origins",
|
|
115
|
-
severity: "info",
|
|
116
|
-
title: "No custom CORS origins — loopback browsers only",
|
|
117
|
-
detail: "Gateway listens on a network address with empty gateway.corsOrigins. localhost and 127.0.0.1 on the gateway port are allowed by default; LAN or remote browser URLs must be added explicitly.",
|
|
118
|
-
remediation: "Add gateway.corsOrigins entries (e.g. http://192.168.x.x:<port>) for non-loopback browser access, or enable gateway.dangerouslyAllowHostHeaderOriginFallback only if you understand the CSRF risk."
|
|
119
|
-
});
|
|
120
113
|
if (!loopback && params.auth.mode !== "none" && params.auth.mode !== "trusted-proxy" && params.rateLimitEnabled === false) findings.push({
|
|
121
114
|
checkId: "gateway.auth.no_rate_limit",
|
|
122
115
|
severity: "warn",
|
|
@@ -51,7 +51,10 @@ var GatewayServer = class {
|
|
|
51
51
|
});
|
|
52
52
|
const { configureTunnelFromGatewayConfig } = await import("../tunnel/gateway-lifecycle.js");
|
|
53
53
|
await configureTunnelFromGatewayConfig(this.service.currentConfig, { deferWellKnownFetch: true });
|
|
54
|
-
const app = createHonoApp({
|
|
54
|
+
const app = createHonoApp({
|
|
55
|
+
service: this.service,
|
|
56
|
+
listenHost: bindHost
|
|
57
|
+
});
|
|
55
58
|
const primaryHost = listenHosts[0] ?? bindHost;
|
|
56
59
|
const attachUpgrade = (server) => {
|
|
57
60
|
server.on("upgrade", (req, socket, head) => {
|
|
@@ -49,6 +49,7 @@ export declare class GatewayAgentRunner {
|
|
|
49
49
|
private readonly clarifyBridge;
|
|
50
50
|
/** Maps webchat session key → active `runId` for `clarify` tool routing. */
|
|
51
51
|
private readonly activeWebchatRunBySession;
|
|
52
|
+
private readonly externalStreamBySession;
|
|
52
53
|
readonly inputs: SessionInputCoordinator;
|
|
53
54
|
constructor(opts: GatewayAgentRunnerOptions);
|
|
54
55
|
/** True when a webchat agent run is currently in-flight for `sessionKey`. */
|
|
@@ -57,6 +58,9 @@ export declare class GatewayAgentRunner {
|
|
|
57
58
|
getClarifyBridge(): ClarifyBridge;
|
|
58
59
|
/** Called from `GatewayService.stop()` so the bridge gets cleaned up. */
|
|
59
60
|
disposeClarifyBridge(): void;
|
|
61
|
+
registerExternalWebchatRun(sessionKey: string, runId: string, publish: (event: ClarifyStreamEvent) => void): void;
|
|
62
|
+
unregisterExternalWebchatRun(sessionKey: string, runId: string): void;
|
|
63
|
+
cancelClarificationForRun(runId: string): void;
|
|
60
64
|
runAgent(message: string, channel: string, chatId: string, origin: TurnOrigin, attachments?: UserTurnAttachment[], thinking?: string, runOptions?: {
|
|
61
65
|
signal?: AbortSignal;
|
|
62
66
|
runId?: string;
|