@xopcai/xopc 0.0.120 → 0.0.122
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -3
- package/README.zh-CN.md +23 -3
- package/dist/browser-ext/manifest.json +1 -1
- package/dist/extensions/dashscope/src/image-generation-provider.d.ts +6 -5
- package/dist/extensions/dashscope/src/image-generation-provider.js +40 -64
- package/dist/extensions/dashscope/src/image-generation-provider.js.map +1 -1
- package/dist/extensions/dashscope/src/index.d.ts +1 -1
- package/dist/extensions/dashscope/src/index.js +2 -2
- package/dist/extensions/dashscope/xopc.extension.json +3 -2
- package/dist/extensions/minimax/src/image-generation-provider.d.ts +7 -5
- package/dist/extensions/minimax/src/image-generation-provider.js +43 -37
- package/dist/extensions/minimax/src/image-generation-provider.js.map +1 -1
- package/dist/extensions/minimax/src/index.d.ts +1 -1
- package/dist/extensions/minimax/src/index.js +2 -2
- package/dist/extensions/minimax/xopc.extension.json +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/Combination-HAlzriaz.js +41 -0
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-APTlX9Sq.js → agent-avatar-dicebear-CK3s5cJo.js} +1 -1
- package/dist/gateway/static/root/assets/agents-SsQrj9B-.js +2 -0
- package/dist/gateway/static/root/assets/{app-management-settings-panel-DTKc5HHT.js → app-management-settings-panel-CWsNHtTs.js} +1 -1
- package/dist/gateway/static/root/assets/{appearance-settings-BTciFrE9.js → appearance-settings-C4b8y6iZ.js} +1 -1
- package/dist/gateway/static/root/assets/apps-page-CYb6fame.js +1 -0
- package/dist/gateway/static/root/assets/{archive-plugin-CiHaUMII.js → archive-plugin-DAtNW3Ia.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-t4gSrNjL.js → attachment-load-Dm-RY54y.js} +1 -1
- package/dist/gateway/static/root/assets/automation-suggestion-card-BKHewSVT.js +1 -0
- package/dist/gateway/static/root/assets/automations-page-DpOOq1nk.js +1 -0
- package/dist/gateway/static/root/assets/{binary-plugins-BAbSldW5.js → binary-plugins-BG6Xnk71.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-BNr_82xl.js → block-editor-DfndiO3h.js} +28 -28
- package/dist/gateway/static/root/assets/browser-settings-page-9nXxgh4u.js +8 -0
- package/dist/gateway/static/root/assets/{capability-presets-api-tzD702Gg.js → capability-presets-api-Cx93GLdw.js} +1 -1
- package/dist/gateway/static/root/assets/{capability-presets-settings-panel-AGhTueAc.js → capability-presets-settings-panel-D528_YFy.js} +1 -1
- package/dist/gateway/static/root/assets/{channel-recipient-api-DL_KzwGE.js → channel-recipient-api-DdPTtEs8.js} +1 -1
- package/dist/gateway/static/root/assets/channels-settings-Ck-3h5lI.js +1 -0
- package/dist/gateway/static/root/assets/connectors-page-DL-fAqE_.js +2 -0
- package/dist/gateway/static/root/assets/directory-picker-path-field-D835EHJd.js +1 -0
- package/dist/gateway/static/root/assets/dist-Mys7eFJ5.js +1 -0
- package/dist/gateway/static/root/assets/dist-wO9a2_ID.js +1 -0
- package/dist/gateway/static/root/assets/dreaming-settings-CCa1F7Fg.js +1 -0
- package/dist/gateway/static/root/assets/{extension-debug-page-Dl2kO0Sf.js → extension-debug-page-W2ITCPSA.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-DmbLPsx_.js → extension-page-DrvUytvn.js} +1 -1
- package/dist/gateway/static/root/assets/extension-settings-page-qM8aLT5u.js +1 -0
- package/dist/gateway/static/root/assets/{fetch-B3ME5Eri.js → fetch-ovkvM8KQ.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-api-DI9_BBTo.js → gateway-config-api-BB3biuD0.js} +1 -1
- package/dist/gateway/static/root/assets/gateway-settings-mmmnIH27.js +1 -0
- package/dist/gateway/static/root/assets/{gateway-startup-retry-CSZQTz6K.js → gateway-startup-retry-CanQ8UcR.js} +1 -1
- package/dist/gateway/static/root/assets/goal-create-dialog-CNqNdEkQ.js +1 -0
- package/dist/gateway/static/root/assets/goal-detail-page-Ceu5zrN6.js +1 -0
- package/dist/gateway/static/root/assets/{goals-config-api-Bhzqrp2w.js → goals-config-api-BBOX6mUl.js} +1 -1
- package/dist/gateway/static/root/assets/goals-page-BP1kfvWv.js +2 -0
- package/dist/gateway/static/root/assets/goals-settings-Bh3A7OZV.js +1 -0
- package/dist/gateway/static/root/assets/heartbeat-settings-YoYgKlfX.js +1 -0
- package/dist/gateway/static/root/assets/index-DdqSO9R2.js +191 -0
- package/dist/gateway/static/root/assets/index-jGB-2nI-.css +1 -0
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-kMp08SlX.js → keyboard-shortcuts-settings-B4TKJHla.js} +1 -1
- package/dist/gateway/static/root/assets/logs-page-BgVk5DvZ.js +2 -0
- package/dist/gateway/static/root/assets/{markdown-split-Bc0w40vz.js → markdown-split-DcFtdLci.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-DJ_HvgIy.js → media-plugins-B6SmeLVB.js} +1 -1
- package/dist/gateway/static/root/assets/messages-B5O4OIEo.js +3 -0
- package/dist/gateway/static/root/assets/models-hub-panel-BU0x8PGP.js +2 -0
- package/dist/gateway/static/root/assets/notes-page-D6JYVP4I.js +1 -0
- package/dist/gateway/static/root/assets/notes-workbench-mqRJYnJS.js +2 -0
- package/dist/gateway/static/root/assets/{preview-open-alternatives-BbA7sRd_.js → preview-open-alternatives-C29O19s7.js} +1 -1
- package/dist/gateway/static/root/assets/project-detail-page-DOTgzoeh.js +1 -0
- package/dist/gateway/static/root/assets/projects-page-DGUIb4y5.js +1 -0
- package/dist/gateway/static/root/assets/remote-access-hub-DVKKUj3u.js +1 -0
- package/dist/gateway/static/root/assets/{save-bar-controls-Dividh61.js → save-bar-controls-B4ad9GfB.js} +1 -1
- package/dist/gateway/static/root/assets/schedule-field-DdYncfLR.js +1 -0
- package/dist/gateway/static/root/assets/schema-form-BMTrDE-7.js +1 -0
- package/dist/gateway/static/root/assets/segmented-styles-BL8oLBEp.js +1 -0
- package/dist/gateway/static/root/assets/sessions-page-BVbzUWxW.js +1 -0
- package/dist/gateway/static/root/assets/settings-advanced-gate-DrNNoSqE.js +1 -0
- package/dist/gateway/static/root/assets/{settings-collapsible-section-BMCOfuE_.js → settings-collapsible-section-MwTW2eK4.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-form-section-DBkGwpIN.js → settings-form-section-Dg6LBbcr.js} +1 -1
- package/dist/gateway/static/root/assets/settings-page-CMDqgeMR.js +1 -0
- package/dist/gateway/static/root/assets/settings-page-layout-BX4T-OLF.js +1 -0
- package/dist/gateway/static/root/assets/setup-status-panel-Ce1pcFtR.js +1 -0
- package/dist/gateway/static/root/assets/share-preview-page-DWE-dUFh.js +2 -0
- package/dist/gateway/static/root/assets/shares-settings-aKSraR8l.js +1 -0
- package/dist/gateway/static/root/assets/skill-reload-api-CS-FU_B-.js +1 -0
- package/dist/gateway/static/root/assets/skills-page-BaF3u0LA.js +2 -0
- package/dist/gateway/static/root/assets/{system-settings-panel-CrmEnyX4.js → system-settings-panel-Dd6Y35Cm.js} +1 -1
- package/dist/gateway/static/root/assets/{typed-models-lib-BAsGn25g.js → typed-models-lib-Cx6XV5A2.js} +1 -1
- package/dist/gateway/static/root/assets/{use-channel-catalog-C7vcAoGb.js → use-channel-catalog-D4sMbmy7.js} +1 -1
- package/dist/gateway/static/root/assets/user-profile-settings-panel-Bx-hjMQc.js +1 -0
- package/dist/gateway/static/root/assets/{utils-Dl9efgnV.js → utils-D7b_vZnv.js} +1 -1
- package/dist/gateway/static/root/assets/voice-api-key-field-CkhbRt1q.js +1 -0
- package/dist/gateway/static/root/assets/workflow-run-setup-panel--TdohF5N.js +3 -0
- package/dist/gateway/static/root/assets/workflow-start-dialog-B0lavx_o.js +1 -0
- package/dist/gateway/static/root/assets/workflows-page-CZR05Bu_.js +18 -0
- package/dist/gateway/static/root/index.html +7 -8
- package/dist/package.js +1 -1
- package/dist/src/agent/agent-manager.d.ts +2 -0
- package/dist/src/agent/agent-manager.js +47 -7
- package/dist/src/agent/agent-manager.js.map +1 -1
- package/dist/src/agent/context/project-context.d.ts +19 -0
- package/dist/src/agent/context/project-context.js +90 -0
- package/dist/src/agent/context/project-context.js.map +1 -0
- package/dist/src/agent/embedded/xopc-auth-storage.d.ts +1 -5
- package/dist/src/agent/embedded/xopc-auth-storage.js +2 -8
- package/dist/src/agent/embedded/xopc-auth-storage.js.map +1 -1
- package/dist/src/agent/embedded/xopc-tools-bridge.js +23 -0
- package/dist/src/agent/embedded/xopc-tools-bridge.js.map +1 -1
- package/dist/src/agent/goals/checklist-judge.js +1 -1
- package/dist/src/agent/goals/checklist-judge.js.map +1 -1
- package/dist/src/agent/goals/goal-locale.js +2 -2
- package/dist/src/agent/goals/judge.js +1 -1
- package/dist/src/agent/goals/judge.js.map +1 -1
- package/dist/src/agent/image/tool-model-config.d.ts +2 -0
- package/dist/src/agent/image/tool-model-config.js +2 -2
- package/dist/src/agent/image/tool-model-config.js.map +1 -1
- package/dist/src/agent/image/understanding/pi-ai-provider.js +1 -1
- package/dist/src/agent/image/understanding/pi-ai-provider.js.map +1 -1
- package/dist/src/agent/memory/compaction.d.ts +1 -1
- package/dist/src/agent/memory/compaction.js +1 -1
- package/dist/src/agent/memory/compaction.js.map +1 -1
- package/dist/src/agent/memory/types.d.ts +1 -0
- package/dist/src/agent/middleware/self-verify.d.ts +3 -0
- package/dist/src/agent/middleware/self-verify.js +18 -0
- package/dist/src/agent/middleware/self-verify.js.map +1 -1
- package/dist/src/agent/orchestration/agent-event-handler.js +28 -3
- package/dist/src/agent/orchestration/agent-event-handler.js.map +1 -1
- package/dist/src/agent/prompt/sections/tooling.js +21 -5
- package/dist/src/agent/prompt/sections/tooling.js.map +1 -1
- package/dist/src/agent/prompt/service-prompt-builder.d.ts +1 -0
- package/dist/src/agent/prompt/service-prompt-builder.js +3 -0
- package/dist/src/agent/prompt/service-prompt-builder.js.map +1 -1
- package/dist/src/agent/prompt/system-prompt-params.d.ts +2 -0
- package/dist/src/agent/prompt/system-prompt-params.js +1 -0
- package/dist/src/agent/prompt/system-prompt-params.js.map +1 -1
- package/dist/src/agent/prompt/system-prompt.d.ts +2 -0
- package/dist/src/agent/prompt/system-prompt.js +14 -1
- package/dist/src/agent/prompt/system-prompt.js.map +1 -1
- package/dist/src/agent/sandbox/path-policy.js +30 -6
- package/dist/src/agent/sandbox/path-policy.js.map +1 -1
- package/dist/src/agent/service/btw-query.js +1 -1
- package/dist/src/agent/service/btw-query.js.map +1 -1
- package/dist/src/agent/service.js +4 -3
- package/dist/src/agent/service.js.map +1 -1
- package/dist/src/agent/session/session-config-service.js +6 -1
- package/dist/src/agent/session/session-config-service.js.map +1 -1
- package/dist/src/agent/session/session-hydrator.js +7 -3
- package/dist/src/agent/session/session-hydrator.js.map +1 -1
- package/dist/src/agent/session/session-inspector.js +9 -5
- package/dist/src/agent/session/session-inspector.js.map +1 -1
- package/dist/src/agent/starter-agents.js +18 -47
- package/dist/src/agent/starter-agents.js.map +1 -1
- package/dist/src/agent/tools/image-generate-tool.js +12 -1
- package/dist/src/agent/tools/image-generate-tool.js.map +1 -1
- package/dist/src/agent/tools/session-search-tool.js +1 -1
- package/dist/src/agent/tools/session-search-tool.js.map +1 -1
- package/dist/src/agent/tools/web-extract.js +1 -1
- package/dist/src/agent/tools/web-extract.js.map +1 -1
- package/dist/src/agent/workflow/index.js +2 -2
- package/dist/src/agent/workflow/runtime.js +1 -1
- package/dist/src/agent-manifest/index.js +2 -2
- package/dist/src/agent-manifest/schema.d.ts +66 -0
- package/dist/src/agent-manifest/schema.js +28 -2
- package/dist/src/agent-manifest/schema.js.map +1 -1
- package/dist/src/automations/api/routes.js +13 -2
- package/dist/src/automations/api/routes.js.map +1 -1
- package/dist/src/automations/domain/types.d.ts +2 -0
- package/dist/src/automations/domain/validation.d.ts +3 -0
- package/dist/src/automations/domain/validation.js +1 -0
- package/dist/src/automations/domain/validation.js.map +1 -1
- package/dist/src/automations/draft/automation-draft-service.js +1 -1
- package/dist/src/automations/draft/automation-draft-service.js.map +1 -1
- package/dist/src/automations/draft/automation-draft-validator.d.ts +1 -0
- package/dist/src/automations/service/action-executor.js +3 -1
- package/dist/src/automations/service/action-executor.js.map +1 -1
- package/dist/src/automations/service/automation-service.d.ts +4 -1
- package/dist/src/automations/service/automation-service.js +2 -2
- package/dist/src/automations/service/automation-service.js.map +1 -1
- package/dist/src/automations/storage/automation-repository.d.ts +3 -1
- package/dist/src/automations/storage/automation-repository.js +7 -5
- package/dist/src/automations/storage/automation-repository.js.map +1 -1
- package/dist/src/automations/storage/automation-run-repository.d.ts +1 -0
- package/dist/src/automations/storage/automation-run-repository.js +12 -1
- package/dist/src/automations/storage/automation-run-repository.js.map +1 -1
- package/dist/src/chat-commands/context.js +2 -1
- package/dist/src/chat-commands/context.js.map +1 -1
- package/dist/src/cli/command-loaders.js +1 -0
- package/dist/src/cli/command-loaders.js.map +1 -1
- package/dist/src/cli/command-manifest.js +4 -0
- package/dist/src/cli/command-manifest.js.map +1 -1
- package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
- package/dist/src/cli/commands/goal.js +27 -4
- package/dist/src/cli/commands/goal.js.map +1 -1
- package/dist/src/cli/commands/onboard/model.js +161 -11
- package/dist/src/cli/commands/onboard/model.js.map +1 -1
- package/dist/src/cli/commands/project.d.ts +4 -0
- package/dist/src/cli/commands/project.js +239 -0
- package/dist/src/cli/commands/project.js.map +1 -0
- package/dist/src/config/default-model.d.ts +11 -0
- package/dist/src/config/default-model.js +19 -0
- package/dist/src/config/default-model.js.map +1 -0
- package/dist/src/config/index.js +2 -2
- package/dist/src/config/schema.d.ts +71 -11
- package/dist/src/config/schema.js +57 -22
- package/dist/src/config/schema.js.map +1 -1
- package/dist/src/connectors/registries/smithery.js +10 -9
- package/dist/src/connectors/registries/smithery.js.map +1 -1
- package/dist/src/daemon/install-plan.js +2 -1
- package/dist/src/daemon/install-plan.js.map +1 -1
- package/dist/src/gateway/global-defaults-admin.js +6 -16
- package/dist/src/gateway/global-defaults-admin.js.map +1 -1
- package/dist/src/gateway/hono/lib/config-payload.d.ts +24 -0
- package/dist/src/gateway/hono/routes/agents.js +1 -79
- package/dist/src/gateway/hono/routes/agents.js.map +1 -1
- package/dist/src/gateway/hono/routes/goals.js +51 -4
- package/dist/src/gateway/hono/routes/goals.js.map +1 -1
- package/dist/src/gateway/hono/routes/index.js +2 -0
- package/dist/src/gateway/hono/routes/index.js.map +1 -1
- package/dist/src/gateway/hono/routes/memory.js +5 -1
- package/dist/src/gateway/hono/routes/memory.js.map +1 -1
- package/dist/src/gateway/hono/routes/models.js +132 -14
- package/dist/src/gateway/hono/routes/models.js.map +1 -1
- package/dist/src/gateway/hono/routes/notes.js +5 -0
- package/dist/src/gateway/hono/routes/notes.js.map +1 -1
- package/dist/src/gateway/hono/routes/projects.d.ts +3 -0
- package/dist/src/gateway/hono/routes/projects.js +578 -0
- package/dist/src/gateway/hono/routes/projects.js.map +1 -0
- package/dist/src/gateway/hono/routes/sessions.js +47 -7
- package/dist/src/gateway/hono/routes/sessions.js.map +1 -1
- package/dist/src/gateway/hono/routes/voice.js +1 -1
- package/dist/src/gateway/hono/routes/voice.js.map +1 -1
- package/dist/src/gateway/hono/routes/workflows.js +27 -4
- package/dist/src/gateway/hono/routes/workflows.js.map +1 -1
- package/dist/src/gateway/image-capabilities.d.ts +11 -1
- package/dist/src/gateway/image-capabilities.js +32 -43
- package/dist/src/gateway/image-capabilities.js.map +1 -1
- package/dist/src/gateway/service.d.ts +3 -0
- package/dist/src/gateway/service.js +10 -3
- package/dist/src/gateway/service.js.map +1 -1
- package/dist/src/gateway/workspace-editor-path.js +9 -1
- package/dist/src/gateway/workspace-editor-path.js.map +1 -1
- package/dist/src/generated/bundled-image-generation-providers.d.ts +4 -2
- package/dist/src/generated/bundled-image-generation-providers.js +6 -4
- package/dist/src/generated/bundled-image-generation-providers.js.map +1 -1
- package/dist/src/goals/goal-service.d.ts +1 -1
- package/dist/src/goals/goal-service.js +2 -1
- package/dist/src/goals/goal-service.js.map +1 -1
- package/dist/src/goals/goal-store.d.ts +1 -1
- package/dist/src/goals/goal-store.js +12 -6
- package/dist/src/goals/goal-store.js.map +1 -1
- package/dist/src/goals/goal-workflow-judge.js +1 -1
- package/dist/src/goals/types.d.ts +3 -0
- package/dist/src/i18n/goals-bundle.js +1 -1
- package/dist/src/i18n/index.js +1 -1
- package/dist/src/i18n/share-tool-bundle.js +2 -2
- package/dist/src/media/paths.js +3 -4
- package/dist/src/media/paths.js.map +1 -1
- package/dist/src/notes/agent-context.js +2 -2
- package/dist/src/notes/service.js +1 -1
- package/dist/src/notes/service.js.map +1 -1
- package/dist/src/projects/automation-bind.d.ts +1 -0
- package/dist/src/projects/automation-bind.js +16 -0
- package/dist/src/projects/automation-bind.js.map +1 -0
- package/dist/src/projects/goal-bind.d.ts +4 -0
- package/dist/src/projects/goal-bind.js +40 -0
- package/dist/src/projects/goal-bind.js.map +1 -0
- package/dist/src/projects/index.d.ts +11 -0
- package/dist/src/projects/index.js +11 -0
- package/dist/src/projects/memory-bind.d.ts +1 -0
- package/dist/src/projects/memory-bind.js +16 -0
- package/dist/src/projects/memory-bind.js.map +1 -0
- package/dist/src/projects/project-agent.d.ts +10 -0
- package/dist/src/projects/project-agent.js +27 -0
- package/dist/src/projects/project-agent.js.map +1 -0
- package/dist/src/projects/project-loop.d.ts +58 -0
- package/dist/src/projects/project-loop.js +118 -0
- package/dist/src/projects/project-loop.js.map +1 -0
- package/dist/src/projects/project-service.d.ts +26 -0
- package/dist/src/projects/project-service.js +94 -0
- package/dist/src/projects/project-service.js.map +1 -0
- package/dist/src/projects/project-store.d.ts +31 -0
- package/dist/src/projects/project-store.js +193 -0
- package/dist/src/projects/project-store.js.map +1 -0
- package/dist/src/projects/session-bind.d.ts +5 -0
- package/dist/src/projects/session-bind.js +46 -0
- package/dist/src/projects/session-bind.js.map +1 -0
- package/dist/src/projects/types.d.ts +60 -0
- package/dist/src/projects/types.js +1 -0
- package/dist/src/projects/workflow-bind.d.ts +1 -0
- package/dist/src/projects/workflow-bind.js +16 -0
- package/dist/src/projects/workflow-bind.js.map +1 -0
- package/dist/src/projects/workspace.d.ts +5 -0
- package/dist/src/projects/workspace.js +25 -0
- package/dist/src/projects/workspace.js.map +1 -0
- package/dist/src/providers/domestic-presets.d.ts +615 -0
- package/dist/src/providers/domestic-presets.js +834 -0
- package/dist/src/providers/domestic-presets.js.map +1 -0
- package/dist/src/providers/env-keys.js +29 -5
- package/dist/src/providers/env-keys.js.map +1 -1
- package/dist/src/providers/extension-stream-bridge.js +1 -1
- package/dist/src/providers/extension-stream-bridge.js.map +1 -1
- package/dist/src/providers/index.d.ts +5 -5
- package/dist/src/providers/index.js +101 -21
- package/dist/src/providers/index.js.map +1 -1
- package/dist/src/providers/model-discovery.d.ts +17 -0
- package/dist/src/providers/model-discovery.js +61 -0
- package/dist/src/providers/model-discovery.js.map +1 -0
- package/dist/src/providers/model-registry.d.ts +1 -1
- package/dist/src/providers/model-registry.js +1 -1
- package/dist/src/providers/model-registry.js.map +1 -1
- package/dist/src/providers/presentation.js +185 -14
- package/dist/src/providers/presentation.js.map +1 -1
- package/dist/src/session/index.d.ts +1 -1
- package/dist/src/session/index.js +3 -3
- package/dist/src/session/session-title.js +1 -1
- package/dist/src/session/session-title.js.map +1 -1
- package/dist/src/session/session-workspace.d.ts +4 -2
- package/dist/src/session/session-workspace.js +10 -3
- package/dist/src/session/session-workspace.js.map +1 -1
- package/dist/src/session/types.d.ts +3 -0
- package/dist/src/session/types.js.map +1 -1
- package/dist/src/storage/sqlite/memory-records-repository.d.ts +3 -0
- package/dist/src/storage/sqlite/memory-records-repository.js +16 -5
- package/dist/src/storage/sqlite/memory-records-repository.js.map +1 -1
- package/dist/src/storage/sqlite/migrations/019_projects.sql +40 -0
- package/dist/src/storage/sqlite/migrations/020_project_default_agent.sql +1 -0
- package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
- package/dist/src/storage/sqlite/migrations/runner.js +3 -3
- package/dist/src/storage/sqlite/migrations/runner.js.map +1 -1
- package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
- package/dist/src/storage/sqlite/notes-repository.js +20 -4
- package/dist/src/storage/sqlite/notes-repository.js.map +1 -1
- package/dist/src/storage/sqlite/row-mappers.d.ts +2 -0
- package/dist/src/storage/sqlite/row-mappers.js +2 -0
- package/dist/src/storage/sqlite/row-mappers.js.map +1 -1
- package/dist/src/storage/sqlite/schema.js +1 -1
- package/dist/src/storage/sqlite/session-repository.js +10 -5
- package/dist/src/storage/sqlite/session-repository.js.map +1 -1
- package/dist/src/storage/sqlite/transcript-repository.js +8 -0
- package/dist/src/storage/sqlite/transcript-repository.js.map +1 -1
- package/dist/src/tui/backends/embedded-backend.js +1 -1
- package/dist/src/tui/tui-commands.js +145 -0
- package/dist/src/tui/tui-commands.js.map +1 -1
- package/dist/src/tui/xopc-tui-keybindings.d.ts +1 -1
- package/dist/src/voice/tts/summarize.js +1 -1
- package/dist/src/voice/tts/summarize.js.map +1 -1
- package/dist/src/workflows/domain/run.d.ts +1 -0
- package/dist/src/workflows/domain/run.js.map +1 -1
- package/dist/src/workflows/draft/workflow-draft-service.js +1 -1
- package/dist/src/workflows/draft/workflow-draft-service.js.map +1 -1
- package/dist/src/workflows/index.js +1 -1
- package/dist/src/workflows/service/workflow-run-service.d.ts +1 -0
- package/dist/src/workflows/service/workflow-run-service.js +18 -4
- package/dist/src/workflows/service/workflow-run-service.js.map +1 -1
- package/dist/src/workflows/service/workflow-run-service.types.d.ts +2 -0
- package/dist/src/workflows/service/workflow-session-bridge.d.ts +1 -0
- package/dist/src/workflows/service/workflow-session-bridge.js +3 -0
- package/dist/src/workflows/service/workflow-session-bridge.js.map +1 -1
- package/dist/src/workflows/store/run-index-store.d.ts +1 -0
- package/dist/src/workflows/store/run-index-store.js +13 -3
- package/dist/src/workflows/store/run-index-store.js.map +1 -1
- package/dist/src/workflows/store/run-store.d.ts +3 -1
- package/dist/src/workflows/store/run-store.js +5 -2
- package/dist/src/workflows/store/run-store.js.map +1 -1
- package/package.json +15 -6
- package/dist/gateway/static/root/assets/Combination-BzaXOLd9.js +0 -41
- package/dist/gateway/static/root/assets/agents-CFW_FS_V.js +0 -2
- package/dist/gateway/static/root/assets/apps-page-DT52OrbM.js +0 -1
- package/dist/gateway/static/root/assets/automation-suggestion-card-CtysjSoQ.js +0 -1
- package/dist/gateway/static/root/assets/automations-page-B2YvT3ES.js +0 -1
- package/dist/gateway/static/root/assets/browser-settings-page-BQEXPhDU.js +0 -8
- package/dist/gateway/static/root/assets/channels-settings-DRTutLlN.js +0 -1
- package/dist/gateway/static/root/assets/connectors-page-DneJVuge.js +0 -2
- package/dist/gateway/static/root/assets/dist-By6Rh2ee.js +0 -1
- package/dist/gateway/static/root/assets/dist-CkobR7mk.js +0 -1
- package/dist/gateway/static/root/assets/dist-DokQNVMQ.js +0 -1
- package/dist/gateway/static/root/assets/dreaming-settings-BEwWA1yf.js +0 -1
- package/dist/gateway/static/root/assets/extension-settings-page-BS2rx40e.js +0 -1
- package/dist/gateway/static/root/assets/gateway-settings-DAFI_T0W.js +0 -1
- package/dist/gateway/static/root/assets/goal-detail-page-DLuz4jC9.js +0 -1
- package/dist/gateway/static/root/assets/goals-page-DTAfv9Ft.js +0 -2
- package/dist/gateway/static/root/assets/goals-settings-CWN2iirQ.js +0 -1
- package/dist/gateway/static/root/assets/heartbeat-settings-CkkkHvOi.js +0 -1
- package/dist/gateway/static/root/assets/index-CnjzMw5i.js +0 -191
- package/dist/gateway/static/root/assets/index-E1QinYKp.css +0 -1
- package/dist/gateway/static/root/assets/logs-page-CDXDlS_W.js +0 -2
- package/dist/gateway/static/root/assets/messages-CISb1HLv.js +0 -3
- package/dist/gateway/static/root/assets/models-hub-panel-BTwxMIgZ.js +0 -2
- package/dist/gateway/static/root/assets/note-detail-page-BBfOeB0m.js +0 -2
- package/dist/gateway/static/root/assets/note-image-lightbox-4zntqpZq.js +0 -1
- package/dist/gateway/static/root/assets/note-time-hm6JO_Wi.js +0 -1
- package/dist/gateway/static/root/assets/notes-page-VocWOjoK.js +0 -1
- package/dist/gateway/static/root/assets/remote-access-hub-CXwjuD_Q.js +0 -1
- package/dist/gateway/static/root/assets/schedule-field-pRheF2CO.js +0 -1
- package/dist/gateway/static/root/assets/schema-form-D6PiPxfr.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-CpcVySQK.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-BVy_TawT.js +0 -1
- package/dist/gateway/static/root/assets/settings-page-CZpNyCWn.js +0 -1
- package/dist/gateway/static/root/assets/settings-page-layout-NxoW5Y57.js +0 -1
- package/dist/gateway/static/root/assets/setup-status-panel-MVHLLu_a.js +0 -1
- package/dist/gateway/static/root/assets/share-preview-page-B0suFCfj.js +0 -2
- package/dist/gateway/static/root/assets/shares-settings-C6DBbQGo.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-6hJmhwSn.js +0 -1
- package/dist/gateway/static/root/assets/skills-page-Im1MA0Iw.js +0 -2
- package/dist/gateway/static/root/assets/user-profile-settings-panel-QAQWGelF.js +0 -1
- package/dist/gateway/static/root/assets/voice-api-key-field-D-6XueTv.js +0 -1
- package/dist/gateway/static/root/assets/voice-player-store-CMC5NOJp.js +0 -1
- package/dist/gateway/static/root/assets/workflow-run-setup-panel-DVPllR4k.js +0 -3
- package/dist/gateway/static/root/assets/workflows-page-B94WISTW.js +0 -18
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validation.js","names":[],"sources":["../../../../src/automations/domain/validation.ts"],"sourcesContent":["import { CronExpressionParser } from 'cron-parser';\nimport { z } from 'zod';\n\nconst nonEmptyString = z.string().trim().min(1);\n\nconst optionalTrimmedString = (max: number) =>\n z.preprocess((value) => {\n if (value == null) return undefined;\n if (typeof value !== 'string') return value;\n const trimmed = value.trim();\n return trimmed.length === 0 ? undefined : trimmed;\n }, z.string().min(1).max(max).optional());\n\nexport const AutomationScheduleSchema = z.discriminatedUnion('kind', [\n z.object({\n kind: z.literal('once'),\n at: nonEmptyString,\n }).strict(),\n z.object({\n kind: z.literal('interval'),\n everyMs: z.number().int().min(1),\n anchorMs: z.number().int().nonnegative().optional(),\n }).strict(),\n z.object({\n kind: z.literal('cron'),\n expr: nonEmptyString.superRefine((expr, ctx) => {\n try {\n CronExpressionParser.parse(expr);\n } catch {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `Invalid cron expression: ${expr}`,\n });\n }\n }),\n tz: optionalTrimmedString(100),\n }).strict(),\n]);\n\nexport const AutomationTriggerSchema = z.discriminatedUnion('kind', [\n z.object({ kind: z.literal('manual') }).strict(),\n z.object({\n kind: z.literal('schedule'),\n schedule: AutomationScheduleSchema,\n }).strict(),\n z.object({\n kind: z.literal('webhook'),\n secretId: optionalTrimmedString(200),\n }).strict(),\n z.object({\n kind: z.literal('event'),\n eventType: nonEmptyString.max(200),\n source: optionalTrimmedString(200),\n payloadMatch: z.record(\n z.string(),\n z.union([z.string(), z.number(), z.boolean(), z.null()]),\n ).optional(),\n }).strict(),\n]);\n\nconst WorkflowRunInputEnvelopeSchema = z.object({\n payload: z.unknown(),\n goal: optionalTrimmedString(5000),\n variables: z.record(z.string(), z.unknown()).optional(),\n context: z.record(z.string(), z.unknown()).optional(),\n}).strict();\n\nexport const AutomationActionSchema = z.discriminatedUnion('kind', [\n z.object({\n kind: z.literal('agent'),\n agentId: optionalTrimmedString(64),\n instruction: nonEmptyString.max(50000),\n workingDirectory: optionalTrimmedString(4096),\n model: optionalTrimmedString(100),\n timeoutSeconds: z.number().int().min(1).max(86400).optional(),\n }).strict(),\n z.object({\n kind: z.literal('workflow'),\n workflowId: nonEmptyString.max(200),\n agentId: optionalTrimmedString(64),\n input: z.unknown().optional(),\n inputEnvelope: WorkflowRunInputEnvelopeSchema.optional(),\n goal: optionalTrimmedString(5000),\n concurrency: z.number().int().min(1).max(50).optional(),\n maxSubagents: z.number().int().min(1).max(100).optional(),\n timeoutSeconds: z.number().int().min(1).max(86400).optional(),\n }).strict(),\n]);\n\nexport const AutomationAfterRunSchema = z.discriminatedUnion('kind', [\n z.object({ kind: z.literal('none') }).strict(),\n z.object({ kind: z.literal('saveToSession') }).strict(),\n z.object({\n kind: z.literal('webhook'),\n url: nonEmptyString.max(2000),\n }).strict(),\n]);\n\nexport const AutomationReliabilitySchema = z.object({\n timeoutSeconds: z.number().int().min(1).max(86400).optional(),\n retryCount: z.number().int().min(0).max(10).optional(),\n maxConcurrentRuns: z.number().int().min(1).max(20).optional(),\n disableAfterConsecutiveFailures: z.number().int().min(1).max(100).optional(),\n}).strict();\n\nexport const AutomationSafetyPolicySchema = z.object({\n mode: z.enum(['suggest_only', 'ask_before_apply', 'auto_apply']),\n}).strict();\n\nexport const AutomationStateSchema = z.object({\n nextRunAtMs: z.number().int().nonnegative().optional(),\n runningRunId: optionalTrimmedString(100),\n lastRunAtMs: z.number().int().nonnegative().optional(),\n lastRunStatus: z.enum(['queued', 'running', 'succeeded', 'failed', 'cancelled', 'timeout']).optional(),\n lastError: z.string().optional(),\n consecutiveFailures: z.number().int().nonnegative().optional(),\n}).strict();\n\nexport const AutomationSchema = z.object({\n id: nonEmptyString.max(100),\n name: nonEmptyString.max(200),\n description: z.string().max(2000).optional(),\n enabled: z.boolean(),\n trigger: AutomationTriggerSchema,\n action: AutomationActionSchema,\n safety: AutomationSafetyPolicySchema.optional(),\n afterRun: AutomationAfterRunSchema.optional(),\n reliability: AutomationReliabilitySchema.optional(),\n state: AutomationStateSchema.default({}),\n createdAtMs: z.number().int().nonnegative(),\n updatedAtMs: z.number().int().nonnegative(),\n}).strict();\n\nexport const CreateAutomationSchema = AutomationSchema.omit({\n id: true,\n enabled: true,\n state: true,\n createdAtMs: true,\n updatedAtMs: true,\n}).extend({\n id: optionalTrimmedString(100),\n enabled: z.boolean().optional(),\n state: AutomationStateSchema.partial().optional(),\n});\n\nexport const UpdateAutomationSchema = AutomationSchema.omit({\n id: true,\n createdAtMs: true,\n updatedAtMs: true,\n}).partial().refine(\n (data) => Object.keys(data).length > 0,\n { message: 'At least one field must be provided' },\n);\n\nexport type CreateAutomationInput = z.infer<typeof CreateAutomationSchema>;\nexport type UpdateAutomationInput = z.infer<typeof UpdateAutomationSchema>;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"validation.js","names":[],"sources":["../../../../src/automations/domain/validation.ts"],"sourcesContent":["import { CronExpressionParser } from 'cron-parser';\nimport { z } from 'zod';\n\nconst nonEmptyString = z.string().trim().min(1);\n\nconst optionalTrimmedString = (max: number) =>\n z.preprocess((value) => {\n if (value == null) return undefined;\n if (typeof value !== 'string') return value;\n const trimmed = value.trim();\n return trimmed.length === 0 ? undefined : trimmed;\n }, z.string().min(1).max(max).optional());\n\nexport const AutomationScheduleSchema = z.discriminatedUnion('kind', [\n z.object({\n kind: z.literal('once'),\n at: nonEmptyString,\n }).strict(),\n z.object({\n kind: z.literal('interval'),\n everyMs: z.number().int().min(1),\n anchorMs: z.number().int().nonnegative().optional(),\n }).strict(),\n z.object({\n kind: z.literal('cron'),\n expr: nonEmptyString.superRefine((expr, ctx) => {\n try {\n CronExpressionParser.parse(expr);\n } catch {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `Invalid cron expression: ${expr}`,\n });\n }\n }),\n tz: optionalTrimmedString(100),\n }).strict(),\n]);\n\nexport const AutomationTriggerSchema = z.discriminatedUnion('kind', [\n z.object({ kind: z.literal('manual') }).strict(),\n z.object({\n kind: z.literal('schedule'),\n schedule: AutomationScheduleSchema,\n }).strict(),\n z.object({\n kind: z.literal('webhook'),\n secretId: optionalTrimmedString(200),\n }).strict(),\n z.object({\n kind: z.literal('event'),\n eventType: nonEmptyString.max(200),\n source: optionalTrimmedString(200),\n payloadMatch: z.record(\n z.string(),\n z.union([z.string(), z.number(), z.boolean(), z.null()]),\n ).optional(),\n }).strict(),\n]);\n\nconst WorkflowRunInputEnvelopeSchema = z.object({\n payload: z.unknown(),\n goal: optionalTrimmedString(5000),\n variables: z.record(z.string(), z.unknown()).optional(),\n context: z.record(z.string(), z.unknown()).optional(),\n}).strict();\n\nexport const AutomationActionSchema = z.discriminatedUnion('kind', [\n z.object({\n kind: z.literal('agent'),\n agentId: optionalTrimmedString(64),\n instruction: nonEmptyString.max(50000),\n workingDirectory: optionalTrimmedString(4096),\n model: optionalTrimmedString(100),\n timeoutSeconds: z.number().int().min(1).max(86400).optional(),\n }).strict(),\n z.object({\n kind: z.literal('workflow'),\n workflowId: nonEmptyString.max(200),\n agentId: optionalTrimmedString(64),\n input: z.unknown().optional(),\n inputEnvelope: WorkflowRunInputEnvelopeSchema.optional(),\n goal: optionalTrimmedString(5000),\n concurrency: z.number().int().min(1).max(50).optional(),\n maxSubagents: z.number().int().min(1).max(100).optional(),\n timeoutSeconds: z.number().int().min(1).max(86400).optional(),\n }).strict(),\n]);\n\nexport const AutomationAfterRunSchema = z.discriminatedUnion('kind', [\n z.object({ kind: z.literal('none') }).strict(),\n z.object({ kind: z.literal('saveToSession') }).strict(),\n z.object({\n kind: z.literal('webhook'),\n url: nonEmptyString.max(2000),\n }).strict(),\n]);\n\nexport const AutomationReliabilitySchema = z.object({\n timeoutSeconds: z.number().int().min(1).max(86400).optional(),\n retryCount: z.number().int().min(0).max(10).optional(),\n maxConcurrentRuns: z.number().int().min(1).max(20).optional(),\n disableAfterConsecutiveFailures: z.number().int().min(1).max(100).optional(),\n}).strict();\n\nexport const AutomationSafetyPolicySchema = z.object({\n mode: z.enum(['suggest_only', 'ask_before_apply', 'auto_apply']),\n}).strict();\n\nexport const AutomationStateSchema = z.object({\n nextRunAtMs: z.number().int().nonnegative().optional(),\n runningRunId: optionalTrimmedString(100),\n lastRunAtMs: z.number().int().nonnegative().optional(),\n lastRunStatus: z.enum(['queued', 'running', 'succeeded', 'failed', 'cancelled', 'timeout']).optional(),\n lastError: z.string().optional(),\n consecutiveFailures: z.number().int().nonnegative().optional(),\n}).strict();\n\nexport const AutomationSchema = z.object({\n id: nonEmptyString.max(100),\n name: nonEmptyString.max(200),\n description: z.string().max(2000).optional(),\n projectId: optionalTrimmedString(100),\n enabled: z.boolean(),\n trigger: AutomationTriggerSchema,\n action: AutomationActionSchema,\n safety: AutomationSafetyPolicySchema.optional(),\n afterRun: AutomationAfterRunSchema.optional(),\n reliability: AutomationReliabilitySchema.optional(),\n state: AutomationStateSchema.default({}),\n createdAtMs: z.number().int().nonnegative(),\n updatedAtMs: z.number().int().nonnegative(),\n}).strict();\n\nexport const CreateAutomationSchema = AutomationSchema.omit({\n id: true,\n enabled: true,\n state: true,\n createdAtMs: true,\n updatedAtMs: true,\n}).extend({\n id: optionalTrimmedString(100),\n enabled: z.boolean().optional(),\n state: AutomationStateSchema.partial().optional(),\n});\n\nexport const UpdateAutomationSchema = AutomationSchema.omit({\n id: true,\n createdAtMs: true,\n updatedAtMs: true,\n}).partial().refine(\n (data) => Object.keys(data).length > 0,\n { message: 'At least one field must be provided' },\n);\n\nexport type CreateAutomationInput = z.infer<typeof CreateAutomationSchema>;\nexport type UpdateAutomationInput = z.infer<typeof UpdateAutomationSchema>;\n"],"mappings":"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"}
|
|
@@ -4,7 +4,7 @@ import { init_providers, resolveModel } from "../../providers/index.js";
|
|
|
4
4
|
import { resolveDraftModelRef } from "../../workflows/draft/workflow-draft-service.js";
|
|
5
5
|
import { buildAutomationDraftPrompt, buildAutomationDraftRepairPrompt, buildAutomationRunRepairPrompt, buildAutomationRunRepairRetryPrompt } from "./automation-draft-prompt.js";
|
|
6
6
|
import { buildAutomationDraftResponse, parseGeneratedAutomationDraft, parseGeneratedAutomationRepairDraft } from "./automation-draft-validator.js";
|
|
7
|
-
import { complete } from "@earendil-works/pi-ai";
|
|
7
|
+
import { complete } from "@earendil-works/pi-ai/compat";
|
|
8
8
|
//#region src/automations/draft/automation-draft-service.ts
|
|
9
9
|
init_providers();
|
|
10
10
|
init_logger();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"automation-draft-service.js","names":[],"sources":["../../../../src/automations/draft/automation-draft-service.ts"],"sourcesContent":["import { complete, type UserMessage } from '@earendil-works/pi-ai';\n\nimport type { Config } from '../../config/schema.js';\nimport { resolveModel } from '../../providers/index.js';\nimport { createLogger } from '../../utils/logger.js';\nimport { resolveDraftModelRef } from '../../workflows/draft/workflow-draft-service.js';\nimport {\n buildAutomationDraftPrompt,\n buildAutomationDraftRepairPrompt,\n buildAutomationRunRepairPrompt,\n buildAutomationRunRepairRetryPrompt,\n type AutomationDraftRepairIssue,\n} from './automation-draft-prompt.js';\nimport type {\n AutomationDraftResponse,\n AutomationRepairDraftResponse,\n CreateAutomationDraftRequest,\n CreateAutomationRepairDraftRequest,\n} from './automation-draft.types.js';\nimport {\n buildAutomationDraftResponse,\n parseGeneratedAutomationDraft,\n parseGeneratedAutomationRepairDraft,\n} from './automation-draft-validator.js';\n\nconst log = createLogger('AutomationDraft');\nconst DEFAULT_MAX_REPAIR_ATTEMPTS = 2;\n\nexport interface AutomationDraftServiceOptions {\n config: Config;\n maxRepairAttempts?: number;\n}\n\nexport class AutomationDraftService {\n constructor(private readonly options: AutomationDraftServiceOptions) {}\n\n async createDraft(request: CreateAutomationDraftRequest, signal?: AbortSignal): Promise<AutomationDraftResponse> {\n const prompt = request.prompt.trim();\n if (!prompt) throw new Error('prompt is required');\n\n const modelRef = resolveDraftModelRef(this.options.config, request.agentId);\n const model = resolveModel(modelRef);\n const maxRepairAttempts = this.options.maxRepairAttempts ?? DEFAULT_MAX_REPAIR_ATTEMPTS;\n let messageContent = buildAutomationDraftPrompt({ prompt, language: request.language });\n let lastText = '';\n let lastIssues: AutomationDraftRepairIssue[] = [];\n\n for (let attempt = 0; attempt <= maxRepairAttempts; attempt += 1) {\n const userMsg: UserMessage = {\n role: 'user',\n content: messageContent,\n timestamp: Date.now(),\n };\n const result = await complete(\n model,\n { messages: [userMsg] },\n { maxTokens: 2048, temperature: attempt === 0 ? 0.2 : 0.1, signal },\n );\n lastText = extractText(result);\n try {\n const draft = parseGeneratedAutomationDraft(lastText);\n return buildAutomationDraftResponse(draft, attempt);\n } catch (err) {\n lastIssues = [{\n source: 'validation',\n severity: 'error',\n message: err instanceof Error ? err.message : String(err),\n }];\n }\n\n if (attempt >= maxRepairAttempts) break;\n log.warn(\n { modelRef, attempt: attempt + 1, issueCount: lastIssues.length, preview: lastText.slice(0, 500) },\n 'Automation draft validation failed; asking model to repair',\n );\n messageContent = buildAutomationDraftRepairPrompt({\n prompt,\n language: request.language,\n previousOutput: lastText,\n issues: lastIssues,\n });\n }\n\n const message = `Unable to generate a valid automation draft after ${maxRepairAttempts + 1} attempts`;\n log.warn({ modelRef, issues: lastIssues, preview: lastText.slice(0, 500) }, message);\n throw new Error(message);\n }\n\n async createRepairDraft(\n request: CreateAutomationRepairDraftRequest,\n signal?: AbortSignal,\n ): Promise<AutomationRepairDraftResponse> {\n const modelRef = resolveDraftModelRef(this.options.config, request.agentId);\n const model = resolveModel(modelRef);\n const maxRepairAttempts = this.options.maxRepairAttempts ?? DEFAULT_MAX_REPAIR_ATTEMPTS;\n let messageContent = buildAutomationRunRepairPrompt({\n language: request.language,\n automation: request.automation,\n run: request.run,\n events: request.events,\n });\n let lastText = '';\n let lastIssues: AutomationDraftRepairIssue[] = [];\n\n for (let attempt = 0; attempt <= maxRepairAttempts; attempt += 1) {\n const userMsg: UserMessage = {\n role: 'user',\n content: messageContent,\n timestamp: Date.now(),\n };\n const result = await complete(\n model,\n { messages: [userMsg] },\n { maxTokens: 2048, temperature: attempt === 0 ? 0.15 : 0.05, signal },\n );\n lastText = extractText(result);\n try {\n return parseGeneratedAutomationRepairDraft(lastText, attempt);\n } catch (err) {\n lastIssues = [{\n source: 'validation',\n severity: 'error',\n message: err instanceof Error ? err.message : String(err),\n }];\n }\n\n if (attempt >= maxRepairAttempts) break;\n log.warn(\n { modelRef, attempt: attempt + 1, issueCount: lastIssues.length, preview: lastText.slice(0, 500) },\n 'Automation repair draft validation failed; asking model to repair',\n );\n messageContent = buildAutomationRunRepairRetryPrompt({\n language: request.language,\n previousOutput: lastText,\n issues: lastIssues,\n });\n }\n\n const message = `Unable to generate a valid automation repair draft after ${maxRepairAttempts + 1} attempts`;\n log.warn({ modelRef, issues: lastIssues, preview: lastText.slice(0, 500) }, message);\n throw new Error(message);\n }\n}\n\nfunction extractText(result: unknown): string {\n const content = (result as { content?: unknown }).content;\n if (typeof content === 'string') return content;\n if (Array.isArray(content)) {\n return content.map((item) => {\n if (item && typeof item === 'object' && (item as { type?: string }).type === 'text') {\n return String((item as { text?: string }).text ?? '');\n }\n return '';\n }).join('');\n }\n return '';\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"automation-draft-service.js","names":[],"sources":["../../../../src/automations/draft/automation-draft-service.ts"],"sourcesContent":["import { complete, type UserMessage } from '@earendil-works/pi-ai/compat';\n\nimport type { Config } from '../../config/schema.js';\nimport { resolveModel } from '../../providers/index.js';\nimport { createLogger } from '../../utils/logger.js';\nimport { resolveDraftModelRef } from '../../workflows/draft/workflow-draft-service.js';\nimport {\n buildAutomationDraftPrompt,\n buildAutomationDraftRepairPrompt,\n buildAutomationRunRepairPrompt,\n buildAutomationRunRepairRetryPrompt,\n type AutomationDraftRepairIssue,\n} from './automation-draft-prompt.js';\nimport type {\n AutomationDraftResponse,\n AutomationRepairDraftResponse,\n CreateAutomationDraftRequest,\n CreateAutomationRepairDraftRequest,\n} from './automation-draft.types.js';\nimport {\n buildAutomationDraftResponse,\n parseGeneratedAutomationDraft,\n parseGeneratedAutomationRepairDraft,\n} from './automation-draft-validator.js';\n\nconst log = createLogger('AutomationDraft');\nconst DEFAULT_MAX_REPAIR_ATTEMPTS = 2;\n\nexport interface AutomationDraftServiceOptions {\n config: Config;\n maxRepairAttempts?: number;\n}\n\nexport class AutomationDraftService {\n constructor(private readonly options: AutomationDraftServiceOptions) {}\n\n async createDraft(request: CreateAutomationDraftRequest, signal?: AbortSignal): Promise<AutomationDraftResponse> {\n const prompt = request.prompt.trim();\n if (!prompt) throw new Error('prompt is required');\n\n const modelRef = resolveDraftModelRef(this.options.config, request.agentId);\n const model = resolveModel(modelRef);\n const maxRepairAttempts = this.options.maxRepairAttempts ?? DEFAULT_MAX_REPAIR_ATTEMPTS;\n let messageContent = buildAutomationDraftPrompt({ prompt, language: request.language });\n let lastText = '';\n let lastIssues: AutomationDraftRepairIssue[] = [];\n\n for (let attempt = 0; attempt <= maxRepairAttempts; attempt += 1) {\n const userMsg: UserMessage = {\n role: 'user',\n content: messageContent,\n timestamp: Date.now(),\n };\n const result = await complete(\n model,\n { messages: [userMsg] },\n { maxTokens: 2048, temperature: attempt === 0 ? 0.2 : 0.1, signal },\n );\n lastText = extractText(result);\n try {\n const draft = parseGeneratedAutomationDraft(lastText);\n return buildAutomationDraftResponse(draft, attempt);\n } catch (err) {\n lastIssues = [{\n source: 'validation',\n severity: 'error',\n message: err instanceof Error ? err.message : String(err),\n }];\n }\n\n if (attempt >= maxRepairAttempts) break;\n log.warn(\n { modelRef, attempt: attempt + 1, issueCount: lastIssues.length, preview: lastText.slice(0, 500) },\n 'Automation draft validation failed; asking model to repair',\n );\n messageContent = buildAutomationDraftRepairPrompt({\n prompt,\n language: request.language,\n previousOutput: lastText,\n issues: lastIssues,\n });\n }\n\n const message = `Unable to generate a valid automation draft after ${maxRepairAttempts + 1} attempts`;\n log.warn({ modelRef, issues: lastIssues, preview: lastText.slice(0, 500) }, message);\n throw new Error(message);\n }\n\n async createRepairDraft(\n request: CreateAutomationRepairDraftRequest,\n signal?: AbortSignal,\n ): Promise<AutomationRepairDraftResponse> {\n const modelRef = resolveDraftModelRef(this.options.config, request.agentId);\n const model = resolveModel(modelRef);\n const maxRepairAttempts = this.options.maxRepairAttempts ?? DEFAULT_MAX_REPAIR_ATTEMPTS;\n let messageContent = buildAutomationRunRepairPrompt({\n language: request.language,\n automation: request.automation,\n run: request.run,\n events: request.events,\n });\n let lastText = '';\n let lastIssues: AutomationDraftRepairIssue[] = [];\n\n for (let attempt = 0; attempt <= maxRepairAttempts; attempt += 1) {\n const userMsg: UserMessage = {\n role: 'user',\n content: messageContent,\n timestamp: Date.now(),\n };\n const result = await complete(\n model,\n { messages: [userMsg] },\n { maxTokens: 2048, temperature: attempt === 0 ? 0.15 : 0.05, signal },\n );\n lastText = extractText(result);\n try {\n return parseGeneratedAutomationRepairDraft(lastText, attempt);\n } catch (err) {\n lastIssues = [{\n source: 'validation',\n severity: 'error',\n message: err instanceof Error ? err.message : String(err),\n }];\n }\n\n if (attempt >= maxRepairAttempts) break;\n log.warn(\n { modelRef, attempt: attempt + 1, issueCount: lastIssues.length, preview: lastText.slice(0, 500) },\n 'Automation repair draft validation failed; asking model to repair',\n );\n messageContent = buildAutomationRunRepairRetryPrompt({\n language: request.language,\n previousOutput: lastText,\n issues: lastIssues,\n });\n }\n\n const message = `Unable to generate a valid automation repair draft after ${maxRepairAttempts + 1} attempts`;\n log.warn({ modelRef, issues: lastIssues, preview: lastText.slice(0, 500) }, message);\n throw new Error(message);\n }\n}\n\nfunction extractText(result: unknown): string {\n const content = (result as { content?: unknown }).content;\n if (typeof content === 'string') return content;\n if (Array.isArray(content)) {\n return content.map((item) => {\n if (item && typeof item === 'object' && (item as { type?: string }).type === 'text') {\n return String((item as { text?: string }).text ?? '');\n }\n return '';\n }).join('');\n }\n return '';\n}\n"],"mappings":"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"}
|
|
@@ -71,7 +71,8 @@ var AutomationActionExecutor = class {
|
|
|
71
71
|
peerKind: "dm",
|
|
72
72
|
peerId: `${automation.id}-${crypto.randomUUID().slice(0, 8)}`
|
|
73
73
|
});
|
|
74
|
-
|
|
74
|
+
const effectiveWorkingDirectory = action.workingDirectory ?? (automation.projectId ? this.deps.getProjectWorkspaceRoot?.(automation.projectId) : void 0);
|
|
75
|
+
await agentService.sessionConfig?.applyAutomationWorkingDirectory?.(sessionKey, effectiveWorkingDirectory);
|
|
75
76
|
if (agentService.sessionConfig?.applyAutomationModelOverride) {
|
|
76
77
|
if (!await agentService.sessionConfig.applyAutomationModelOverride(sessionKey, action.model) && action.model) log.warn({
|
|
77
78
|
automationId: automation.id,
|
|
@@ -111,6 +112,7 @@ var AutomationActionExecutor = class {
|
|
|
111
112
|
input: action.input,
|
|
112
113
|
inputEnvelope: action.inputEnvelope,
|
|
113
114
|
goal: action.goal,
|
|
115
|
+
projectId: automation.projectId,
|
|
114
116
|
concurrency: action.concurrency,
|
|
115
117
|
maxSubagents: action.maxSubagents,
|
|
116
118
|
source: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"action-executor.js","names":[],"sources":["../../../../src/automations/service/action-executor.ts"],"sourcesContent":["import crypto from 'node:crypto';\n\nimport { DEFAULT_AGENT_ID, normalizeAgentId } from '../../agent/agent-scope.js';\nimport { buildSessionKey } from '../../routing/session-key.js';\nimport { createLogger } from '../../utils/logger.js';\nimport type {\n Automation,\n AutomationAction,\n AutomationActionOutcome,\n AutomationDeps,\n AutomationRun,\n} from '../domain/types.js';\n\nconst log = createLogger('Automation:ActionExecutor');\n\nconst DEFAULT_TIMEOUT_MS = 180_000;\n\nfunction withTimeout<T>(promise: Promise<T>, timeoutMs: number, signal: AbortSignal): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n if (signal.aborted) {\n reject(new Error('Automation run was cancelled'));\n return;\n }\n const timer = setTimeout(() => reject(new Error(`Automation timed out after ${timeoutMs}ms`)), timeoutMs);\n const onAbort = () => {\n clearTimeout(timer);\n reject(new Error('Automation run was cancelled'));\n };\n signal.addEventListener('abort', onAbort, { once: true });\n promise.then(\n (value) => {\n clearTimeout(timer);\n signal.removeEventListener('abort', onAbort);\n resolve(value);\n },\n (err) => {\n clearTimeout(timer);\n signal.removeEventListener('abort', onAbort);\n reject(err);\n },\n );\n });\n}\n\nexport class AutomationActionExecutor {\n private deps: AutomationDeps = {};\n\n setDeps(deps: AutomationDeps): void {\n this.deps = { ...this.deps, ...deps };\n }\n\n async execute(\n automation: Automation,\n run: AutomationRun,\n signal: AbortSignal,\n ): Promise<AutomationActionOutcome> {\n const timeoutMs =\n (automation.action.timeoutSeconds ?? automation.reliability?.timeoutSeconds ?? 0) > 0\n ? (automation.action.timeoutSeconds ?? automation.reliability?.timeoutSeconds)! * 1000\n : DEFAULT_TIMEOUT_MS;\n\n return withTimeout(this.executeWithoutTimeout(automation, run, signal), timeoutMs, signal).catch((err) => {\n const message = err instanceof Error ? err.message : String(err);\n return {\n status: message.toLowerCase().includes('cancelled')\n ? 'cancelled'\n : message.toLowerCase().includes('timed out')\n ? 'timeout'\n : 'failed',\n error: message,\n };\n });\n }\n\n private async executeWithoutTimeout(\n automation: Automation,\n run: AutomationRun,\n signal: AbortSignal,\n ): Promise<AutomationActionOutcome> {\n if (signal.aborted) {\n return { status: 'cancelled', error: 'Automation run was cancelled' };\n }\n if (automation.action.kind === 'workflow') {\n return this.executeWorkflow(automation, automation.action, run);\n }\n return this.executeAgent(automation, automation.action);\n }\n\n private async executeAgent(\n automation: Automation,\n action: Extract<AutomationAction, { kind: 'agent' }>,\n ): Promise<AutomationActionOutcome> {\n const agentService = this.deps.agentService;\n if (!agentService?.turnDispatcher?.processDirect) {\n return { status: 'failed', error: 'Agent service is not available' };\n }\n const agentId = normalizeAgentId(\n action.agentId || this.deps.getDefaultAgentId?.() || DEFAULT_AGENT_ID,\n );\n const sessionKey = buildSessionKey({\n agentId,\n source: 'automation',\n accountId: 'default',\n peerKind: 'dm',\n peerId: `${automation.id}-${crypto.randomUUID().slice(0, 8)}`,\n });\n\n await agentService.sessionConfig?.applyAutomationWorkingDirectory?.(\n sessionKey,\n action.workingDirectory,\n );\n if (agentService.sessionConfig?.applyAutomationModelOverride) {\n const ok = await agentService.sessionConfig.applyAutomationModelOverride(sessionKey, action.model);\n if (!ok && action.model) {\n log.warn(\n { automationId: automation.id, sessionKey, model: action.model },\n 'Automation model override invalid; using agent default',\n );\n }\n }\n\n const response = await agentService.turnDispatcher.processDirect(\n buildSafetyInstruction(automation, action.instruction),\n sessionKey,\n );\n const model = agentService.getModelForSession?.(sessionKey);\n return {\n status: 'succeeded',\n summary: response.slice(0, 500),\n sessionKey,\n model,\n };\n }\n\n private async executeWorkflow(\n automation: Automation,\n action: Extract<AutomationAction, { kind: 'workflow' }>,\n run: AutomationRun,\n ): Promise<AutomationActionOutcome> {\n const safetyMode = automation.safety?.mode ?? 'auto_apply';\n if (safetyMode === 'suggest_only') {\n return {\n status: 'succeeded',\n summary: `Suggest only: workflow ${action.workflowId} was not started. Review the automation and upgrade safety mode to run it automatically.`,\n };\n }\n if (safetyMode === 'ask_before_apply') {\n return {\n status: 'succeeded',\n summary: `Ask before applying: workflow ${action.workflowId} is ready, but was not started automatically. Upgrade to Auto apply when you trust this automation.`,\n };\n }\n const workflowRunService = this.deps.workflowRunService;\n if (!workflowRunService) {\n return { status: 'failed', error: 'Workflow service is not available' };\n }\n const agentId = normalizeAgentId(\n action.agentId || this.deps.getDefaultAgentId?.() || DEFAULT_AGENT_ID,\n );\n const idempotencyKey = `automation:${automation.id}:${run.id}`;\n const result = await workflowRunService.startWorkflowRun({\n agentId,\n definitionId: action.workflowId,\n input: action.input,\n inputEnvelope: action.inputEnvelope,\n goal: action.goal,\n concurrency: action.concurrency,\n maxSubagents: action.maxSubagents,\n source: { kind: 'automation', automationId: automation.id, runId: run.id },\n idempotencyKey,\n });\n if (result.ok === false) {\n return { status: 'failed', error: result.message };\n }\n return {\n status: 'succeeded',\n summary: `Started workflow run ${result.runId}`,\n sessionKey: result.sessionKey,\n workflowRunId: result.runId,\n };\n }\n}\n\nfunction buildSafetyInstruction(automation: Automation, instruction: string): string {\n const mode = automation.safety?.mode ?? 'auto_apply';\n if (mode === 'suggest_only') {\n return [\n 'Automation safety mode: Suggest only.',\n 'Only analyze the situation and produce a concise recommendation.',\n 'Do not modify files, notes, goals, workflows, external systems, or persistent state.',\n 'If a change seems useful, describe the exact change for the user to review.',\n '',\n instruction,\n ].join('\\n');\n }\n if (mode === 'ask_before_apply') {\n return [\n 'Automation safety mode: Ask before applying.',\n 'Draft the proposed change or action and clearly ask for user confirmation before applying anything.',\n 'Do not perform irreversible or external side effects unless the user has explicitly approved them in this run.',\n '',\n instruction,\n ].join('\\n');\n }\n return instruction;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"action-executor.js","names":[],"sources":["../../../../src/automations/service/action-executor.ts"],"sourcesContent":["import crypto from 'node:crypto';\n\nimport { DEFAULT_AGENT_ID, normalizeAgentId } from '../../agent/agent-scope.js';\nimport { buildSessionKey } from '../../routing/session-key.js';\nimport { createLogger } from '../../utils/logger.js';\nimport type {\n Automation,\n AutomationAction,\n AutomationActionOutcome,\n AutomationDeps,\n AutomationRun,\n} from '../domain/types.js';\n\nconst log = createLogger('Automation:ActionExecutor');\n\nconst DEFAULT_TIMEOUT_MS = 180_000;\n\nfunction withTimeout<T>(promise: Promise<T>, timeoutMs: number, signal: AbortSignal): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n if (signal.aborted) {\n reject(new Error('Automation run was cancelled'));\n return;\n }\n const timer = setTimeout(() => reject(new Error(`Automation timed out after ${timeoutMs}ms`)), timeoutMs);\n const onAbort = () => {\n clearTimeout(timer);\n reject(new Error('Automation run was cancelled'));\n };\n signal.addEventListener('abort', onAbort, { once: true });\n promise.then(\n (value) => {\n clearTimeout(timer);\n signal.removeEventListener('abort', onAbort);\n resolve(value);\n },\n (err) => {\n clearTimeout(timer);\n signal.removeEventListener('abort', onAbort);\n reject(err);\n },\n );\n });\n}\n\nexport class AutomationActionExecutor {\n private deps: AutomationDeps = {};\n\n setDeps(deps: AutomationDeps): void {\n this.deps = { ...this.deps, ...deps };\n }\n\n async execute(\n automation: Automation,\n run: AutomationRun,\n signal: AbortSignal,\n ): Promise<AutomationActionOutcome> {\n const timeoutMs =\n (automation.action.timeoutSeconds ?? automation.reliability?.timeoutSeconds ?? 0) > 0\n ? (automation.action.timeoutSeconds ?? automation.reliability?.timeoutSeconds)! * 1000\n : DEFAULT_TIMEOUT_MS;\n\n return withTimeout(this.executeWithoutTimeout(automation, run, signal), timeoutMs, signal).catch((err) => {\n const message = err instanceof Error ? err.message : String(err);\n return {\n status: message.toLowerCase().includes('cancelled')\n ? 'cancelled'\n : message.toLowerCase().includes('timed out')\n ? 'timeout'\n : 'failed',\n error: message,\n };\n });\n }\n\n private async executeWithoutTimeout(\n automation: Automation,\n run: AutomationRun,\n signal: AbortSignal,\n ): Promise<AutomationActionOutcome> {\n if (signal.aborted) {\n return { status: 'cancelled', error: 'Automation run was cancelled' };\n }\n if (automation.action.kind === 'workflow') {\n return this.executeWorkflow(automation, automation.action, run);\n }\n return this.executeAgent(automation, automation.action);\n }\n\n private async executeAgent(\n automation: Automation,\n action: Extract<AutomationAction, { kind: 'agent' }>,\n ): Promise<AutomationActionOutcome> {\n const agentService = this.deps.agentService;\n if (!agentService?.turnDispatcher?.processDirect) {\n return { status: 'failed', error: 'Agent service is not available' };\n }\n const agentId = normalizeAgentId(\n action.agentId || this.deps.getDefaultAgentId?.() || DEFAULT_AGENT_ID,\n );\n const sessionKey = buildSessionKey({\n agentId,\n source: 'automation',\n accountId: 'default',\n peerKind: 'dm',\n peerId: `${automation.id}-${crypto.randomUUID().slice(0, 8)}`,\n });\n const effectiveWorkingDirectory = action.workingDirectory\n ?? (automation.projectId ? this.deps.getProjectWorkspaceRoot?.(automation.projectId) : undefined);\n\n await agentService.sessionConfig?.applyAutomationWorkingDirectory?.(\n sessionKey,\n effectiveWorkingDirectory,\n );\n if (agentService.sessionConfig?.applyAutomationModelOverride) {\n const ok = await agentService.sessionConfig.applyAutomationModelOverride(sessionKey, action.model);\n if (!ok && action.model) {\n log.warn(\n { automationId: automation.id, sessionKey, model: action.model },\n 'Automation model override invalid; using agent default',\n );\n }\n }\n\n const response = await agentService.turnDispatcher.processDirect(\n buildSafetyInstruction(automation, action.instruction),\n sessionKey,\n );\n const model = agentService.getModelForSession?.(sessionKey);\n return {\n status: 'succeeded',\n summary: response.slice(0, 500),\n sessionKey,\n model,\n };\n }\n\n private async executeWorkflow(\n automation: Automation,\n action: Extract<AutomationAction, { kind: 'workflow' }>,\n run: AutomationRun,\n ): Promise<AutomationActionOutcome> {\n const safetyMode = automation.safety?.mode ?? 'auto_apply';\n if (safetyMode === 'suggest_only') {\n return {\n status: 'succeeded',\n summary: `Suggest only: workflow ${action.workflowId} was not started. Review the automation and upgrade safety mode to run it automatically.`,\n };\n }\n if (safetyMode === 'ask_before_apply') {\n return {\n status: 'succeeded',\n summary: `Ask before applying: workflow ${action.workflowId} is ready, but was not started automatically. Upgrade to Auto apply when you trust this automation.`,\n };\n }\n const workflowRunService = this.deps.workflowRunService;\n if (!workflowRunService) {\n return { status: 'failed', error: 'Workflow service is not available' };\n }\n const agentId = normalizeAgentId(\n action.agentId || this.deps.getDefaultAgentId?.() || DEFAULT_AGENT_ID,\n );\n const idempotencyKey = `automation:${automation.id}:${run.id}`;\n const result = await workflowRunService.startWorkflowRun({\n agentId,\n definitionId: action.workflowId,\n input: action.input,\n inputEnvelope: action.inputEnvelope,\n goal: action.goal,\n projectId: automation.projectId,\n concurrency: action.concurrency,\n maxSubagents: action.maxSubagents,\n source: { kind: 'automation', automationId: automation.id, runId: run.id },\n idempotencyKey,\n });\n if (result.ok === false) {\n return { status: 'failed', error: result.message };\n }\n return {\n status: 'succeeded',\n summary: `Started workflow run ${result.runId}`,\n sessionKey: result.sessionKey,\n workflowRunId: result.runId,\n };\n }\n}\n\nfunction buildSafetyInstruction(automation: Automation, instruction: string): string {\n const mode = automation.safety?.mode ?? 'auto_apply';\n if (mode === 'suggest_only') {\n return [\n 'Automation safety mode: Suggest only.',\n 'Only analyze the situation and produce a concise recommendation.',\n 'Do not modify files, notes, goals, workflows, external systems, or persistent state.',\n 'If a change seems useful, describe the exact change for the user to review.',\n '',\n instruction,\n ].join('\\n');\n }\n if (mode === 'ask_before_apply') {\n return [\n 'Automation safety mode: Ask before applying.',\n 'Draft the proposed change or action and clearly ask for user confirmation before applying anything.',\n 'Do not perform irreversible or external side effects unless the user has explicitly approved them in this run.',\n '',\n instruction,\n ].join('\\n');\n }\n return instruction;\n}\n"],"mappings":"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"}
|
|
@@ -19,7 +19,9 @@ export declare class AutomationService {
|
|
|
19
19
|
}): Promise<void>;
|
|
20
20
|
stop(): Promise<void>;
|
|
21
21
|
create(input: CreateAutomationInput): Promise<Automation>;
|
|
22
|
-
list(
|
|
22
|
+
list(options?: {
|
|
23
|
+
projectId?: string;
|
|
24
|
+
}): Promise<Automation[]>;
|
|
23
25
|
get(id: string): Promise<Automation | null>;
|
|
24
26
|
update(id: string, patch: UpdateAutomationInput): Promise<Automation | null>;
|
|
25
27
|
remove(id: string): Promise<boolean>;
|
|
@@ -30,6 +32,7 @@ export declare class AutomationService {
|
|
|
30
32
|
triggerEvent(event: AutomationEvent): Promise<AutomationRun[]>;
|
|
31
33
|
listRuns(options?: {
|
|
32
34
|
automationId?: string;
|
|
35
|
+
projectId?: string;
|
|
33
36
|
limit?: number;
|
|
34
37
|
}): Promise<AutomationRun[]>;
|
|
35
38
|
listRunsForProductEvent(options: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"automation-service.js","names":[],"sources":["../../../../src/automations/service/automation-service.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\n\nimport { createLogger } from '../../utils/logger.js';\nimport {\n computeNextAutomationRunAtMs,\n timerDelayUntil,\n} from '../domain/schedule.js';\nimport type {\n Automation,\n AutomationDeps,\n AutomationEvent,\n AutomationMetrics,\n AutomationProductEventRun,\n AutomationRun,\n AutomationRunEvent,\n AutomationRunEventType,\n AutomationRunStatus,\n} from '../domain/types.js';\nimport {\n CreateAutomationSchema,\n UpdateAutomationSchema,\n type CreateAutomationInput,\n type UpdateAutomationInput,\n AutomationSchema,\n} from '../domain/validation.js';\nimport {\n appendAutomationRunEvent,\n deleteAutomation,\n getAutomation,\n getAutomationRun,\n listAutomationRunEvents,\n listAutomationRuns,\n listAutomationRunsForProductEvent,\n listAutomations,\n saveAutomation,\n saveAutomationRun,\n saveAutomations,\n} from '../storage/index.js';\nimport { AutomationActionExecutor } from './action-executor.js';\n\nconst log = createLogger('AutomationService');\n\ntype TimerHandle = ReturnType<typeof setTimeout> & { unref?: () => void };\n\nconst DEFAULT_MAX_CONCURRENT_RUNS = 5;\nconst MISSED_RUN_STAGGER_MS = 5_000;\n\nexport class AutomationAlreadyRunningError extends Error {\n constructor(readonly automationId: string, readonly runningRunId?: string) {\n super(`Automation is already running: ${automationId}`);\n this.name = 'AutomationAlreadyRunningError';\n }\n}\n\nexport class AutomationService {\n private readonly executor = new AutomationActionExecutor();\n private timer: TimerHandle | null = null;\n private stopped = true;\n private runningScheduler = false;\n private deps: AutomationDeps = {};\n private maxConcurrentRuns = DEFAULT_MAX_CONCURRENT_RUNS;\n private readonly activeRuns = new Map<string, AbortController>();\n\n setDeps(deps: AutomationDeps): void {\n this.deps = { ...this.deps, ...deps };\n this.executor.setDeps(this.deps);\n }\n\n async initialize(options?: { maxConcurrentRuns?: number }): Promise<void> {\n this.maxConcurrentRuns = Math.max(1, Math.floor(options?.maxConcurrentRuns ?? DEFAULT_MAX_CONCURRENT_RUNS));\n this.stopped = false;\n await this.recomputeNextRuns({ catchUp: true });\n this.armTimer();\n log.info('Automation service initialized');\n }\n\n async stop(): Promise<void> {\n this.stopped = true;\n if (this.timer) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n for (const controller of this.activeRuns.values()) {\n controller.abort();\n }\n log.info('Automation service stopped');\n }\n\n async create(input: CreateAutomationInput): Promise<Automation> {\n const parsed = CreateAutomationSchema.parse(input);\n const now = Date.now();\n const automation = AutomationSchema.parse({\n ...parsed,\n id: parsed.id?.trim() || randomUUID().slice(0, 12),\n enabled: parsed.enabled ?? true,\n safety: parsed.safety ?? { mode: 'auto_apply' },\n afterRun: parsed.afterRun ?? { kind: 'none' },\n state: parsed.state ?? {},\n createdAtMs: now,\n updatedAtMs: now,\n }) as Automation;\n automation.state.nextRunAtMs = computeNextAutomationRunAtMs(automation, now);\n saveAutomation(automation);\n this.armTimer();\n return automation;\n }\n\n async list(): Promise<Automation[]> {\n return listAutomations();\n }\n\n async get(id: string): Promise<Automation | null> {\n return getAutomation(id);\n }\n\n async update(id: string, patch: UpdateAutomationInput): Promise<Automation | null> {\n const parsed = UpdateAutomationSchema.parse(patch);\n const current = getAutomation(id);\n if (!current) return null;\n const now = Date.now();\n const next = AutomationSchema.parse({\n ...current,\n ...parsed,\n id: current.id,\n createdAtMs: current.createdAtMs,\n updatedAtMs: now,\n state: {\n ...current.state,\n ...(parsed.state ?? {}),\n },\n }) as Automation;\n if ('enabled' in parsed || 'trigger' in parsed) {\n next.state.nextRunAtMs = computeNextAutomationRunAtMs(next, now);\n }\n saveAutomation(next);\n this.armTimer();\n return next;\n }\n\n async remove(id: string): Promise<boolean> {\n const automation = getAutomation(id);\n if (automation?.state.runningRunId) {\n this.activeRuns.get(automation.state.runningRunId)?.abort();\n }\n const removed = deleteAutomation(id);\n if (removed) {\n this.armTimer();\n }\n return removed;\n }\n\n async pause(id: string): Promise<Automation | null> {\n return this.update(id, { enabled: false });\n }\n\n async resume(id: string): Promise<Automation | null> {\n return this.update(id, { enabled: true });\n }\n\n async runNow(id: string): Promise<AutomationRun> {\n const automation = getAutomation(id);\n if (!automation) throw new Error(`Automation not found: ${id}`);\n if (automation.state.runningRunId) {\n throw new AutomationAlreadyRunningError(id, automation.state.runningRunId);\n }\n return this.startRun(automation, { manual: true });\n }\n\n async rerunFromRun(runId: string): Promise<AutomationRun> {\n const previous = getAutomationRun(runId);\n if (!previous) throw new Error(`Automation run not found: ${runId}`);\n const automation = getAutomation(previous.automationId);\n if (!automation) throw new Error(`Automation not found: ${previous.automationId}`);\n if (automation.state.runningRunId) {\n throw new AutomationAlreadyRunningError(automation.id, automation.state.runningRunId);\n }\n const triggerEvent = listAutomationRunEvents(runId)\n .map((event) => readAutomationEventFromRunEvent(event))\n .find((event): event is AutomationEvent => event !== null);\n return this.startRun(automation, triggerEvent ? { manual: false, event: triggerEvent } : { manual: true });\n }\n\n async triggerEvent(event: AutomationEvent): Promise<AutomationRun[]> {\n const normalized: AutomationEvent = {\n ...event,\n occurredAtMs: event.occurredAtMs ?? Date.now(),\n };\n const automations = listAutomations().filter(\n (automation) =>\n automation.enabled &&\n automation.trigger.kind === 'event' &&\n !automation.state.runningRunId &&\n matchesAutomationEvent(automation.trigger, normalized),\n );\n const availableSlots = Math.max(0, this.maxConcurrentRuns - this.activeRuns.size);\n const started: AutomationRun[] = [];\n for (const automation of automations.slice(0, availableSlots)) {\n started.push(await this.startRun(automation, { manual: false, event: normalized }));\n }\n return started;\n }\n\n async listRuns(options?: { automationId?: string; limit?: number }): Promise<AutomationRun[]> {\n return listAutomationRuns(options);\n }\n\n async listRunsForProductEvent(options: {\n eventType: string;\n source?: string;\n payloadKey?: string;\n payloadValue?: string;\n limit?: number;\n }): Promise<AutomationProductEventRun[]> {\n return listAutomationRunsForProductEvent(options);\n }\n\n async getRun(runId: string): Promise<AutomationRun | null> {\n return getAutomationRun(runId);\n }\n\n async listRunEvents(runId: string): Promise<AutomationRunEvent[]> {\n return listAutomationRunEvents(runId);\n }\n\n async cancelRun(runId: string): Promise<boolean> {\n const controller = this.activeRuns.get(runId);\n if (!controller) return false;\n controller.abort();\n return true;\n }\n\n async getMetrics(): Promise<AutomationMetrics> {\n const automations = listAutomations();\n const enabled = automations.filter((automation) => automation.enabled);\n const next = enabled\n .filter((automation) => automation.state.nextRunAtMs != null)\n .toSorted((a, b) => (a.state.nextRunAtMs ?? 0) - (b.state.nextRunAtMs ?? 0))[0];\n const oneHourAgo = Date.now() - 60 * 60_000;\n return {\n totalAutomations: automations.length,\n enabledAutomations: enabled.length,\n runningRuns: this.activeRuns.size,\n failedLastHour: automations.filter(\n (automation) =>\n (automation.state.lastRunStatus === 'failed' || automation.state.lastRunStatus === 'timeout') &&\n (automation.state.lastRunAtMs ?? 0) >= oneHourAgo,\n ).length,\n nextRun: next?.state.nextRunAtMs\n ? {\n automationId: next.id,\n name: next.name,\n runAtMs: next.state.nextRunAtMs,\n }\n : undefined,\n };\n }\n\n private async recomputeNextRuns(options?: { catchUp?: boolean }): Promise<void> {\n const now = Date.now();\n const automations = listAutomations().map((automation, index) => {\n const next: Automation = {\n ...automation,\n state: { ...automation.state },\n };\n if (next.state.runningRunId) {\n next.state.lastRunStatus = 'failed';\n next.state.lastError = 'Interrupted by gateway restart';\n next.state.consecutiveFailures = (next.state.consecutiveFailures ?? 0) + 1;\n delete next.state.runningRunId;\n }\n const computed = computeNextAutomationRunAtMs(next, now);\n next.state.nextRunAtMs =\n options?.catchUp && computed != null && computed <= now\n ? now + index * MISSED_RUN_STAGGER_MS\n : computed;\n return next;\n });\n saveAutomations(automations);\n }\n\n private armTimer(): void {\n if (this.stopped) return;\n if (this.timer) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n const next = listAutomations()\n .filter((automation) => automation.enabled && automation.state.nextRunAtMs != null)\n .toSorted((a, b) => (a.state.nextRunAtMs ?? 0) - (b.state.nextRunAtMs ?? 0))[0];\n if (!next?.state.nextRunAtMs) return;\n this.timer = setTimeout(() => {\n void this.onTimer();\n }, timerDelayUntil(next.state.nextRunAtMs)) as TimerHandle;\n this.timer.unref?.();\n }\n\n private async onTimer(): Promise<void> {\n if (this.stopped) return;\n if (this.runningScheduler) {\n this.armTimer();\n return;\n }\n this.runningScheduler = true;\n try {\n const now = Date.now();\n const due = listAutomations()\n .filter(\n (automation) =>\n automation.enabled &&\n automation.trigger.kind === 'schedule' &&\n automation.state.nextRunAtMs != null &&\n automation.state.nextRunAtMs <= now &&\n !automation.state.runningRunId,\n )\n .slice(0, this.maxConcurrentRuns);\n await Promise.all(due.map((automation) => this.startRun(automation, { manual: false })));\n } finally {\n this.runningScheduler = false;\n this.armTimer();\n }\n }\n\n private async startRun(\n automation: Automation,\n opts: { manual: boolean; event?: AutomationEvent },\n ): Promise<AutomationRun> {\n const now = Date.now();\n const run: AutomationRun = {\n id: randomUUID(),\n automationId: automation.id,\n automationName: automation.name,\n status: 'queued',\n triggerSnapshot: automation.trigger,\n actionSnapshot: automation.action,\n manual: opts.manual,\n createdAtMs: now,\n };\n saveAutomationRun(run);\n this.appendRunEvent(\n run,\n 'run.queued',\n opts.event\n ? `Event ${opts.event.type} queued automation`\n : opts.manual\n ? 'Manual run queued'\n : 'Scheduled run queued',\n {\n trigger: automation.trigger,\n actionKind: automation.action.kind,\n safety: automation.safety ?? { mode: 'auto_apply' },\n event: opts.event,\n },\n );\n const nextAutomation: Automation = {\n ...automation,\n state: {\n ...automation.state,\n runningRunId: run.id,\n },\n };\n saveAutomation(nextAutomation);\n\n void this.executeRun(nextAutomation, run).catch((err) => {\n log.error(\n { err, automationId: automation.id, runId: run.id },\n `Automation run failed: ${err instanceof Error ? err.message : String(err)}`,\n );\n });\n\n return run;\n }\n\n private async executeRun(automation: Automation, initialRun: AutomationRun): Promise<void> {\n const controller = new AbortController();\n this.activeRuns.set(initialRun.id, controller);\n const startedAtMs = Date.now();\n let run: AutomationRun = {\n ...initialRun,\n status: 'running',\n startedAtMs,\n };\n saveAutomationRun(run);\n this.appendRunEvent(run, 'run.started', 'Automation run started');\n\n let status: AutomationRunStatus = 'failed';\n let error: string | undefined;\n let activePhase: 'action' | 'after_run' = 'action';\n try {\n this.appendRunEvent(run, 'action.started', `Running ${automation.action.kind} action`, {\n actionKind: automation.action.kind,\n safety: automation.safety ?? { mode: 'auto_apply' },\n });\n const outcome = await this.executor.execute(automation, run, controller.signal);\n status = outcome.status;\n error = outcome.error;\n this.appendRunEvent(\n run,\n status === 'succeeded' ? 'action.completed' : 'action.failed',\n status === 'succeeded'\n ? `${automation.action.kind} action completed`\n : `${automation.action.kind} action ${status}`,\n {\n summary: outcome.summary,\n error: outcome.error,\n sessionKey: outcome.sessionKey,\n workflowRunId: outcome.workflowRunId,\n model: outcome.model,\n },\n );\n run = {\n ...run,\n status,\n summary: outcome.summary,\n error,\n sessionKey: outcome.sessionKey,\n workflowRunId: outcome.workflowRunId,\n model: outcome.model,\n };\n if ((automation.safety?.mode ?? 'auto_apply') !== 'auto_apply' && automation.afterRun?.kind === 'webhook') {\n this.appendRunEvent(run, 'after_run.completed', 'After-run webhook skipped by safety mode', {\n safety: automation.safety ?? { mode: 'auto_apply' },\n });\n } else if (automation.afterRun?.kind === 'webhook') {\n activePhase = 'after_run';\n this.appendRunEvent(run, 'after_run.started', 'Calling after-run webhook', {\n url: automation.afterRun.url,\n });\n await this.postAfterRunWebhook(automation.afterRun.url, run);\n this.appendRunEvent(run, 'after_run.completed', 'After-run webhook completed');\n }\n } catch (err) {\n error = err instanceof Error ? err.message : String(err);\n status = controller.signal.aborted ? 'cancelled' : 'failed';\n this.appendRunEvent(run, activePhase === 'after_run' ? 'after_run.failed' : 'action.failed', `Automation run ${status}`, {\n error,\n });\n run = { ...run, status, error };\n } finally {\n const endedAtMs = Date.now();\n run = {\n ...run,\n status,\n endedAtMs,\n durationMs: endedAtMs - startedAtMs,\n };\n saveAutomationRun(run);\n this.appendRunEvent(run, 'run.completed', `Automation run ${status}`, {\n status,\n durationMs: run.durationMs,\n error,\n });\n this.activeRuns.delete(initialRun.id);\n this.finishAutomationRun(automation.id, status, error, endedAtMs);\n }\n }\n\n private appendRunEvent(\n run: Pick<AutomationRun, 'id' | 'automationId'>,\n type: AutomationRunEventType,\n message: string,\n data?: unknown,\n ): void {\n appendAutomationRunEvent({\n id: randomUUID(),\n runId: run.id,\n automationId: run.automationId,\n type,\n message,\n data,\n createdAtMs: Date.now(),\n });\n }\n\n private finishAutomationRun(\n automationId: string,\n status: AutomationRunStatus,\n error: string | undefined,\n finishedAtMs: number,\n ): void {\n const current = getAutomation(automationId);\n if (!current) return;\n const failed = status === 'failed' || status === 'timeout';\n const consecutiveFailures = failed ? (current.state.consecutiveFailures ?? 0) + 1 : 0;\n const disableAfter = current.reliability?.disableAfterConsecutiveFailures;\n const shouldDisable = disableAfter != null && consecutiveFailures >= disableAfter;\n const next: Automation = {\n ...current,\n enabled: shouldDisable ? false : current.enabled,\n state: {\n ...current.state,\n lastRunAtMs: finishedAtMs,\n lastRunStatus: status,\n lastError: error,\n consecutiveFailures,\n },\n updatedAtMs: shouldDisable ? finishedAtMs : current.updatedAtMs,\n };\n delete next.state.runningRunId;\n next.state.nextRunAtMs = computeNextAutomationRunAtMs(next, finishedAtMs);\n saveAutomation(next);\n this.armTimer();\n }\n\n private async postAfterRunWebhook(url: string, run: AutomationRun): Promise<void> {\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ run }),\n });\n if (!response.ok) {\n const text = await response.text().catch(() => '');\n throw new Error(`Automation webhook failed: HTTP ${response.status}${text ? ` ${text.slice(0, 200)}` : ''}`);\n }\n }\n}\n\nfunction matchesAutomationEvent(\n trigger: Extract<Automation['trigger'], { kind: 'event' }>,\n event: AutomationEvent,\n): boolean {\n if (trigger.eventType !== event.type) return false;\n if (trigger.source && trigger.source !== event.source) return false;\n const payloadMatch = trigger.payloadMatch;\n if (!payloadMatch) return true;\n const payload = event.payload ?? {};\n return Object.entries(payloadMatch).every(([key, expected]) => Object.is(payload[key], expected));\n}\n\nfunction readAutomationEventFromRunEvent(event: AutomationRunEvent): AutomationEvent | null {\n if (!event.data || typeof event.data !== 'object' || Array.isArray(event.data)) return null;\n const raw = (event.data as { event?: unknown }).event;\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return null;\n const candidate = raw as Partial<AutomationEvent>;\n if (typeof candidate.type !== 'string' || !candidate.type.trim()) return null;\n return {\n type: candidate.type,\n source: typeof candidate.source === 'string' ? candidate.source : undefined,\n payload: candidate.payload && typeof candidate.payload === 'object' && !Array.isArray(candidate.payload)\n ? candidate.payload as Record<string, unknown>\n : undefined,\n occurredAtMs: typeof candidate.occurredAtMs === 'number' ? candidate.occurredAtMs : undefined,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"automation-service.js","names":[],"sources":["../../../../src/automations/service/automation-service.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\n\nimport { createLogger } from '../../utils/logger.js';\nimport {\n computeNextAutomationRunAtMs,\n timerDelayUntil,\n} from '../domain/schedule.js';\nimport type {\n Automation,\n AutomationDeps,\n AutomationEvent,\n AutomationMetrics,\n AutomationProductEventRun,\n AutomationRun,\n AutomationRunEvent,\n AutomationRunEventType,\n AutomationRunStatus,\n} from '../domain/types.js';\nimport {\n CreateAutomationSchema,\n UpdateAutomationSchema,\n type CreateAutomationInput,\n type UpdateAutomationInput,\n AutomationSchema,\n} from '../domain/validation.js';\nimport {\n appendAutomationRunEvent,\n deleteAutomation,\n getAutomation,\n getAutomationRun,\n listAutomationRunEvents,\n listAutomationRuns,\n listAutomationRunsForProductEvent,\n listAutomations,\n saveAutomation,\n saveAutomationRun,\n saveAutomations,\n} from '../storage/index.js';\nimport { AutomationActionExecutor } from './action-executor.js';\n\nconst log = createLogger('AutomationService');\n\ntype TimerHandle = ReturnType<typeof setTimeout> & { unref?: () => void };\n\nconst DEFAULT_MAX_CONCURRENT_RUNS = 5;\nconst MISSED_RUN_STAGGER_MS = 5_000;\n\nexport class AutomationAlreadyRunningError extends Error {\n constructor(readonly automationId: string, readonly runningRunId?: string) {\n super(`Automation is already running: ${automationId}`);\n this.name = 'AutomationAlreadyRunningError';\n }\n}\n\nexport class AutomationService {\n private readonly executor = new AutomationActionExecutor();\n private timer: TimerHandle | null = null;\n private stopped = true;\n private runningScheduler = false;\n private deps: AutomationDeps = {};\n private maxConcurrentRuns = DEFAULT_MAX_CONCURRENT_RUNS;\n private readonly activeRuns = new Map<string, AbortController>();\n\n setDeps(deps: AutomationDeps): void {\n this.deps = { ...this.deps, ...deps };\n this.executor.setDeps(this.deps);\n }\n\n async initialize(options?: { maxConcurrentRuns?: number }): Promise<void> {\n this.maxConcurrentRuns = Math.max(1, Math.floor(options?.maxConcurrentRuns ?? DEFAULT_MAX_CONCURRENT_RUNS));\n this.stopped = false;\n await this.recomputeNextRuns({ catchUp: true });\n this.armTimer();\n log.info('Automation service initialized');\n }\n\n async stop(): Promise<void> {\n this.stopped = true;\n if (this.timer) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n for (const controller of this.activeRuns.values()) {\n controller.abort();\n }\n log.info('Automation service stopped');\n }\n\n async create(input: CreateAutomationInput): Promise<Automation> {\n const parsed = CreateAutomationSchema.parse(input);\n const now = Date.now();\n const automation = AutomationSchema.parse({\n ...parsed,\n id: parsed.id?.trim() || randomUUID().slice(0, 12),\n enabled: parsed.enabled ?? true,\n safety: parsed.safety ?? { mode: 'auto_apply' },\n afterRun: parsed.afterRun ?? { kind: 'none' },\n state: parsed.state ?? {},\n createdAtMs: now,\n updatedAtMs: now,\n }) as Automation;\n automation.state.nextRunAtMs = computeNextAutomationRunAtMs(automation, now);\n saveAutomation(automation);\n this.armTimer();\n return automation;\n }\n\n async list(options?: { projectId?: string }): Promise<Automation[]> {\n return listAutomations(options);\n }\n\n async get(id: string): Promise<Automation | null> {\n return getAutomation(id);\n }\n\n async update(id: string, patch: UpdateAutomationInput): Promise<Automation | null> {\n const parsed = UpdateAutomationSchema.parse(patch);\n const current = getAutomation(id);\n if (!current) return null;\n const now = Date.now();\n const next = AutomationSchema.parse({\n ...current,\n ...parsed,\n id: current.id,\n createdAtMs: current.createdAtMs,\n updatedAtMs: now,\n state: {\n ...current.state,\n ...(parsed.state ?? {}),\n },\n }) as Automation;\n if ('enabled' in parsed || 'trigger' in parsed) {\n next.state.nextRunAtMs = computeNextAutomationRunAtMs(next, now);\n }\n saveAutomation(next);\n this.armTimer();\n return next;\n }\n\n async remove(id: string): Promise<boolean> {\n const automation = getAutomation(id);\n if (automation?.state.runningRunId) {\n this.activeRuns.get(automation.state.runningRunId)?.abort();\n }\n const removed = deleteAutomation(id);\n if (removed) {\n this.armTimer();\n }\n return removed;\n }\n\n async pause(id: string): Promise<Automation | null> {\n return this.update(id, { enabled: false });\n }\n\n async resume(id: string): Promise<Automation | null> {\n return this.update(id, { enabled: true });\n }\n\n async runNow(id: string): Promise<AutomationRun> {\n const automation = getAutomation(id);\n if (!automation) throw new Error(`Automation not found: ${id}`);\n if (automation.state.runningRunId) {\n throw new AutomationAlreadyRunningError(id, automation.state.runningRunId);\n }\n return this.startRun(automation, { manual: true });\n }\n\n async rerunFromRun(runId: string): Promise<AutomationRun> {\n const previous = getAutomationRun(runId);\n if (!previous) throw new Error(`Automation run not found: ${runId}`);\n const automation = getAutomation(previous.automationId);\n if (!automation) throw new Error(`Automation not found: ${previous.automationId}`);\n if (automation.state.runningRunId) {\n throw new AutomationAlreadyRunningError(automation.id, automation.state.runningRunId);\n }\n const triggerEvent = listAutomationRunEvents(runId)\n .map((event) => readAutomationEventFromRunEvent(event))\n .find((event): event is AutomationEvent => event !== null);\n return this.startRun(automation, triggerEvent ? { manual: false, event: triggerEvent } : { manual: true });\n }\n\n async triggerEvent(event: AutomationEvent): Promise<AutomationRun[]> {\n const normalized: AutomationEvent = {\n ...event,\n occurredAtMs: event.occurredAtMs ?? Date.now(),\n };\n const automations = listAutomations().filter(\n (automation) =>\n automation.enabled &&\n automation.trigger.kind === 'event' &&\n !automation.state.runningRunId &&\n matchesAutomationEvent(automation.trigger, normalized),\n );\n const availableSlots = Math.max(0, this.maxConcurrentRuns - this.activeRuns.size);\n const started: AutomationRun[] = [];\n for (const automation of automations.slice(0, availableSlots)) {\n started.push(await this.startRun(automation, { manual: false, event: normalized }));\n }\n return started;\n }\n\n async listRuns(options?: { automationId?: string; projectId?: string; limit?: number }): Promise<AutomationRun[]> {\n return listAutomationRuns(options);\n }\n\n async listRunsForProductEvent(options: {\n eventType: string;\n source?: string;\n payloadKey?: string;\n payloadValue?: string;\n limit?: number;\n }): Promise<AutomationProductEventRun[]> {\n return listAutomationRunsForProductEvent(options);\n }\n\n async getRun(runId: string): Promise<AutomationRun | null> {\n return getAutomationRun(runId);\n }\n\n async listRunEvents(runId: string): Promise<AutomationRunEvent[]> {\n return listAutomationRunEvents(runId);\n }\n\n async cancelRun(runId: string): Promise<boolean> {\n const controller = this.activeRuns.get(runId);\n if (!controller) return false;\n controller.abort();\n return true;\n }\n\n async getMetrics(): Promise<AutomationMetrics> {\n const automations = listAutomations();\n const enabled = automations.filter((automation) => automation.enabled);\n const next = enabled\n .filter((automation) => automation.state.nextRunAtMs != null)\n .toSorted((a, b) => (a.state.nextRunAtMs ?? 0) - (b.state.nextRunAtMs ?? 0))[0];\n const oneHourAgo = Date.now() - 60 * 60_000;\n return {\n totalAutomations: automations.length,\n enabledAutomations: enabled.length,\n runningRuns: this.activeRuns.size,\n failedLastHour: automations.filter(\n (automation) =>\n (automation.state.lastRunStatus === 'failed' || automation.state.lastRunStatus === 'timeout') &&\n (automation.state.lastRunAtMs ?? 0) >= oneHourAgo,\n ).length,\n nextRun: next?.state.nextRunAtMs\n ? {\n automationId: next.id,\n name: next.name,\n runAtMs: next.state.nextRunAtMs,\n }\n : undefined,\n };\n }\n\n private async recomputeNextRuns(options?: { catchUp?: boolean }): Promise<void> {\n const now = Date.now();\n const automations = listAutomations().map((automation, index) => {\n const next: Automation = {\n ...automation,\n state: { ...automation.state },\n };\n if (next.state.runningRunId) {\n next.state.lastRunStatus = 'failed';\n next.state.lastError = 'Interrupted by gateway restart';\n next.state.consecutiveFailures = (next.state.consecutiveFailures ?? 0) + 1;\n delete next.state.runningRunId;\n }\n const computed = computeNextAutomationRunAtMs(next, now);\n next.state.nextRunAtMs =\n options?.catchUp && computed != null && computed <= now\n ? now + index * MISSED_RUN_STAGGER_MS\n : computed;\n return next;\n });\n saveAutomations(automations);\n }\n\n private armTimer(): void {\n if (this.stopped) return;\n if (this.timer) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n const next = listAutomations()\n .filter((automation) => automation.enabled && automation.state.nextRunAtMs != null)\n .toSorted((a, b) => (a.state.nextRunAtMs ?? 0) - (b.state.nextRunAtMs ?? 0))[0];\n if (!next?.state.nextRunAtMs) return;\n this.timer = setTimeout(() => {\n void this.onTimer();\n }, timerDelayUntil(next.state.nextRunAtMs)) as TimerHandle;\n this.timer.unref?.();\n }\n\n private async onTimer(): Promise<void> {\n if (this.stopped) return;\n if (this.runningScheduler) {\n this.armTimer();\n return;\n }\n this.runningScheduler = true;\n try {\n const now = Date.now();\n const due = listAutomations()\n .filter(\n (automation) =>\n automation.enabled &&\n automation.trigger.kind === 'schedule' &&\n automation.state.nextRunAtMs != null &&\n automation.state.nextRunAtMs <= now &&\n !automation.state.runningRunId,\n )\n .slice(0, this.maxConcurrentRuns);\n await Promise.all(due.map((automation) => this.startRun(automation, { manual: false })));\n } finally {\n this.runningScheduler = false;\n this.armTimer();\n }\n }\n\n private async startRun(\n automation: Automation,\n opts: { manual: boolean; event?: AutomationEvent },\n ): Promise<AutomationRun> {\n const now = Date.now();\n const run: AutomationRun = {\n id: randomUUID(),\n automationId: automation.id,\n automationName: automation.name,\n status: 'queued',\n triggerSnapshot: automation.trigger,\n actionSnapshot: automation.action,\n manual: opts.manual,\n createdAtMs: now,\n };\n saveAutomationRun(run);\n this.appendRunEvent(\n run,\n 'run.queued',\n opts.event\n ? `Event ${opts.event.type} queued automation`\n : opts.manual\n ? 'Manual run queued'\n : 'Scheduled run queued',\n {\n trigger: automation.trigger,\n actionKind: automation.action.kind,\n safety: automation.safety ?? { mode: 'auto_apply' },\n event: opts.event,\n },\n );\n const nextAutomation: Automation = {\n ...automation,\n state: {\n ...automation.state,\n runningRunId: run.id,\n },\n };\n saveAutomation(nextAutomation);\n\n void this.executeRun(nextAutomation, run).catch((err) => {\n log.error(\n { err, automationId: automation.id, runId: run.id },\n `Automation run failed: ${err instanceof Error ? err.message : String(err)}`,\n );\n });\n\n return run;\n }\n\n private async executeRun(automation: Automation, initialRun: AutomationRun): Promise<void> {\n const controller = new AbortController();\n this.activeRuns.set(initialRun.id, controller);\n const startedAtMs = Date.now();\n let run: AutomationRun = {\n ...initialRun,\n status: 'running',\n startedAtMs,\n };\n saveAutomationRun(run);\n this.appendRunEvent(run, 'run.started', 'Automation run started');\n\n let status: AutomationRunStatus = 'failed';\n let error: string | undefined;\n let activePhase: 'action' | 'after_run' = 'action';\n try {\n this.appendRunEvent(run, 'action.started', `Running ${automation.action.kind} action`, {\n actionKind: automation.action.kind,\n safety: automation.safety ?? { mode: 'auto_apply' },\n });\n const outcome = await this.executor.execute(automation, run, controller.signal);\n status = outcome.status;\n error = outcome.error;\n this.appendRunEvent(\n run,\n status === 'succeeded' ? 'action.completed' : 'action.failed',\n status === 'succeeded'\n ? `${automation.action.kind} action completed`\n : `${automation.action.kind} action ${status}`,\n {\n summary: outcome.summary,\n error: outcome.error,\n sessionKey: outcome.sessionKey,\n workflowRunId: outcome.workflowRunId,\n model: outcome.model,\n },\n );\n run = {\n ...run,\n status,\n summary: outcome.summary,\n error,\n sessionKey: outcome.sessionKey,\n workflowRunId: outcome.workflowRunId,\n model: outcome.model,\n };\n if ((automation.safety?.mode ?? 'auto_apply') !== 'auto_apply' && automation.afterRun?.kind === 'webhook') {\n this.appendRunEvent(run, 'after_run.completed', 'After-run webhook skipped by safety mode', {\n safety: automation.safety ?? { mode: 'auto_apply' },\n });\n } else if (automation.afterRun?.kind === 'webhook') {\n activePhase = 'after_run';\n this.appendRunEvent(run, 'after_run.started', 'Calling after-run webhook', {\n url: automation.afterRun.url,\n });\n await this.postAfterRunWebhook(automation.afterRun.url, run);\n this.appendRunEvent(run, 'after_run.completed', 'After-run webhook completed');\n }\n } catch (err) {\n error = err instanceof Error ? err.message : String(err);\n status = controller.signal.aborted ? 'cancelled' : 'failed';\n this.appendRunEvent(run, activePhase === 'after_run' ? 'after_run.failed' : 'action.failed', `Automation run ${status}`, {\n error,\n });\n run = { ...run, status, error };\n } finally {\n const endedAtMs = Date.now();\n run = {\n ...run,\n status,\n endedAtMs,\n durationMs: endedAtMs - startedAtMs,\n };\n saveAutomationRun(run);\n this.appendRunEvent(run, 'run.completed', `Automation run ${status}`, {\n status,\n durationMs: run.durationMs,\n error,\n });\n this.activeRuns.delete(initialRun.id);\n this.finishAutomationRun(automation.id, status, error, endedAtMs);\n }\n }\n\n private appendRunEvent(\n run: Pick<AutomationRun, 'id' | 'automationId'>,\n type: AutomationRunEventType,\n message: string,\n data?: unknown,\n ): void {\n appendAutomationRunEvent({\n id: randomUUID(),\n runId: run.id,\n automationId: run.automationId,\n type,\n message,\n data,\n createdAtMs: Date.now(),\n });\n }\n\n private finishAutomationRun(\n automationId: string,\n status: AutomationRunStatus,\n error: string | undefined,\n finishedAtMs: number,\n ): void {\n const current = getAutomation(automationId);\n if (!current) return;\n const failed = status === 'failed' || status === 'timeout';\n const consecutiveFailures = failed ? (current.state.consecutiveFailures ?? 0) + 1 : 0;\n const disableAfter = current.reliability?.disableAfterConsecutiveFailures;\n const shouldDisable = disableAfter != null && consecutiveFailures >= disableAfter;\n const next: Automation = {\n ...current,\n enabled: shouldDisable ? false : current.enabled,\n state: {\n ...current.state,\n lastRunAtMs: finishedAtMs,\n lastRunStatus: status,\n lastError: error,\n consecutiveFailures,\n },\n updatedAtMs: shouldDisable ? finishedAtMs : current.updatedAtMs,\n };\n delete next.state.runningRunId;\n next.state.nextRunAtMs = computeNextAutomationRunAtMs(next, finishedAtMs);\n saveAutomation(next);\n this.armTimer();\n }\n\n private async postAfterRunWebhook(url: string, run: AutomationRun): Promise<void> {\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ run }),\n });\n if (!response.ok) {\n const text = await response.text().catch(() => '');\n throw new Error(`Automation webhook failed: HTTP ${response.status}${text ? ` ${text.slice(0, 200)}` : ''}`);\n }\n }\n}\n\nfunction matchesAutomationEvent(\n trigger: Extract<Automation['trigger'], { kind: 'event' }>,\n event: AutomationEvent,\n): boolean {\n if (trigger.eventType !== event.type) return false;\n if (trigger.source && trigger.source !== event.source) return false;\n const payloadMatch = trigger.payloadMatch;\n if (!payloadMatch) return true;\n const payload = event.payload ?? {};\n return Object.entries(payloadMatch).every(([key, expected]) => Object.is(payload[key], expected));\n}\n\nfunction readAutomationEventFromRunEvent(event: AutomationRunEvent): AutomationEvent | null {\n if (!event.data || typeof event.data !== 'object' || Array.isArray(event.data)) return null;\n const raw = (event.data as { event?: unknown }).event;\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return null;\n const candidate = raw as Partial<AutomationEvent>;\n if (typeof candidate.type !== 'string' || !candidate.type.trim()) return null;\n return {\n type: candidate.type,\n source: typeof candidate.source === 'string' ? candidate.source : undefined,\n payload: candidate.payload && typeof candidate.payload === 'object' && !Array.isArray(candidate.payload)\n ? candidate.payload as Record<string, unknown>\n : undefined,\n occurredAtMs: typeof candidate.occurredAtMs === 'number' ? candidate.occurredAtMs : undefined,\n };\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { Automation } from '../domain/types.js';
|
|
2
|
-
export declare function listAutomations(
|
|
2
|
+
export declare function listAutomations(options?: {
|
|
3
|
+
projectId?: string;
|
|
4
|
+
}): Automation[];
|
|
3
5
|
export declare function getAutomation(automationId: string): Automation | null;
|
|
4
6
|
export declare function saveAutomation(automation: Automation): void;
|
|
5
7
|
export declare function saveAutomations(automations: Automation[]): void;
|
|
@@ -13,6 +13,7 @@ function rowToAutomation(row) {
|
|
|
13
13
|
id: row.automation_id,
|
|
14
14
|
name: row.name,
|
|
15
15
|
description: row.description ?? void 0,
|
|
16
|
+
projectId: row.project_id ?? void 0,
|
|
16
17
|
enabled: row.enabled !== 0,
|
|
17
18
|
trigger: parseJson(row.trigger_json),
|
|
18
19
|
action: parseJson(row.action_json),
|
|
@@ -26,17 +27,18 @@ function rowToAutomation(row) {
|
|
|
26
27
|
}
|
|
27
28
|
function upsertAutomation(db, automation) {
|
|
28
29
|
db.prepare(`INSERT OR REPLACE INTO automations (
|
|
29
|
-
automation_id, name, description, enabled, trigger_json, action_json,
|
|
30
|
+
automation_id, name, description, project_id, enabled, trigger_json, action_json,
|
|
30
31
|
safety_json, after_run_json, reliability_json, state_json, created_at_ms, updated_at_ms
|
|
31
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(automation.id, automation.name, automation.description ?? null, automation.enabled ? 1 : 0, JSON.stringify(automation.trigger), JSON.stringify(automation.action), bindJson(automation.safety ?? { mode: "auto_apply" }), bindJson(automation.afterRun), bindJson(automation.reliability), JSON.stringify(automation.state ?? {}), automation.createdAtMs, automation.updatedAtMs);
|
|
32
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(automation.id, automation.name, automation.description ?? null, automation.projectId ?? null, automation.enabled ? 1 : 0, JSON.stringify(automation.trigger), JSON.stringify(automation.action), bindJson(automation.safety ?? { mode: "auto_apply" }), bindJson(automation.afterRun), bindJson(automation.reliability), JSON.stringify(automation.state ?? {}), automation.createdAtMs, automation.updatedAtMs);
|
|
32
33
|
}
|
|
33
34
|
const AUTOMATION_SELECT = `
|
|
34
|
-
SELECT automation_id, name, description, enabled, trigger_json, action_json,
|
|
35
|
+
SELECT automation_id, name, description, project_id, enabled, trigger_json, action_json,
|
|
35
36
|
safety_json, after_run_json, reliability_json, state_json, created_at_ms, updated_at_ms
|
|
36
37
|
FROM automations
|
|
37
38
|
`;
|
|
38
|
-
function listAutomations() {
|
|
39
|
-
|
|
39
|
+
function listAutomations(options) {
|
|
40
|
+
const db = getSqliteDatabase();
|
|
41
|
+
return (options?.projectId ? db.prepare(`${AUTOMATION_SELECT} WHERE project_id = ? ORDER BY created_at_ms DESC`).all(options.projectId) : db.prepare(`${AUTOMATION_SELECT} ORDER BY created_at_ms DESC`).all()).map(rowToAutomation);
|
|
40
42
|
}
|
|
41
43
|
function getAutomation(automationId) {
|
|
42
44
|
const row = getSqliteDatabase().prepare(`${AUTOMATION_SELECT} WHERE automation_id = ?`).get(automationId);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"automation-repository.js","names":[],"sources":["../../../../src/automations/storage/automation-repository.ts"],"sourcesContent":["import type { Automation } from '../domain/types.js';\nimport { AutomationSchema } from '../domain/validation.js';\nimport { getSqliteDatabase, runSqliteWriteTransaction } from '../../storage/sqlite/transaction.js';\n\ntype AutomationRow = {\n automation_id: string;\n name: string;\n description: string | null;\n enabled: number;\n trigger_json: string;\n action_json: string;\n safety_json: string | null;\n after_run_json: string | null;\n reliability_json: string | null;\n state_json: string;\n created_at_ms: number;\n updated_at_ms: number;\n};\n\nfunction parseJson(value: string | null): unknown {\n if (!value) return undefined;\n return JSON.parse(value);\n}\n\nfunction bindJson(value: unknown): string | null {\n return value === undefined ? null : JSON.stringify(value);\n}\n\nfunction rowToAutomation(row: AutomationRow): Automation {\n return AutomationSchema.parse({\n id: row.automation_id,\n name: row.name,\n description: row.description ?? undefined,\n enabled: row.enabled !== 0,\n trigger: parseJson(row.trigger_json),\n action: parseJson(row.action_json),\n safety: parseJson(row.safety_json) ?? { mode: 'auto_apply' },\n afterRun: parseJson(row.after_run_json),\n reliability: parseJson(row.reliability_json),\n state: parseJson(row.state_json) ?? {},\n createdAtMs: row.created_at_ms,\n updatedAtMs: row.updated_at_ms,\n }) as Automation;\n}\n\nfunction upsertAutomation(db: ReturnType<typeof getSqliteDatabase>, automation: Automation): void {\n db.prepare(\n `INSERT OR REPLACE INTO automations (\n automation_id, name, description, enabled, trigger_json, action_json,\n safety_json, after_run_json, reliability_json, state_json, created_at_ms, updated_at_ms\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n ).run(\n automation.id,\n automation.name,\n automation.description ?? null,\n automation.enabled ? 1 : 0,\n JSON.stringify(automation.trigger),\n JSON.stringify(automation.action),\n bindJson(automation.safety ?? { mode: 'auto_apply' }),\n bindJson(automation.afterRun),\n bindJson(automation.reliability),\n JSON.stringify(automation.state ?? {}),\n automation.createdAtMs,\n automation.updatedAtMs,\n );\n}\n\nconst AUTOMATION_SELECT = `\n SELECT automation_id, name, description, enabled, trigger_json, action_json,\n safety_json, after_run_json, reliability_json, state_json, created_at_ms, updated_at_ms\n FROM automations\n`;\n\nexport function listAutomations(): Automation[] {\n const
|
|
1
|
+
{"version":3,"file":"automation-repository.js","names":[],"sources":["../../../../src/automations/storage/automation-repository.ts"],"sourcesContent":["import type { Automation } from '../domain/types.js';\nimport { AutomationSchema } from '../domain/validation.js';\nimport { getSqliteDatabase, runSqliteWriteTransaction } from '../../storage/sqlite/transaction.js';\n\ntype AutomationRow = {\n automation_id: string;\n name: string;\n description: string | null;\n project_id: string | null;\n enabled: number;\n trigger_json: string;\n action_json: string;\n safety_json: string | null;\n after_run_json: string | null;\n reliability_json: string | null;\n state_json: string;\n created_at_ms: number;\n updated_at_ms: number;\n};\n\nfunction parseJson(value: string | null): unknown {\n if (!value) return undefined;\n return JSON.parse(value);\n}\n\nfunction bindJson(value: unknown): string | null {\n return value === undefined ? null : JSON.stringify(value);\n}\n\nfunction rowToAutomation(row: AutomationRow): Automation {\n return AutomationSchema.parse({\n id: row.automation_id,\n name: row.name,\n description: row.description ?? undefined,\n projectId: row.project_id ?? undefined,\n enabled: row.enabled !== 0,\n trigger: parseJson(row.trigger_json),\n action: parseJson(row.action_json),\n safety: parseJson(row.safety_json) ?? { mode: 'auto_apply' },\n afterRun: parseJson(row.after_run_json),\n reliability: parseJson(row.reliability_json),\n state: parseJson(row.state_json) ?? {},\n createdAtMs: row.created_at_ms,\n updatedAtMs: row.updated_at_ms,\n }) as Automation;\n}\n\nfunction upsertAutomation(db: ReturnType<typeof getSqliteDatabase>, automation: Automation): void {\n db.prepare(\n `INSERT OR REPLACE INTO automations (\n automation_id, name, description, project_id, enabled, trigger_json, action_json,\n safety_json, after_run_json, reliability_json, state_json, created_at_ms, updated_at_ms\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n ).run(\n automation.id,\n automation.name,\n automation.description ?? null,\n automation.projectId ?? null,\n automation.enabled ? 1 : 0,\n JSON.stringify(automation.trigger),\n JSON.stringify(automation.action),\n bindJson(automation.safety ?? { mode: 'auto_apply' }),\n bindJson(automation.afterRun),\n bindJson(automation.reliability),\n JSON.stringify(automation.state ?? {}),\n automation.createdAtMs,\n automation.updatedAtMs,\n );\n}\n\nconst AUTOMATION_SELECT = `\n SELECT automation_id, name, description, project_id, enabled, trigger_json, action_json,\n safety_json, after_run_json, reliability_json, state_json, created_at_ms, updated_at_ms\n FROM automations\n`;\n\nexport function listAutomations(options?: { projectId?: string }): Automation[] {\n const db = getSqliteDatabase();\n const rows = options?.projectId\n ? db.prepare(`${AUTOMATION_SELECT} WHERE project_id = ? ORDER BY created_at_ms DESC`).all(options.projectId)\n : db.prepare(`${AUTOMATION_SELECT} ORDER BY created_at_ms DESC`).all();\n return (rows as AutomationRow[]).map(rowToAutomation);\n}\n\nexport function getAutomation(automationId: string): Automation | null {\n const row = getSqliteDatabase()\n .prepare(`${AUTOMATION_SELECT} WHERE automation_id = ?`)\n .get(automationId) as AutomationRow | undefined;\n return row ? rowToAutomation(row) : null;\n}\n\nexport function saveAutomation(automation: Automation): void {\n runSqliteWriteTransaction((db) => upsertAutomation(db, automation));\n}\n\nexport function saveAutomations(automations: Automation[]): void {\n runSqliteWriteTransaction((db) => {\n for (const automation of automations) {\n upsertAutomation(db, automation);\n }\n });\n}\n\nexport function deleteAutomation(automationId: string): boolean {\n return runSqliteWriteTransaction((db) => {\n const result = db.prepare(`DELETE FROM automations WHERE automation_id = ?`).run(automationId);\n return result.changes > 0;\n });\n}\n"],"mappings":";;;AAoBA,SAAS,UAAU,OAA+B;AAChD,KAAI,CAAC,MAAO,QAAO,KAAA;AACnB,QAAO,KAAK,MAAM,MAAM;;AAG1B,SAAS,SAAS,OAA+B;AAC/C,QAAO,UAAU,KAAA,IAAY,OAAO,KAAK,UAAU,MAAM;;AAG3D,SAAS,gBAAgB,KAAgC;AACvD,QAAO,iBAAiB,MAAM;EAC5B,IAAI,IAAI;EACR,MAAM,IAAI;EACV,aAAa,IAAI,eAAe,KAAA;EAChC,WAAW,IAAI,cAAc,KAAA;EAC7B,SAAS,IAAI,YAAY;EACzB,SAAS,UAAU,IAAI,aAAa;EACpC,QAAQ,UAAU,IAAI,YAAY;EAClC,QAAQ,UAAU,IAAI,YAAY,IAAI,EAAE,MAAM,cAAc;EAC5D,UAAU,UAAU,IAAI,eAAe;EACvC,aAAa,UAAU,IAAI,iBAAiB;EAC5C,OAAO,UAAU,IAAI,WAAW,IAAI,EAAE;EACtC,aAAa,IAAI;EACjB,aAAa,IAAI;EAClB,CAAC;;AAGJ,SAAS,iBAAiB,IAA0C,YAA8B;AAChG,IAAG,QACD;;;sDAID,CAAC,IACA,WAAW,IACX,WAAW,MACX,WAAW,eAAe,MAC1B,WAAW,aAAa,MACxB,WAAW,UAAU,IAAI,GACzB,KAAK,UAAU,WAAW,QAAQ,EAClC,KAAK,UAAU,WAAW,OAAO,EACjC,SAAS,WAAW,UAAU,EAAE,MAAM,cAAc,CAAC,EACrD,SAAS,WAAW,SAAS,EAC7B,SAAS,WAAW,YAAY,EAChC,KAAK,UAAU,WAAW,SAAS,EAAE,CAAC,EACtC,WAAW,aACX,WAAW,YACZ;;AAGH,MAAM,oBAAoB;;;;;AAM1B,SAAgB,gBAAgB,SAAgD;CAC9E,MAAM,KAAK,mBAAmB;AAI9B,SAHa,SAAS,YAClB,GAAG,QAAQ,GAAG,kBAAkB,mDAAmD,CAAC,IAAI,QAAQ,UAAU,GAC1G,GAAG,QAAQ,GAAG,kBAAkB,8BAA8B,CAAC,KAAK,EACvC,IAAI,gBAAgB;;AAGvD,SAAgB,cAAc,cAAyC;CACrE,MAAM,MAAM,mBAAmB,CAC5B,QAAQ,GAAG,kBAAkB,0BAA0B,CACvD,IAAI,aAAa;AACpB,QAAO,MAAM,gBAAgB,IAAI,GAAG;;AAGtC,SAAgB,eAAe,YAA8B;AAC3D,4BAA2B,OAAO,iBAAiB,IAAI,WAAW,CAAC;;AAGrE,SAAgB,gBAAgB,aAAiC;AAC/D,4BAA2B,OAAO;AAChC,OAAK,MAAM,cAAc,YACvB,kBAAiB,IAAI,WAAW;GAElC;;AAGJ,SAAgB,iBAAiB,cAA+B;AAC9D,QAAO,2BAA2B,OAAO;AAEvC,SADe,GAAG,QAAQ,kDAAkD,CAAC,IAAI,aACpE,CAAC,UAAU;GACxB"}
|
|
@@ -5,6 +5,7 @@ export declare function listAutomationRunEvents(runId: string): AutomationRunEve
|
|
|
5
5
|
export declare function getAutomationRun(runId: string): AutomationRun | null;
|
|
6
6
|
export declare function listAutomationRuns(options?: {
|
|
7
7
|
automationId?: string;
|
|
8
|
+
projectId?: string;
|
|
8
9
|
limit?: number;
|
|
9
10
|
}): AutomationRun[];
|
|
10
11
|
export declare function listAutomationRunsForProductEvent(options: {
|
|
@@ -81,7 +81,18 @@ function listAutomationRuns(options) {
|
|
|
81
81
|
action_snapshot_json, manual, created_at_ms, started_at_ms, ended_at_ms,
|
|
82
82
|
duration_ms, summary, error, session_key, workflow_run_id, model
|
|
83
83
|
FROM automation_runs`;
|
|
84
|
-
|
|
84
|
+
let rows;
|
|
85
|
+
if (options?.automationId) rows = db.prepare(`${base} WHERE automation_id = ? ORDER BY created_at_ms DESC LIMIT ?`).all(options.automationId, limit);
|
|
86
|
+
else if (options?.projectId) rows = db.prepare(`SELECT r.run_id, r.automation_id, r.automation_name, r.status, r.trigger_snapshot_json,
|
|
87
|
+
r.action_snapshot_json, r.manual, r.created_at_ms, r.started_at_ms, r.ended_at_ms,
|
|
88
|
+
r.duration_ms, r.summary, r.error, r.session_key, r.workflow_run_id, r.model
|
|
89
|
+
FROM automation_runs r
|
|
90
|
+
JOIN automations a ON a.automation_id = r.automation_id
|
|
91
|
+
WHERE a.project_id = ?
|
|
92
|
+
ORDER BY r.created_at_ms DESC
|
|
93
|
+
LIMIT ?`).all(options.projectId, limit);
|
|
94
|
+
else rows = db.prepare(`${base} ORDER BY created_at_ms DESC LIMIT ?`).all(limit);
|
|
95
|
+
return rows.map(rowToRun);
|
|
85
96
|
}
|
|
86
97
|
function listAutomationRunsForProductEvent(options) {
|
|
87
98
|
const limit = Math.min(Math.max(options.limit ?? 10, 1), 100);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"automation-run-repository.js","names":[],"sources":["../../../../src/automations/storage/automation-run-repository.ts"],"sourcesContent":["import type { AutomationProductEventRun, AutomationRun, AutomationRunEvent } from '../domain/types.js';\nimport { getSqliteDatabase, runSqliteWriteTransaction } from '../../storage/sqlite/transaction.js';\n\nconst MAX_RUNS_PER_AUTOMATION = 2000;\nconst TRIM_TO_RUNS = 1500;\n\ntype AutomationRunRow = {\n run_id: string;\n automation_id: string;\n automation_name: string;\n status: string;\n trigger_snapshot_json: string;\n action_snapshot_json: string;\n manual: number;\n created_at_ms: number;\n started_at_ms: number | null;\n ended_at_ms: number | null;\n duration_ms: number | null;\n summary: string | null;\n error: string | null;\n session_key: string | null;\n workflow_run_id: string | null;\n model: string | null;\n};\n\ntype AutomationRunEventRow = {\n event_id: string;\n run_id: string;\n automation_id: string;\n type: string;\n message: string;\n data_json: string | null;\n created_at_ms: number;\n};\n\nfunction rowToRun(row: AutomationRunRow): AutomationRun {\n return {\n id: row.run_id,\n automationId: row.automation_id,\n automationName: row.automation_name,\n status: row.status as AutomationRun['status'],\n triggerSnapshot: JSON.parse(row.trigger_snapshot_json),\n actionSnapshot: JSON.parse(row.action_snapshot_json),\n manual: row.manual !== 0,\n createdAtMs: row.created_at_ms,\n startedAtMs: row.started_at_ms ?? undefined,\n endedAtMs: row.ended_at_ms ?? undefined,\n durationMs: row.duration_ms ?? undefined,\n summary: row.summary ?? undefined,\n error: row.error ?? undefined,\n sessionKey: row.session_key ?? undefined,\n workflowRunId: row.workflow_run_id ?? undefined,\n model: row.model ?? undefined,\n };\n}\n\nfunction rowToEvent(row: AutomationRunEventRow): AutomationRunEvent {\n return {\n id: row.event_id,\n runId: row.run_id,\n automationId: row.automation_id,\n type: row.type as AutomationRunEvent['type'],\n message: row.message,\n data: row.data_json ? JSON.parse(row.data_json) : undefined,\n createdAtMs: row.created_at_ms,\n };\n}\n\nfunction trimRuns(db: ReturnType<typeof getSqliteDatabase>, automationId: string): void {\n const countRow = db\n .prepare(`SELECT COUNT(*) AS count FROM automation_runs WHERE automation_id = ?`)\n .get(automationId) as { count: number };\n if (countRow.count <= MAX_RUNS_PER_AUTOMATION) return;\n const staleRunIds = db\n .prepare(\n `SELECT run_id FROM automation_runs WHERE automation_id = ?\n ORDER BY created_at_ms ASC LIMIT ?`,\n )\n .all(automationId, countRow.count - TRIM_TO_RUNS) as { run_id: string }[];\n for (const { run_id: runId } of staleRunIds) {\n db.prepare(`DELETE FROM automation_run_events WHERE run_id = ?`).run(runId);\n db.prepare(`DELETE FROM automation_runs WHERE run_id = ?`).run(runId);\n }\n}\n\nexport function appendAutomationRunEvent(event: AutomationRunEvent): void {\n runSqliteWriteTransaction((db) => {\n db.prepare(\n `INSERT INTO automation_run_events (\n event_id, run_id, automation_id, type, message, data_json, created_at_ms\n ) VALUES (?, ?, ?, ?, ?, ?, ?)`,\n ).run(\n event.id,\n event.runId,\n event.automationId,\n event.type,\n event.message,\n event.data === undefined ? null : JSON.stringify(event.data),\n event.createdAtMs,\n );\n });\n}\n\nexport function saveAutomationRun(run: AutomationRun): void {\n runSqliteWriteTransaction((db) => {\n db.prepare(\n `INSERT OR REPLACE INTO automation_runs (\n run_id, automation_id, automation_name, status, trigger_snapshot_json,\n action_snapshot_json, manual, created_at_ms, started_at_ms, ended_at_ms,\n duration_ms, summary, error, session_key, workflow_run_id, model\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n ).run(\n run.id,\n run.automationId,\n run.automationName,\n run.status,\n JSON.stringify(run.triggerSnapshot),\n JSON.stringify(run.actionSnapshot),\n run.manual ? 1 : 0,\n run.createdAtMs,\n run.startedAtMs ?? null,\n run.endedAtMs ?? null,\n run.durationMs ?? null,\n run.summary ?? null,\n run.error ?? null,\n run.sessionKey ?? null,\n run.workflowRunId ?? null,\n run.model ?? null,\n );\n trimRuns(db, run.automationId);\n });\n}\n\nexport function listAutomationRunEvents(runId: string): AutomationRunEvent[] {\n const rows = getSqliteDatabase()\n .prepare(\n `SELECT event_id, run_id, automation_id, type, message, data_json, created_at_ms\n FROM automation_run_events\n WHERE run_id = ?\n ORDER BY created_at_ms ASC`,\n )\n .all(runId) as AutomationRunEventRow[];\n return rows.map(rowToEvent);\n}\n\nexport function getAutomationRun(runId: string): AutomationRun | null {\n const row = getSqliteDatabase()\n .prepare(\n `SELECT run_id, automation_id, automation_name, status, trigger_snapshot_json,\n action_snapshot_json, manual, created_at_ms, started_at_ms, ended_at_ms,\n duration_ms, summary, error, session_key, workflow_run_id, model\n FROM automation_runs\n WHERE run_id = ?`,\n )\n .get(runId) as AutomationRunRow | undefined;\n return row ? rowToRun(row) : null;\n}\n\nexport function listAutomationRuns(options?: {\n automationId?: string;\n limit?: number;\n}): AutomationRun[] {\n const limit = Math.min(Math.max(options?.limit ?? 50, 1), 500);\n const db = getSqliteDatabase();\n const base = `SELECT run_id, automation_id, automation_name, status, trigger_snapshot_json,\n action_snapshot_json, manual, created_at_ms, started_at_ms, ended_at_ms,\n duration_ms, summary, error, session_key, workflow_run_id, model\n FROM automation_runs`;\n const rows = options?.automationId\n ? db.prepare(`${base} WHERE automation_id = ? ORDER BY created_at_ms DESC LIMIT ?`).all(options.automationId, limit)\n : db.prepare(`${base} ORDER BY created_at_ms DESC LIMIT ?`).all(limit);\n return (rows as AutomationRunRow[]).map(rowToRun);\n}\n\nexport function listAutomationRunsForProductEvent(options: {\n eventType: string;\n source?: string;\n payloadKey?: string;\n payloadValue?: string;\n limit?: number;\n}): AutomationProductEventRun[] {\n const limit = Math.min(Math.max(options.limit ?? 10, 1), 100);\n const clauses = [\n `e.type = 'run.queued'`,\n `json_extract(e.data_json, '$.event.type') = ?`,\n ];\n const params: Array<string | number> = [options.eventType, limit];\n\n if (options.source) {\n clauses.push(`json_extract(e.data_json, '$.event.source') = ?`);\n params.splice(params.length - 1, 0, options.source);\n }\n if (options.payloadKey && options.payloadValue !== undefined) {\n clauses.push(`json_extract(e.data_json, '$.event.payload.' || ?) = ?`);\n params.splice(params.length - 1, 0, options.payloadKey, options.payloadValue);\n }\n\n const rows = getSqliteDatabase()\n .prepare(\n `SELECT r.run_id, r.automation_id, r.automation_name, r.status, r.trigger_snapshot_json,\n r.action_snapshot_json, r.manual, r.created_at_ms, r.started_at_ms, r.ended_at_ms,\n r.duration_ms, r.summary, r.error, r.session_key, r.workflow_run_id, r.model,\n e.event_id, e.run_id AS event_run_id, e.automation_id AS event_automation_id,\n e.type AS event_type, e.message AS event_message, e.data_json AS event_data_json,\n e.created_at_ms AS event_created_at_ms\n FROM automation_run_events e\n JOIN automation_runs r ON r.run_id = e.run_id\n WHERE ${clauses.join(' AND ')}\n ORDER BY e.created_at_ms DESC\n LIMIT ?`,\n )\n .all(...params) as Array<AutomationRunRow & {\n event_id: string;\n event_run_id: string;\n event_automation_id: string;\n event_type: string;\n event_message: string;\n event_data_json: string | null;\n event_created_at_ms: number;\n }>;\n\n return rows.map((row) => ({\n run: rowToRun(row),\n triggerEvent: rowToEvent({\n event_id: row.event_id,\n run_id: row.event_run_id,\n automation_id: row.event_automation_id,\n type: row.event_type,\n message: row.event_message,\n data_json: row.event_data_json,\n created_at_ms: row.event_created_at_ms,\n }),\n }));\n}\n\nexport function deleteAutomationRunsForAutomation(automationId: string): void {\n runSqliteWriteTransaction((db) => {\n db.prepare(`DELETE FROM automation_run_events WHERE automation_id = ?`).run(automationId);\n db.prepare(`DELETE FROM automation_runs WHERE automation_id = ?`).run(automationId);\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"automation-run-repository.js","names":[],"sources":["../../../../src/automations/storage/automation-run-repository.ts"],"sourcesContent":["import type { AutomationProductEventRun, AutomationRun, AutomationRunEvent } from '../domain/types.js';\nimport { getSqliteDatabase, runSqliteWriteTransaction } from '../../storage/sqlite/transaction.js';\n\nconst MAX_RUNS_PER_AUTOMATION = 2000;\nconst TRIM_TO_RUNS = 1500;\n\ntype AutomationRunRow = {\n run_id: string;\n automation_id: string;\n automation_name: string;\n status: string;\n trigger_snapshot_json: string;\n action_snapshot_json: string;\n manual: number;\n created_at_ms: number;\n started_at_ms: number | null;\n ended_at_ms: number | null;\n duration_ms: number | null;\n summary: string | null;\n error: string | null;\n session_key: string | null;\n workflow_run_id: string | null;\n model: string | null;\n};\n\ntype AutomationRunEventRow = {\n event_id: string;\n run_id: string;\n automation_id: string;\n type: string;\n message: string;\n data_json: string | null;\n created_at_ms: number;\n};\n\nfunction rowToRun(row: AutomationRunRow): AutomationRun {\n return {\n id: row.run_id,\n automationId: row.automation_id,\n automationName: row.automation_name,\n status: row.status as AutomationRun['status'],\n triggerSnapshot: JSON.parse(row.trigger_snapshot_json),\n actionSnapshot: JSON.parse(row.action_snapshot_json),\n manual: row.manual !== 0,\n createdAtMs: row.created_at_ms,\n startedAtMs: row.started_at_ms ?? undefined,\n endedAtMs: row.ended_at_ms ?? undefined,\n durationMs: row.duration_ms ?? undefined,\n summary: row.summary ?? undefined,\n error: row.error ?? undefined,\n sessionKey: row.session_key ?? undefined,\n workflowRunId: row.workflow_run_id ?? undefined,\n model: row.model ?? undefined,\n };\n}\n\nfunction rowToEvent(row: AutomationRunEventRow): AutomationRunEvent {\n return {\n id: row.event_id,\n runId: row.run_id,\n automationId: row.automation_id,\n type: row.type as AutomationRunEvent['type'],\n message: row.message,\n data: row.data_json ? JSON.parse(row.data_json) : undefined,\n createdAtMs: row.created_at_ms,\n };\n}\n\nfunction trimRuns(db: ReturnType<typeof getSqliteDatabase>, automationId: string): void {\n const countRow = db\n .prepare(`SELECT COUNT(*) AS count FROM automation_runs WHERE automation_id = ?`)\n .get(automationId) as { count: number };\n if (countRow.count <= MAX_RUNS_PER_AUTOMATION) return;\n const staleRunIds = db\n .prepare(\n `SELECT run_id FROM automation_runs WHERE automation_id = ?\n ORDER BY created_at_ms ASC LIMIT ?`,\n )\n .all(automationId, countRow.count - TRIM_TO_RUNS) as { run_id: string }[];\n for (const { run_id: runId } of staleRunIds) {\n db.prepare(`DELETE FROM automation_run_events WHERE run_id = ?`).run(runId);\n db.prepare(`DELETE FROM automation_runs WHERE run_id = ?`).run(runId);\n }\n}\n\nexport function appendAutomationRunEvent(event: AutomationRunEvent): void {\n runSqliteWriteTransaction((db) => {\n db.prepare(\n `INSERT INTO automation_run_events (\n event_id, run_id, automation_id, type, message, data_json, created_at_ms\n ) VALUES (?, ?, ?, ?, ?, ?, ?)`,\n ).run(\n event.id,\n event.runId,\n event.automationId,\n event.type,\n event.message,\n event.data === undefined ? null : JSON.stringify(event.data),\n event.createdAtMs,\n );\n });\n}\n\nexport function saveAutomationRun(run: AutomationRun): void {\n runSqliteWriteTransaction((db) => {\n db.prepare(\n `INSERT OR REPLACE INTO automation_runs (\n run_id, automation_id, automation_name, status, trigger_snapshot_json,\n action_snapshot_json, manual, created_at_ms, started_at_ms, ended_at_ms,\n duration_ms, summary, error, session_key, workflow_run_id, model\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n ).run(\n run.id,\n run.automationId,\n run.automationName,\n run.status,\n JSON.stringify(run.triggerSnapshot),\n JSON.stringify(run.actionSnapshot),\n run.manual ? 1 : 0,\n run.createdAtMs,\n run.startedAtMs ?? null,\n run.endedAtMs ?? null,\n run.durationMs ?? null,\n run.summary ?? null,\n run.error ?? null,\n run.sessionKey ?? null,\n run.workflowRunId ?? null,\n run.model ?? null,\n );\n trimRuns(db, run.automationId);\n });\n}\n\nexport function listAutomationRunEvents(runId: string): AutomationRunEvent[] {\n const rows = getSqliteDatabase()\n .prepare(\n `SELECT event_id, run_id, automation_id, type, message, data_json, created_at_ms\n FROM automation_run_events\n WHERE run_id = ?\n ORDER BY created_at_ms ASC`,\n )\n .all(runId) as AutomationRunEventRow[];\n return rows.map(rowToEvent);\n}\n\nexport function getAutomationRun(runId: string): AutomationRun | null {\n const row = getSqliteDatabase()\n .prepare(\n `SELECT run_id, automation_id, automation_name, status, trigger_snapshot_json,\n action_snapshot_json, manual, created_at_ms, started_at_ms, ended_at_ms,\n duration_ms, summary, error, session_key, workflow_run_id, model\n FROM automation_runs\n WHERE run_id = ?`,\n )\n .get(runId) as AutomationRunRow | undefined;\n return row ? rowToRun(row) : null;\n}\n\nexport function listAutomationRuns(options?: {\n automationId?: string;\n projectId?: string;\n limit?: number;\n}): AutomationRun[] {\n const limit = Math.min(Math.max(options?.limit ?? 50, 1), 500);\n const db = getSqliteDatabase();\n const base = `SELECT run_id, automation_id, automation_name, status, trigger_snapshot_json,\n action_snapshot_json, manual, created_at_ms, started_at_ms, ended_at_ms,\n duration_ms, summary, error, session_key, workflow_run_id, model\n FROM automation_runs`;\n let rows: unknown[];\n if (options?.automationId) {\n rows = db.prepare(`${base} WHERE automation_id = ? ORDER BY created_at_ms DESC LIMIT ?`).all(options.automationId, limit);\n } else if (options?.projectId) {\n rows = db.prepare(\n `SELECT r.run_id, r.automation_id, r.automation_name, r.status, r.trigger_snapshot_json,\n r.action_snapshot_json, r.manual, r.created_at_ms, r.started_at_ms, r.ended_at_ms,\n r.duration_ms, r.summary, r.error, r.session_key, r.workflow_run_id, r.model\n FROM automation_runs r\n JOIN automations a ON a.automation_id = r.automation_id\n WHERE a.project_id = ?\n ORDER BY r.created_at_ms DESC\n LIMIT ?`,\n ).all(options.projectId, limit);\n } else {\n rows = db.prepare(`${base} ORDER BY created_at_ms DESC LIMIT ?`).all(limit);\n }\n return (rows as AutomationRunRow[]).map(rowToRun);\n}\n\nexport function listAutomationRunsForProductEvent(options: {\n eventType: string;\n source?: string;\n payloadKey?: string;\n payloadValue?: string;\n limit?: number;\n}): AutomationProductEventRun[] {\n const limit = Math.min(Math.max(options.limit ?? 10, 1), 100);\n const clauses = [\n `e.type = 'run.queued'`,\n `json_extract(e.data_json, '$.event.type') = ?`,\n ];\n const params: Array<string | number> = [options.eventType, limit];\n\n if (options.source) {\n clauses.push(`json_extract(e.data_json, '$.event.source') = ?`);\n params.splice(params.length - 1, 0, options.source);\n }\n if (options.payloadKey && options.payloadValue !== undefined) {\n clauses.push(`json_extract(e.data_json, '$.event.payload.' || ?) = ?`);\n params.splice(params.length - 1, 0, options.payloadKey, options.payloadValue);\n }\n\n const rows = getSqliteDatabase()\n .prepare(\n `SELECT r.run_id, r.automation_id, r.automation_name, r.status, r.trigger_snapshot_json,\n r.action_snapshot_json, r.manual, r.created_at_ms, r.started_at_ms, r.ended_at_ms,\n r.duration_ms, r.summary, r.error, r.session_key, r.workflow_run_id, r.model,\n e.event_id, e.run_id AS event_run_id, e.automation_id AS event_automation_id,\n e.type AS event_type, e.message AS event_message, e.data_json AS event_data_json,\n e.created_at_ms AS event_created_at_ms\n FROM automation_run_events e\n JOIN automation_runs r ON r.run_id = e.run_id\n WHERE ${clauses.join(' AND ')}\n ORDER BY e.created_at_ms DESC\n LIMIT ?`,\n )\n .all(...params) as Array<AutomationRunRow & {\n event_id: string;\n event_run_id: string;\n event_automation_id: string;\n event_type: string;\n event_message: string;\n event_data_json: string | null;\n event_created_at_ms: number;\n }>;\n\n return rows.map((row) => ({\n run: rowToRun(row),\n triggerEvent: rowToEvent({\n event_id: row.event_id,\n run_id: row.event_run_id,\n automation_id: row.event_automation_id,\n type: row.event_type,\n message: row.event_message,\n data_json: row.event_data_json,\n created_at_ms: row.event_created_at_ms,\n }),\n }));\n}\n\nexport function deleteAutomationRunsForAutomation(automationId: string): void {\n runSqliteWriteTransaction((db) => {\n db.prepare(`DELETE FROM automation_run_events WHERE automation_id = ?`).run(automationId);\n db.prepare(`DELETE FROM automation_runs WHERE automation_id = ?`).run(automationId);\n });\n}\n"],"mappings":"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"}
|
|
@@ -3,6 +3,7 @@ import { createLogger } from "../utils/logger/index.js";
|
|
|
3
3
|
import { init_logger } from "../utils/logger.js";
|
|
4
4
|
import { init_loader, saveConfig } from "../config/loader.js";
|
|
5
5
|
import { effectiveWorkspacePathForSession } from "../session/session-workspace.js";
|
|
6
|
+
import { getProjectForSession } from "../projects/workspace.js";
|
|
6
7
|
import { getSessionDisplayName } from "./session-key.js";
|
|
7
8
|
import { wrapMarkdownExportAsHtml } from "../session/chat-export.js";
|
|
8
9
|
import { join } from "path";
|
|
@@ -244,7 +245,7 @@ var CommandContextImpl = class {
|
|
|
244
245
|
let body = await this.deps.sessionStore.exportSession(this.sessionKey, exportFmt);
|
|
245
246
|
if (format === "html") body = wrapMarkdownExportAsHtml(`Session ${this.sessionKey}`, body);
|
|
246
247
|
const sc = this.deps.sessionConfigStore ? await this.deps.sessionConfigStore.get(this.sessionKey) : null;
|
|
247
|
-
const dir = join(effectiveWorkspacePathForSession(this.config, this.sessionKey, sc), "exports");
|
|
248
|
+
const dir = join(effectiveWorkspacePathForSession(this.config, this.sessionKey, sc, getProjectForSession(this.sessionKey)), "exports");
|
|
248
249
|
await mkdir(dir, { recursive: true });
|
|
249
250
|
const safe = this.sessionKey.replace(/[^a-zA-Z0-9._-]+/g, "_").slice(0, 96);
|
|
250
251
|
const ext = format === "json" ? "json" : format === "html" ? "html" : "md";
|