@xopcai/xopc 0.0.197 → 0.0.200
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-C5st7lt5.js → agent-avatar-dicebear-eMarz29u.js} +1 -1
- package/dist/gateway/static/root/assets/agents-DeLW-454.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-yfb68wq9.js +1 -0
- package/dist/gateway/static/root/assets/appearance-settings-B8SPknFM.js +1 -0
- package/dist/gateway/static/root/assets/apps-page-Bmor22Ny.js +1 -0
- package/dist/gateway/static/root/assets/{archive-plugin-qL07mwnY.js → archive-plugin-DmYRiOP3.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-CqSobpdi.js → attachment-load-D66JvcPi.js} +1 -1
- package/dist/gateway/static/root/assets/automations-page-Q4e8m11h.js +6 -0
- package/dist/gateway/static/root/assets/{binary-plugins-DBRPyLYE.js → binary-plugins-ChIsZArT.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-6wG1LU7t.js → block-editor-Ck-k_d0Z.js} +4 -4
- package/dist/gateway/static/root/assets/browser-settings-page--uVL73Sb.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-BEu3E0et.js → browser-workflows-page-BC5jm1zL.js} +2 -2
- package/dist/gateway/static/root/assets/button-CSEbl4zT.js +1 -0
- package/dist/gateway/static/root/assets/capabilities-settings-panel-DXpf3QYN.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-NAjCsrTi.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-CiWUo7jW.js +1 -0
- package/dist/gateway/static/root/assets/channels-status-swr-B4Rbh3bM.js +1 -0
- package/dist/gateway/static/root/assets/connectors-api-5xiCRVUS.js +1 -0
- package/dist/gateway/static/root/assets/connectors-page-DOwlMseU.js +2 -0
- package/dist/gateway/static/root/assets/copy-Dv6d4Dvw.js +1 -0
- package/dist/gateway/static/root/assets/cron-expression-B3PebXKZ.js +2 -0
- package/dist/gateway/static/root/assets/date-picker-C1Vke7Xn.js +1 -0
- package/dist/gateway/static/root/assets/dependency-picker-FrQ0tsBC.js +1 -0
- package/dist/gateway/static/root/assets/{desktop-pet-CsLR4pWB.js → desktop-pet-CPBAzNJi.js} +1 -1
- package/dist/gateway/static/root/assets/desktop-pet-settings-D6s3glDP.js +1 -0
- package/dist/gateway/static/root/assets/directory-picker-path-field-CbNQ7QQS.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-CD73euAM.js → extension-debug-page-dTK14hUv.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-DAKjpNwd.js → extension-page-Cpdj1Kip.js} +1 -1
- package/dist/gateway/static/root/assets/extension-settings-page-iNHUZGd5.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-SvMvWqMM.js +1 -0
- package/dist/gateway/static/root/assets/gateway-config-swr-jB0hAhWO.js +1 -0
- package/dist/gateway/static/root/assets/gateway-settings-BilR3dGZ.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-zK40dcQ5.js +1 -0
- package/dist/gateway/static/root/assets/home-page-NyXy5MLr.js +3 -0
- package/dist/gateway/static/root/assets/index-Bp8I_eX4.css +1 -0
- package/dist/gateway/static/root/assets/index-CCK8zdlS.js +152 -0
- package/dist/gateway/static/root/assets/interaction-BmQogeqz.js +1 -0
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-HMhxgOuD.js → keyboard-shortcuts-settings-nKzsroud.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-Ds3sPXTh.js +2 -0
- package/dist/gateway/static/root/assets/local-apps-page-DV1s_FYh.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-Biz4eA02.js +2 -0
- package/dist/gateway/static/root/assets/management-settings-D7KjYgHu.js +1 -0
- package/dist/gateway/static/root/assets/{markdown-split-DvVBewS4.js → markdown-split-CTYbpkrd.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-YVj7NVzD.js → media-plugins-BXYnaiZ6.js} +1 -1
- package/dist/gateway/static/root/assets/messages-3qRsLZuX.js +3 -0
- package/dist/gateway/static/root/assets/{notes-page-BDzuL2I9.js → notes-page-CWWVXpw5.js} +1 -1
- package/dist/gateway/static/root/assets/notes-workbench-DnBkQihH.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-pimQAkeI.js +1 -0
- package/dist/gateway/static/root/assets/{product-open-page-Bwx_CQht.js → product-open-page-BoYSS_YR.js} +1 -1
- package/dist/gateway/static/root/assets/project-detail-page-ZO7t1UkB.js +1 -0
- package/dist/gateway/static/root/assets/projects-page-By-vq41g.js +1 -0
- package/dist/gateway/static/root/assets/remote-access-hub-C0C_Y9d0.js +1 -0
- package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-Fe05Nus6.js → runtime-tools-settings-panel-DT8flaX1.js} +4 -4
- package/dist/gateway/static/root/assets/schema-form-DabEP-UI.js +1 -0
- package/dist/gateway/static/root/assets/sessions-page-BNlVwUzV.js +1 -0
- package/dist/gateway/static/root/assets/settings-advanced-gate-CxYBQDaM.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-DCsfZAtC.js +1 -0
- package/dist/gateway/static/root/assets/{settings-loading-skeleton-BRE_-7Da.js → settings-loading-skeleton-Dac6Yd0T.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-C1zxCpqB.js → settings-page-BDoYoEar.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-DWijJ1rw.js +1 -0
- package/dist/gateway/static/root/assets/share-preview-page-ie9I2_7h.js +2 -0
- package/dist/gateway/static/root/assets/shares-settings-DU9VrJLf.js +1 -0
- package/dist/gateway/static/root/assets/skill-reload-api-C5RaIvoE.js +1 -0
- package/dist/gateway/static/root/assets/skills-page-N0YhaR5S.js +2 -0
- package/dist/gateway/static/root/assets/{skills-page.utils-CYwFWTso.js → skills-page.utils-C639DLXr.js} +1 -1
- package/dist/gateway/static/root/assets/system-settings-panel-CpshpQtC.js +1 -0
- package/dist/gateway/static/root/assets/task-detail-page-DsTocYkK.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-ms6rDy59.js +1 -0
- package/dist/gateway/static/root/assets/user-context-page-BUxIemwy.js +1 -0
- package/dist/gateway/static/root/assets/voice-api-key-field-CObrQWh2.js +1 -0
- package/dist/gateway/static/root/assets/work-discovery-overlay-BBxrnEFt.js +1 -0
- package/dist/gateway/static/root/assets/work-discovery-page-DN3l-HW3.js +1 -0
- package/dist/gateway/static/root/assets/{workflow-run-setup-panel-DqIb1oBB.js → workflow-run-setup-panel-BxXhe3IV.js} +2 -2
- package/dist/gateway/static/root/assets/workflows-page-BCHkjd1i.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 +103 -9
- package/dist/src/agent/agent-manager.d.ts +1 -0
- package/dist/src/agent/agent-manager.js +1 -0
- package/dist/src/agent/context/execution-scope.js +3 -1
- package/dist/src/agent/service.js +1 -0
- package/dist/src/agent/service.types.d.ts +1 -0
- package/dist/src/agent/tasks/task-judge-service.d.ts +1 -1
- package/dist/src/agent/tasks/task-judge-service.js +3 -2
- 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 +69 -9
- package/dist/src/automations/domain/validation.d.ts +40 -12
- package/dist/src/automations/draft/automation-draft-validator.d.ts +4 -4
- package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
- package/dist/src/config/rules.js +2 -2
- 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/agent-stream.js +2 -74
- 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/projects.js +55 -0
- package/dist/src/gateway/hono/routes/session-input-handler.d.ts +62 -0
- package/dist/src/gateway/hono/routes/session-input-handler.js +82 -0
- package/dist/src/gateway/hono/routes/tasks.js +272 -20
- 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/task-conversation-query-service.d.ts +58 -0
- package/dist/src/gateway/service/task-conversation-query-service.js +84 -0
- package/dist/src/gateway/service.d.ts +11 -0
- package/dist/src/gateway/service.js +75 -38
- 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/projects/project-agent.js +1 -1
- 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/120_task_conversation.sql +112 -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/execution-context.js +8 -4
- package/dist/src/tasks/home-query-service.d.ts +5 -0
- package/dist/src/tasks/home-query-service.js +7 -2
- package/dist/src/tasks/index.d.ts +3 -0
- package/dist/src/tasks/index.js +6 -3
- 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 +1 -2
- package/dist/src/tasks/project-operating-view-service.js +26 -23
- package/dist/src/tasks/task-application-service.js +107 -21
- 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-context-assembler.js +9 -4
- package/dist/src/tasks/task-context-repository.d.ts +0 -6
- package/dist/src/tasks/task-context-repository.js +0 -4
- package/dist/src/tasks/task-conversation-repository.d.ts +71 -0
- package/dist/src/tasks/task-conversation-repository.js +194 -0
- package/dist/src/tasks/task-handoff-service.d.ts +31 -0
- package/dist/src/tasks/task-handoff-service.js +143 -0
- package/dist/src/tasks/task-read-model-projector.js +3 -6
- package/dist/src/tasks/task-repository.d.ts +7 -2
- package/dist/src/tasks/task-repository.js +31 -15
- package/dist/src/tasks/task-run-coordinator.js +3 -1
- 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/dist/src/workflows/service/workflow-run-service.js +4 -2
- package/package.json +5 -3
- package/dist/gateway/static/root/assets/agents-Baxfj44y.js +0 -68
- package/dist/gateway/static/root/assets/app-management-settings-panel-S8LhoD6G.js +0 -1
- package/dist/gateway/static/root/assets/appearance-settings-D8Cd5vDP.js +0 -1
- package/dist/gateway/static/root/assets/apps-page-0uCxKyE5.js +0 -1
- package/dist/gateway/static/root/assets/automations-page-XyDat125.js +0 -6
- package/dist/gateway/static/root/assets/browser-settings-page-CrYNMR4w.js +0 -8
- package/dist/gateway/static/root/assets/browser-workflow-inputs-Abil4puY.js +0 -1
- package/dist/gateway/static/root/assets/capabilities-settings-panel-CvMcDhGf.js +0 -7
- package/dist/gateway/static/root/assets/capability-presets-settings-panel-CHJDY-Ih.js +0 -3
- package/dist/gateway/static/root/assets/channels-settings-Ds9BJbcY.js +0 -1
- package/dist/gateway/static/root/assets/channels-status-swr-DPaJSYX_.js +0 -1
- package/dist/gateway/static/root/assets/connectors-api-CgWjgPSI.js +0 -1
- package/dist/gateway/static/root/assets/connectors-page-NUs-Zf7F.js +0 -2
- package/dist/gateway/static/root/assets/cron-expression-ZdDBAoqA.js +0 -2
- package/dist/gateway/static/root/assets/dependency-picker-BGeB1jpn.js +0 -1
- package/dist/gateway/static/root/assets/desktop-pet-settings-CCjT1bdo.js +0 -1
- package/dist/gateway/static/root/assets/directory-picker-path-field-C-Te1QN3.js +0 -1
- package/dist/gateway/static/root/assets/extension-settings-page-BLSNdlgd.js +0 -1
- package/dist/gateway/static/root/assets/gateway-config-api-CQrf0k3D.js +0 -1
- package/dist/gateway/static/root/assets/gateway-settings-CvgH8IJI.js +0 -1
- package/dist/gateway/static/root/assets/heartbeat-settings-SPpnqbjl.js +0 -1
- package/dist/gateway/static/root/assets/home-page-BFoD6047.js +0 -3
- package/dist/gateway/static/root/assets/index-84ys9u-K.js +0 -152
- package/dist/gateway/static/root/assets/index-D-srnucP.css +0 -1
- package/dist/gateway/static/root/assets/local-app-workbench-page-D5uL2_Fd.js +0 -2
- package/dist/gateway/static/root/assets/local-apps-page-DIWkFa3g.js +0 -1
- package/dist/gateway/static/root/assets/logs-page--6v8qq6y.js +0 -2
- package/dist/gateway/static/root/assets/management-settings-DtEuS-kR.js +0 -1
- package/dist/gateway/static/root/assets/messages-DQ5X6cXp.js +0 -3
- package/dist/gateway/static/root/assets/notes-workbench-IdjuNXvH.js +0 -4
- package/dist/gateway/static/root/assets/page-tabs-7bSEWfSX.js +0 -1
- package/dist/gateway/static/root/assets/preview-open-alternatives-tgLbHgcV.js +0 -1
- package/dist/gateway/static/root/assets/project-detail-page-DnJXSuM4.js +0 -1
- package/dist/gateway/static/root/assets/projects-page-DckZKlBI.js +0 -1
- package/dist/gateway/static/root/assets/remote-access-hub-CfbUHBUH.js +0 -1
- package/dist/gateway/static/root/assets/schema-form-CFfyC4CB.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-BCfCh4My.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-Cbi4FvzO.js +0 -1
- package/dist/gateway/static/root/assets/settings-form-section-gEnEYT_3.js +0 -1
- package/dist/gateway/static/root/assets/settings-page-layout-CRpXF_05.js +0 -1
- package/dist/gateway/static/root/assets/setup-status-panel-BcHdpmFf.js +0 -1
- package/dist/gateway/static/root/assets/share-preview-page-DU8L68A5.js +0 -2
- package/dist/gateway/static/root/assets/shares-settings-kjTqH9yY.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-QAGpwzIM.js +0 -1
- package/dist/gateway/static/root/assets/skills-page-lE1-YRdK.js +0 -2
- package/dist/gateway/static/root/assets/system-settings-panel-CVDsiPKn.js +0 -1
- package/dist/gateway/static/root/assets/task-detail-page-DsM13-cy.js +0 -1
- package/dist/gateway/static/root/assets/typed-models-lib-DelyYgoc.js +0 -1
- package/dist/gateway/static/root/assets/use-autosave-C50Mq5yQ.js +0 -1
- package/dist/gateway/static/root/assets/user-context-page-D-VEOt2A.js +0 -1
- package/dist/gateway/static/root/assets/voice-api-key-field-DYfgTe3Y.js +0 -1
- package/dist/gateway/static/root/assets/work-discovery-overlay-DPbujsp3.js +0 -1
- package/dist/gateway/static/root/assets/work-discovery-page-D77opady.js +0 -1
- package/dist/gateway/static/root/assets/workflows-page-B9-PLmlX.js +0 -5
- package/dist/src/gateway/ensure-network-cors.d.ts +0 -3
- package/dist/src/gateway/ensure-network-cors.js +0 -23
|
@@ -1,18 +1,26 @@
|
|
|
1
|
+
import { runSqliteWriteTransaction } from "../../../storage/sqlite/transaction.js";
|
|
1
2
|
import { TaskRepository } from "../../../tasks/task-repository.js";
|
|
3
|
+
import { TaskConversationRepository } from "../../../tasks/task-conversation-repository.js";
|
|
2
4
|
import { TaskRunRepository } from "../../../tasks/task-run-repository.js";
|
|
3
5
|
import { TaskReadModelProjector } from "../../../tasks/task-read-model-projector.js";
|
|
4
6
|
import { TaskPhaseSchema } from "../../../../packages/gateway-contract/src/task-lifecycle.js";
|
|
5
|
-
import { TaskCommandRequestSchema, TaskContextInputSchema, TaskCreateRequestSchema, TaskDependencyUpdateRequestSchema, TaskPatchRequestSchema } from "../../../../packages/gateway-contract/src/tasks.js";
|
|
7
|
+
import { TaskBoardPositionRequestSchema, TaskCommandRequestSchema, TaskContextInputSchema, TaskCreateRequestSchema, TaskDependencyUpdateRequestSchema, TaskHandoffRequestSchema, TaskPatchRequestSchema } from "../../../../packages/gateway-contract/src/tasks.js";
|
|
6
8
|
import { ProjectMonitoringUpdateSchema } from "../../../../packages/gateway-contract/src/project-monitoring.js";
|
|
9
|
+
import { resolveProjectAgentId } from "../../../projects/project-agent.js";
|
|
7
10
|
import { TaskContextRepository } from "../../../tasks/task-context-repository.js";
|
|
11
|
+
import { enqueueTaskChangedEvent } from "../../../tasks/task-change-events.js";
|
|
8
12
|
import { TaskDependencyError, TaskDependencyService } from "../../../tasks/task-dependency-service.js";
|
|
9
13
|
import { TaskApplicationService } from "../../../tasks/task-application-service.js";
|
|
10
14
|
import { ProjectMonitoringService } from "../../../tasks/project-monitoring-service.js";
|
|
11
15
|
import { ProjectOperatingViewService } from "../../../tasks/project-operating-view-service.js";
|
|
12
16
|
import { TaskValueMetricsService } from "../../../tasks/task-value-metrics-service.js";
|
|
17
|
+
import { TaskHandoffService } from "../../../tasks/task-handoff-service.js";
|
|
13
18
|
import { TaskSignalService } from "../../../tasks/task-signal-service.js";
|
|
19
|
+
import { submitSessionInput } from "./session-input-handler.js";
|
|
20
|
+
import { TaskConversationQueryService } from "../../service/task-conversation-query-service.js";
|
|
14
21
|
//#region src/gateway/hono/routes/tasks.ts
|
|
15
22
|
function registerTaskRoutes(authenticated, deps) {
|
|
23
|
+
const taskRateLimit = deps.taskRateLimitMiddleware ?? deps.strictRateLimitMiddleware;
|
|
16
24
|
const application = new TaskApplicationService();
|
|
17
25
|
const operatingViews = new ProjectOperatingViewService(deps.service.projects);
|
|
18
26
|
const monitoring = new ProjectMonitoringService();
|
|
@@ -20,9 +28,17 @@ function registerTaskRoutes(authenticated, deps) {
|
|
|
20
28
|
const tasks = new TaskRepository();
|
|
21
29
|
const runs = new TaskRunRepository();
|
|
22
30
|
const context = new TaskContextRepository();
|
|
31
|
+
const conversations = new TaskConversationRepository();
|
|
32
|
+
const conversationQuery = new TaskConversationQueryService(deps.service.sessions);
|
|
23
33
|
const projector = new TaskReadModelProjector();
|
|
24
34
|
const signals = new TaskSignalService(() => deps.service.dispatchTaskRuns());
|
|
25
35
|
const dependencies = new TaskDependencyService();
|
|
36
|
+
const handoffs = new TaskHandoffService({
|
|
37
|
+
getConfig: () => deps.service.currentConfig,
|
|
38
|
+
sessionIndex: deps.service.sessionIndexInstance,
|
|
39
|
+
getActiveRunId: (sessionKey) => deps.service.getActiveWebchatRunId(sessionKey),
|
|
40
|
+
abortRun: (runId) => deps.service.abortAgentRun(runId)
|
|
41
|
+
});
|
|
26
42
|
authenticated.get("/api/tasks", (c) => {
|
|
27
43
|
const phase = TaskPhaseSchema.safeParse(c.req.query("phase"));
|
|
28
44
|
const rawLimit = Number(c.req.query("limit") ?? 50);
|
|
@@ -42,7 +58,7 @@ function registerTaskRoutes(authenticated, deps) {
|
|
|
42
58
|
})
|
|
43
59
|
});
|
|
44
60
|
});
|
|
45
|
-
authenticated.post("/api/tasks",
|
|
61
|
+
authenticated.post("/api/tasks", taskRateLimit, async (c) => {
|
|
46
62
|
const body = await c.req.json().catch(() => ({}));
|
|
47
63
|
const parsed = TaskCreateRequestSchema.safeParse(body);
|
|
48
64
|
if (!parsed.success) return c.json({
|
|
@@ -50,13 +66,32 @@ function registerTaskRoutes(authenticated, deps) {
|
|
|
50
66
|
error: "Invalid task request"
|
|
51
67
|
}, 400);
|
|
52
68
|
try {
|
|
53
|
-
const
|
|
69
|
+
const requestedAgentId = parsed.data.delegateAgentId ?? (parsed.data.activation.mode === "start" && parsed.data.activation.executor?.kind === "agent" ? parsed.data.activation.executor.agentId : void 0);
|
|
70
|
+
const agentId = resolveProjectAgentId({
|
|
71
|
+
config: deps.service.currentConfig,
|
|
72
|
+
projects: deps.service.projects,
|
|
73
|
+
explicitAgentId: requestedAgentId,
|
|
74
|
+
projectId: parsed.data.projectId
|
|
75
|
+
});
|
|
76
|
+
const input = {
|
|
77
|
+
...parsed.data,
|
|
78
|
+
delegateAgentId: agentId,
|
|
79
|
+
activation: parsed.data.activation.mode === "start" ? {
|
|
80
|
+
...parsed.data.activation,
|
|
81
|
+
executor: parsed.data.activation.executor?.kind === "agent" || !parsed.data.activation.executor ? {
|
|
82
|
+
kind: "agent",
|
|
83
|
+
agentId
|
|
84
|
+
} : parsed.data.activation.executor
|
|
85
|
+
} : parsed.data.activation
|
|
86
|
+
};
|
|
87
|
+
const created = application.create(input);
|
|
54
88
|
if (created.ok === false) return c.json({
|
|
55
89
|
ok: false,
|
|
56
90
|
code: created.reason,
|
|
57
91
|
error: created.reason
|
|
58
92
|
}, 409);
|
|
59
93
|
if (created.runId) deps.service.dispatchTaskRuns();
|
|
94
|
+
else deps.service.dispatchTaskEvents();
|
|
60
95
|
return c.json({
|
|
61
96
|
ok: true,
|
|
62
97
|
task: created.model.task,
|
|
@@ -91,39 +126,191 @@ function registerTaskRoutes(authenticated, deps) {
|
|
|
91
126
|
runs: runs.listByTask(task.id),
|
|
92
127
|
receipts: runs.listReceipts(task.id),
|
|
93
128
|
context: context.list(task.id),
|
|
129
|
+
conversation: conversations.requireState(task.id),
|
|
130
|
+
sessions: conversations.listSessions(task.id),
|
|
94
131
|
authorityGrants: context.listActiveGrants(task.id),
|
|
95
132
|
dependencies: dependencies.listDependencies(task.id),
|
|
96
133
|
dependents: dependencies.listDependents(task.id),
|
|
97
134
|
allowedCommands: model.allowedCommands
|
|
98
135
|
});
|
|
99
136
|
});
|
|
100
|
-
authenticated.
|
|
137
|
+
authenticated.post("/api/tasks/:id/conversation", taskRateLimit, async (c) => {
|
|
138
|
+
try {
|
|
139
|
+
const result = await deps.service.ensureTaskConversation(c.req.param("id"));
|
|
140
|
+
if (result.created) {
|
|
141
|
+
const task = tasks.require(c.req.param("id"));
|
|
142
|
+
runSqliteWriteTransaction((db) => enqueueTaskChangedEvent(db, {
|
|
143
|
+
taskId: task.id,
|
|
144
|
+
projectId: task.projectId,
|
|
145
|
+
version: task.version,
|
|
146
|
+
changedFields: ["conversation"],
|
|
147
|
+
actor: { kind: "user" }
|
|
148
|
+
}));
|
|
149
|
+
deps.service.dispatchTaskEvents();
|
|
150
|
+
}
|
|
151
|
+
return c.json({
|
|
152
|
+
ok: true,
|
|
153
|
+
...result
|
|
154
|
+
});
|
|
155
|
+
} catch (error) {
|
|
156
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
157
|
+
return c.json({
|
|
158
|
+
ok: false,
|
|
159
|
+
error: message
|
|
160
|
+
}, message.startsWith("Task not found") ? 404 : 409);
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
authenticated.post("/api/tasks/:id/handoff", taskRateLimit, async (c) => {
|
|
164
|
+
const parsed = TaskHandoffRequestSchema.safeParse(await c.req.json().catch(() => ({})));
|
|
165
|
+
if (!parsed.success) return c.json({
|
|
166
|
+
ok: false,
|
|
167
|
+
error: "Invalid task handoff request"
|
|
168
|
+
}, 400);
|
|
169
|
+
try {
|
|
170
|
+
const result = await handoffs.handoff({
|
|
171
|
+
taskId: c.req.param("id"),
|
|
172
|
+
...parsed.data
|
|
173
|
+
});
|
|
174
|
+
runSqliteWriteTransaction((db) => enqueueTaskChangedEvent(db, {
|
|
175
|
+
taskId: result.task.id,
|
|
176
|
+
projectId: result.task.projectId,
|
|
177
|
+
version: result.task.version,
|
|
178
|
+
changedFields: ["delegateAgentId", "conversation"],
|
|
179
|
+
actor: { kind: "user" }
|
|
180
|
+
}));
|
|
181
|
+
deps.service.dispatchTaskEvents();
|
|
182
|
+
return c.json({
|
|
183
|
+
ok: true,
|
|
184
|
+
task: result.task,
|
|
185
|
+
conversation: result.conversation,
|
|
186
|
+
...result.fromAgentId ? { fromAgentId: result.fromAgentId } : {},
|
|
187
|
+
toAgentId: result.toAgentId,
|
|
188
|
+
activeSessionKey: result.activeSessionKey,
|
|
189
|
+
assignmentEpoch: result.assignmentEpoch
|
|
190
|
+
});
|
|
191
|
+
} catch (error) {
|
|
192
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
193
|
+
return c.json({
|
|
194
|
+
ok: false,
|
|
195
|
+
error: message
|
|
196
|
+
}, message.startsWith("Agent not found") || message.startsWith("Task not found") ? 404 : 409);
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
authenticated.post("/api/tasks/:id/inputs", deps.chatRateLimitMiddleware, async (c) => {
|
|
200
|
+
const active = conversations.getActiveSession(c.req.param("id"));
|
|
201
|
+
if (!active?.sessionKey) return c.json({
|
|
202
|
+
ok: false,
|
|
203
|
+
error: {
|
|
204
|
+
code: "NOT_FOUND",
|
|
205
|
+
message: "Task has no active conversation"
|
|
206
|
+
}
|
|
207
|
+
}, 404);
|
|
208
|
+
const expectedSessionKey = c.req.header("X-Xopc-Expected-Session-Key")?.trim();
|
|
209
|
+
if (expectedSessionKey && expectedSessionKey !== active.sessionKey) return c.json({
|
|
210
|
+
ok: false,
|
|
211
|
+
error: {
|
|
212
|
+
code: "CONFLICT",
|
|
213
|
+
message: "Task executor changed; refresh the conversation"
|
|
214
|
+
}
|
|
215
|
+
}, 409);
|
|
216
|
+
return submitSessionInput(c, deps, active.sessionKey);
|
|
217
|
+
});
|
|
218
|
+
authenticated.patch("/api/tasks/:id/conversation/config", taskRateLimit, async (c) => {
|
|
219
|
+
const body = await c.req.json().catch(() => null);
|
|
220
|
+
const model = typeof body?.model === "string" ? body.model.trim() : "";
|
|
221
|
+
if (!model) return c.json({
|
|
222
|
+
ok: false,
|
|
223
|
+
error: "Missing model"
|
|
224
|
+
}, 400);
|
|
225
|
+
const active = conversations.getActiveSession(c.req.param("id"));
|
|
226
|
+
if (!active?.sessionKey) return c.json({
|
|
227
|
+
ok: false,
|
|
228
|
+
error: "Task has no active conversation"
|
|
229
|
+
}, 404);
|
|
230
|
+
const result = await deps.service.sessions.patchAgentConfig(active.sessionKey, { model });
|
|
231
|
+
if (!result.ok) return c.json({
|
|
232
|
+
ok: false,
|
|
233
|
+
error: result.error
|
|
234
|
+
}, 400);
|
|
235
|
+
return c.json({
|
|
236
|
+
ok: true,
|
|
237
|
+
activeSessionKey: active.sessionKey
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
authenticated.get("/api/tasks/:id/conversation/history", async (c) => {
|
|
241
|
+
const parsedLimit = Number.parseInt(c.req.query("limit") ?? "50", 10);
|
|
242
|
+
const parsedOffset = Number.parseInt(c.req.query("offset") ?? "0", 10);
|
|
243
|
+
const rawBefore = c.req.query("before")?.trim();
|
|
244
|
+
const before = rawBefore === void 0 ? void 0 : Number.parseInt(rawBefore, 10);
|
|
245
|
+
if (rawBefore !== void 0 && (!Number.isInteger(before) || before < 0)) return c.json({ error: "Invalid conversation history cursor" }, 400);
|
|
246
|
+
const result = await conversationQuery.getMessagePage(c.req.param("id"), {
|
|
247
|
+
limit: Number.isFinite(parsedLimit) ? Math.min(200, Math.max(1, parsedLimit)) : 50,
|
|
248
|
+
offset: Number.isFinite(parsedOffset) ? Math.max(0, parsedOffset) : 0,
|
|
249
|
+
...before === void 0 ? {} : { before }
|
|
250
|
+
});
|
|
251
|
+
return result ? c.json(result) : c.json({ error: "Task conversation not found" }, 404);
|
|
252
|
+
});
|
|
253
|
+
authenticated.get("/api/tasks/:id/conversation/timeline", async (c) => {
|
|
254
|
+
const items = await conversationQuery.getTimeline(c.req.param("id"));
|
|
255
|
+
return items ? c.json({
|
|
256
|
+
ok: true,
|
|
257
|
+
items
|
|
258
|
+
}) : c.json({
|
|
259
|
+
ok: false,
|
|
260
|
+
error: "Task conversation not found"
|
|
261
|
+
}, 404);
|
|
262
|
+
});
|
|
263
|
+
authenticated.patch("/api/tasks/:id", taskRateLimit, async (c) => {
|
|
101
264
|
const parsed = TaskPatchRequestSchema.safeParse(await c.req.json().catch(() => ({})));
|
|
102
265
|
if (!parsed.success) return c.json({
|
|
103
266
|
ok: false,
|
|
104
267
|
error: "Invalid task patch"
|
|
105
268
|
}, 400);
|
|
106
|
-
const updated =
|
|
269
|
+
const updated = runSqliteWriteTransaction((db) => {
|
|
270
|
+
const task = tasks.update(c.req.param("id"), parsed.data);
|
|
271
|
+
if (!task) return void 0;
|
|
272
|
+
const changedFields = Object.keys(parsed.data).filter((field) => field !== "expectedVersion");
|
|
273
|
+
enqueueTaskChangedEvent(db, {
|
|
274
|
+
taskId: task.id,
|
|
275
|
+
projectId: task.projectId,
|
|
276
|
+
version: task.version,
|
|
277
|
+
changedFields,
|
|
278
|
+
actor: { kind: "user" }
|
|
279
|
+
});
|
|
280
|
+
return task;
|
|
281
|
+
});
|
|
107
282
|
if (!updated) return c.json({
|
|
108
283
|
ok: false,
|
|
109
284
|
error: "Task changed or was not found"
|
|
110
285
|
}, 409);
|
|
286
|
+
deps.service.dispatchTaskEvents();
|
|
111
287
|
return c.json({
|
|
112
288
|
ok: true,
|
|
113
289
|
task: updated
|
|
114
290
|
});
|
|
115
291
|
});
|
|
116
|
-
authenticated.put("/api/tasks/:id/dependencies",
|
|
292
|
+
authenticated.put("/api/tasks/:id/dependencies", taskRateLimit, async (c) => {
|
|
117
293
|
const parsed = TaskDependencyUpdateRequestSchema.safeParse(await c.req.json().catch(() => ({})));
|
|
118
294
|
if (!parsed.success) return c.json({
|
|
119
295
|
ok: false,
|
|
120
296
|
error: "Invalid task dependencies"
|
|
121
297
|
}, 400);
|
|
122
298
|
try {
|
|
123
|
-
const task =
|
|
124
|
-
|
|
125
|
-
|
|
299
|
+
const task = runSqliteWriteTransaction((db) => {
|
|
300
|
+
const changed = dependencies.replace({
|
|
301
|
+
taskId: c.req.param("id"),
|
|
302
|
+
...parsed.data
|
|
303
|
+
});
|
|
304
|
+
enqueueTaskChangedEvent(db, {
|
|
305
|
+
taskId: changed.id,
|
|
306
|
+
projectId: changed.projectId,
|
|
307
|
+
version: changed.version,
|
|
308
|
+
changedFields: ["dependencies"],
|
|
309
|
+
actor: { kind: "user" }
|
|
310
|
+
});
|
|
311
|
+
return changed;
|
|
126
312
|
});
|
|
313
|
+
deps.service.dispatchTaskEvents();
|
|
127
314
|
return c.json({
|
|
128
315
|
ok: true,
|
|
129
316
|
task,
|
|
@@ -140,7 +327,45 @@ function registerTaskRoutes(authenticated, deps) {
|
|
|
140
327
|
}, status);
|
|
141
328
|
}
|
|
142
329
|
});
|
|
143
|
-
authenticated.
|
|
330
|
+
authenticated.put("/api/tasks/:id/board-position", taskRateLimit, async (c) => {
|
|
331
|
+
const parsed = TaskBoardPositionRequestSchema.safeParse(await c.req.json().catch(() => ({})));
|
|
332
|
+
if (!parsed.success) return c.json({
|
|
333
|
+
ok: false,
|
|
334
|
+
error: "Invalid task board position"
|
|
335
|
+
}, 400);
|
|
336
|
+
try {
|
|
337
|
+
const task = runSqliteWriteTransaction((db) => {
|
|
338
|
+
const reordered = tasks.reorder({
|
|
339
|
+
taskId: c.req.param("id"),
|
|
340
|
+
...parsed.data
|
|
341
|
+
});
|
|
342
|
+
if (!reordered) return void 0;
|
|
343
|
+
enqueueTaskChangedEvent(db, {
|
|
344
|
+
taskId: reordered.id,
|
|
345
|
+
projectId: reordered.projectId,
|
|
346
|
+
version: reordered.version,
|
|
347
|
+
changedFields: ["boardRank"],
|
|
348
|
+
actor: { kind: "user" }
|
|
349
|
+
});
|
|
350
|
+
return reordered;
|
|
351
|
+
});
|
|
352
|
+
if (!task) return c.json({
|
|
353
|
+
ok: false,
|
|
354
|
+
error: "Task changed or was not found"
|
|
355
|
+
}, 409);
|
|
356
|
+
deps.service.dispatchTaskEvents();
|
|
357
|
+
return c.json({
|
|
358
|
+
ok: true,
|
|
359
|
+
task
|
|
360
|
+
});
|
|
361
|
+
} catch (error) {
|
|
362
|
+
return c.json({
|
|
363
|
+
ok: false,
|
|
364
|
+
error: error instanceof Error ? error.message : String(error)
|
|
365
|
+
}, 400);
|
|
366
|
+
}
|
|
367
|
+
});
|
|
368
|
+
authenticated.post("/api/tasks/:id/commands", taskRateLimit, async (c) => {
|
|
144
369
|
const parsed = TaskCommandRequestSchema.safeParse(await c.req.json().catch(() => ({})));
|
|
145
370
|
if (!parsed.success) return c.json({
|
|
146
371
|
ok: false,
|
|
@@ -165,6 +390,7 @@ function registerTaskRoutes(authenticated, deps) {
|
|
|
165
390
|
}, 409);
|
|
166
391
|
}
|
|
167
392
|
if (result.runId || parsed.data.command?.type === "resolve_wait") deps.service.dispatchTaskRuns();
|
|
393
|
+
else deps.service.dispatchTaskEvents();
|
|
168
394
|
if (parsed.data.command?.type === "close" && parsed.data.command.resolution === "done") signals.dependencyClosed(c.req.param("id"));
|
|
169
395
|
return c.json({
|
|
170
396
|
ok: true,
|
|
@@ -172,7 +398,7 @@ function registerTaskRoutes(authenticated, deps) {
|
|
|
172
398
|
...result.runId ? { run: runs.get(result.runId) } : {}
|
|
173
399
|
});
|
|
174
400
|
});
|
|
175
|
-
authenticated.post("/api/tasks/:id/context",
|
|
401
|
+
authenticated.post("/api/tasks/:id/context", taskRateLimit, async (c) => {
|
|
176
402
|
const task = tasks.get(c.req.param("id"));
|
|
177
403
|
if (!task) return c.json({
|
|
178
404
|
ok: false,
|
|
@@ -183,17 +409,42 @@ function registerTaskRoutes(authenticated, deps) {
|
|
|
183
409
|
ok: false,
|
|
184
410
|
error: "Invalid task context edge"
|
|
185
411
|
}, 400);
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
edge: context.add({
|
|
412
|
+
const edge = runSqliteWriteTransaction((db) => {
|
|
413
|
+
const created = context.add({
|
|
189
414
|
taskId: task.id,
|
|
190
415
|
...parsed.data,
|
|
191
416
|
createdBy: { kind: "user" }
|
|
192
|
-
})
|
|
417
|
+
});
|
|
418
|
+
enqueueTaskChangedEvent(db, {
|
|
419
|
+
taskId: task.id,
|
|
420
|
+
projectId: task.projectId,
|
|
421
|
+
version: task.version,
|
|
422
|
+
changedFields: ["context"],
|
|
423
|
+
actor: { kind: "user" }
|
|
424
|
+
});
|
|
425
|
+
return created;
|
|
426
|
+
});
|
|
427
|
+
deps.service.dispatchTaskEvents();
|
|
428
|
+
return c.json({
|
|
429
|
+
ok: true,
|
|
430
|
+
edge
|
|
193
431
|
}, 201);
|
|
194
432
|
});
|
|
195
|
-
authenticated.delete("/api/tasks/:id/context/:edgeId",
|
|
196
|
-
|
|
433
|
+
authenticated.delete("/api/tasks/:id/context/:edgeId", taskRateLimit, (c) => {
|
|
434
|
+
const task = tasks.get(c.req.param("id"));
|
|
435
|
+
const removed = task ? runSqliteWriteTransaction((db) => {
|
|
436
|
+
const didRemove = context.remove(task.id, c.req.param("edgeId"));
|
|
437
|
+
if (didRemove) enqueueTaskChangedEvent(db, {
|
|
438
|
+
taskId: task.id,
|
|
439
|
+
projectId: task.projectId,
|
|
440
|
+
version: task.version,
|
|
441
|
+
changedFields: ["context"],
|
|
442
|
+
actor: { kind: "user" }
|
|
443
|
+
});
|
|
444
|
+
return didRemove;
|
|
445
|
+
}) : false;
|
|
446
|
+
if (removed) deps.service.dispatchTaskEvents();
|
|
447
|
+
return removed ? c.json({ ok: true }) : c.json({
|
|
197
448
|
ok: false,
|
|
198
449
|
error: "Task context edge not found"
|
|
199
450
|
}, 404);
|
|
@@ -219,7 +470,7 @@ function registerTaskRoutes(authenticated, deps) {
|
|
|
219
470
|
error: "TaskRun not found"
|
|
220
471
|
}, 404);
|
|
221
472
|
});
|
|
222
|
-
authenticated.post("/api/task-runs/:runId/cancel",
|
|
473
|
+
authenticated.post("/api/task-runs/:runId/cancel", taskRateLimit, async (c) => {
|
|
223
474
|
const body = await c.req.json().catch(() => ({}));
|
|
224
475
|
const run = runs.get(c.req.param("runId"));
|
|
225
476
|
if (!run) return c.json({
|
|
@@ -256,13 +507,14 @@ function registerTaskRoutes(authenticated, deps) {
|
|
|
256
507
|
ok: false,
|
|
257
508
|
error: result.reason
|
|
258
509
|
}, 409);
|
|
510
|
+
deps.service.dispatchTaskEvents();
|
|
259
511
|
return c.json({
|
|
260
512
|
ok: true,
|
|
261
513
|
run: runs.get(run.id),
|
|
262
514
|
receipt: runs.getReceipt(run.id)
|
|
263
515
|
});
|
|
264
516
|
});
|
|
265
|
-
authenticated.post("/api/task-runs/:runId/feedback",
|
|
517
|
+
authenticated.post("/api/task-runs/:runId/feedback", taskRateLimit, async (c) => {
|
|
266
518
|
const body = await c.req.json().catch(() => ({}));
|
|
267
519
|
if (body.rating !== "helpful" && body.rating !== "not_helpful") return c.json({
|
|
268
520
|
ok: false,
|
|
@@ -306,7 +558,7 @@ function registerTaskRoutes(authenticated, deps) {
|
|
|
306
558
|
policy: monitoring.get(projectId)
|
|
307
559
|
});
|
|
308
560
|
});
|
|
309
|
-
authenticated.patch("/api/projects/:projectId/monitoring",
|
|
561
|
+
authenticated.patch("/api/projects/:projectId/monitoring", taskRateLimit, async (c) => {
|
|
310
562
|
const projectId = c.req.param("projectId");
|
|
311
563
|
if (!deps.service.projects.get(projectId)) return c.json({
|
|
312
564
|
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) => {
|