@elizaos/ui 2.0.0-alpha.192 → 2.0.0-alpha.196
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/apps/app-companion/src/components/companion/companion-app.js +2 -2
- package/apps/app-lifeops/src/action.d.ts.map +1 -1
- package/apps/app-lifeops/src/action.js +21 -2
- package/apps/app-lifeops/src/actions/app-blocker.d.ts +3 -1
- package/apps/app-lifeops/src/actions/app-blocker.d.ts.map +1 -1
- package/apps/app-lifeops/src/actions/app-blocker.js +2 -0
- package/apps/app-lifeops/src/actions/calendar.d.ts.map +1 -1
- package/apps/app-lifeops/src/actions/calendar.js +140 -7
- package/apps/app-lifeops/src/actions/computer-use.d.ts.map +1 -1
- package/apps/app-lifeops/src/actions/computer-use.js +248 -17
- package/apps/app-lifeops/src/actions/cross-channel-send.d.ts +3 -1
- package/apps/app-lifeops/src/actions/cross-channel-send.d.ts.map +1 -1
- package/apps/app-lifeops/src/actions/cross-channel-send.js +1 -0
- package/apps/app-lifeops/src/actions/dossier.d.ts.map +1 -1
- package/apps/app-lifeops/src/actions/dossier.js +51 -34
- package/apps/app-lifeops/src/actions/gmail.d.ts.map +1 -1
- package/apps/app-lifeops/src/actions/gmail.js +5 -0
- package/apps/app-lifeops/src/actions/inbox.d.ts.map +1 -1
- package/apps/app-lifeops/src/actions/inbox.js +8 -1
- package/apps/app-lifeops/src/actions/life.d.ts.map +1 -1
- package/apps/app-lifeops/src/actions/life.extractor.d.ts.map +1 -1
- package/apps/app-lifeops/src/actions/life.extractor.js +103 -4
- package/apps/app-lifeops/src/actions/life.js +8 -0
- package/apps/app-lifeops/src/actions/non-actionable-request.d.ts +6 -0
- package/apps/app-lifeops/src/actions/non-actionable-request.d.ts.map +1 -0
- package/apps/app-lifeops/src/actions/non-actionable-request.js +33 -0
- package/apps/app-lifeops/src/actions/password-manager.d.ts.map +1 -1
- package/apps/app-lifeops/src/actions/password-manager.js +11 -2
- package/apps/app-lifeops/src/actions/relationships.d.ts +3 -1
- package/apps/app-lifeops/src/actions/relationships.d.ts.map +1 -1
- package/apps/app-lifeops/src/actions/relationships.js +167 -11
- package/apps/app-lifeops/src/actions/remote-desktop.js +1 -1
- package/apps/app-lifeops/src/actions/screen-time.d.ts.map +1 -1
- package/apps/app-lifeops/src/actions/screen-time.js +8 -2
- package/apps/app-lifeops/src/actions/start-remote-session.js +1 -1
- package/apps/app-lifeops/src/actions/subscriptions.d.ts +5 -0
- package/apps/app-lifeops/src/actions/subscriptions.d.ts.map +1 -0
- package/apps/app-lifeops/src/actions/subscriptions.js +251 -0
- package/apps/app-lifeops/src/actions/twilio-call.d.ts.map +1 -1
- package/apps/app-lifeops/src/actions/twilio-call.js +48 -3
- package/apps/app-lifeops/src/actions/update-owner-profile.d.ts.map +1 -1
- package/apps/app-lifeops/src/actions/update-owner-profile.js +2 -1
- package/apps/app-lifeops/src/actions/website-blocker.d.ts +6 -2
- package/apps/app-lifeops/src/actions/website-blocker.d.ts.map +1 -1
- package/apps/app-lifeops/src/actions/website-blocker.js +38 -0
- package/apps/app-lifeops/src/actions/x-read.d.ts.map +1 -1
- package/apps/app-lifeops/src/actions/x-read.js +27 -3
- package/apps/app-lifeops/src/dossier/action.d.ts.map +1 -1
- package/apps/app-lifeops/src/dossier/action.js +8 -7
- package/apps/app-lifeops/src/followup/actions/listOverdueFollowups.d.ts.map +1 -1
- package/apps/app-lifeops/src/followup/actions/listOverdueFollowups.js +1 -0
- package/apps/app-lifeops/src/followup/actions/markFollowupDone.d.ts.map +1 -1
- package/apps/app-lifeops/src/followup/actions/markFollowupDone.js +2 -1
- package/apps/app-lifeops/src/followup/actions/setFollowupThreshold.d.ts.map +1 -1
- package/apps/app-lifeops/src/followup/actions/setFollowupThreshold.js +2 -1
- package/apps/app-lifeops/src/lifeops/calendly-client.d.ts.map +1 -1
- package/apps/app-lifeops/src/lifeops/calendly-client.js +4 -2
- package/apps/app-lifeops/src/lifeops/discord-browser-scraper.d.ts.map +1 -1
- package/apps/app-lifeops/src/lifeops/discord-browser-scraper.js +6 -5
- package/apps/app-lifeops/src/lifeops/password-manager-bridge.d.ts +1 -1
- package/apps/app-lifeops/src/lifeops/password-manager-bridge.d.ts.map +1 -1
- package/apps/app-lifeops/src/lifeops/password-manager-bridge.js +84 -6
- package/apps/app-lifeops/src/lifeops/remote-desktop.d.ts.map +1 -1
- package/apps/app-lifeops/src/lifeops/remote-desktop.js +30 -0
- package/apps/app-lifeops/src/lifeops/repository.d.ts +15 -0
- package/apps/app-lifeops/src/lifeops/repository.d.ts.map +1 -1
- package/apps/app-lifeops/src/lifeops/repository.js +330 -1
- package/apps/app-lifeops/src/lifeops/service-mixin-discord.js +222 -42
- package/apps/app-lifeops/src/lifeops/service-mixin-subscriptions.d.ts +2 -0
- package/apps/app-lifeops/src/lifeops/service-mixin-subscriptions.d.ts.map +1 -0
- package/apps/app-lifeops/src/lifeops/service-mixin-subscriptions.js +763 -0
- package/apps/app-lifeops/src/lifeops/service.d.ts +64 -1
- package/apps/app-lifeops/src/lifeops/service.d.ts.map +1 -1
- package/apps/app-lifeops/src/lifeops/service.js +2 -1
- package/apps/app-lifeops/src/lifeops/subscriptions-playbooks.d.ts +54 -0
- package/apps/app-lifeops/src/lifeops/subscriptions-playbooks.d.ts.map +1 -0
- package/apps/app-lifeops/src/lifeops/subscriptions-playbooks.js +213 -0
- package/apps/app-lifeops/src/lifeops/subscriptions-types.d.ts +80 -0
- package/apps/app-lifeops/src/lifeops/subscriptions-types.d.ts.map +1 -0
- package/apps/app-lifeops/src/lifeops/twilio.d.ts.map +1 -1
- package/apps/app-lifeops/src/lifeops/twilio.js +4 -2
- package/apps/app-lifeops/src/lifeops/whatsapp-client.d.ts.map +1 -1
- package/apps/app-lifeops/src/lifeops/whatsapp-client.js +4 -2
- package/apps/app-lifeops/src/lifeops/x-poster.d.ts.map +1 -1
- package/apps/app-lifeops/src/lifeops/x-poster.js +12 -6
- package/apps/app-lifeops/src/lifeops/x-reader.d.ts.map +1 -1
- package/apps/app-lifeops/src/lifeops/x-reader.js +7 -5
- package/apps/app-lifeops/src/plugin.d.ts.map +1 -1
- package/apps/app-lifeops/src/plugin.js +31 -24
- package/apps/app-lifeops/src/provider.d.ts.map +1 -1
- package/apps/app-lifeops/src/provider.js +4 -4
- package/apps/app-lifeops/src/providers/inbox-triage.js +2 -2
- package/apps/app-lifeops/src/providers/lifeops.d.ts.map +1 -1
- package/apps/app-lifeops/src/providers/lifeops.js +19 -10
- package/apps/app-lifeops/src/providers/website-blocker.js +1 -1
- package/apps/app-lifeops/src/public.d.ts +4 -0
- package/apps/app-lifeops/src/public.d.ts.map +1 -0
- package/apps/app-lifeops/src/public.js +3 -0
- package/apps/app-lifeops/src/routes/plugin.d.ts +18 -0
- package/apps/app-lifeops/src/routes/plugin.d.ts.map +1 -0
- package/apps/app-lifeops/src/routes/plugin.js +270 -0
- package/apps/app-lifeops/src/website-blocker/chat-integration/actions/blockUntilTaskComplete.d.ts.map +1 -1
- package/apps/app-lifeops/src/website-blocker/chat-integration/actions/blockUntilTaskComplete.js +18 -2
- package/package.json +1 -1
- package/packages/agent/src/actions/browser-session.d.ts +3 -0
- package/packages/agent/src/actions/browser-session.d.ts.map +1 -0
- package/packages/agent/src/actions/browser-session.js +207 -0
- package/packages/agent/src/actions/context-signal.d.ts +1 -1
- package/packages/agent/src/actions/context-signal.d.ts.map +1 -1
- package/packages/agent/src/actions/context-signal.js +1 -1
- package/packages/agent/src/actions/extract-page.d.ts +3 -0
- package/packages/agent/src/actions/extract-page.d.ts.map +1 -0
- package/packages/agent/src/actions/extract-page.js +124 -0
- package/packages/agent/src/actions/grounded-action-reply.js +1 -1
- package/packages/agent/src/actions/recent-conversation-texts.d.ts +9 -0
- package/packages/agent/src/actions/recent-conversation-texts.d.ts.map +1 -0
- package/packages/agent/src/actions/recent-conversation-texts.js +81 -0
- package/packages/agent/src/actions/set-user-name.js +1 -1
- package/packages/agent/src/actions/web-search.d.ts +4 -10
- package/packages/agent/src/actions/web-search.d.ts.map +1 -1
- package/packages/agent/src/actions/web-search.js +89 -19
- package/packages/agent/src/api/binance-skill-helpers.d.ts.map +1 -1
- package/packages/agent/src/api/binance-skill-helpers.js +1 -1
- package/packages/agent/src/api/chat-augmentation.d.ts +1 -20
- package/packages/agent/src/api/chat-augmentation.d.ts.map +1 -1
- package/packages/agent/src/api/chat-augmentation.js +2 -110
- package/packages/agent/src/api/conversation-metadata.d.ts +9 -0
- package/packages/agent/src/api/conversation-metadata.d.ts.map +1 -0
- package/packages/agent/src/api/conversation-metadata.js +98 -0
- package/packages/agent/src/api/conversation-routes.d.ts.map +1 -1
- package/packages/agent/src/api/conversation-routes.js +40 -9
- package/packages/agent/src/api/permissions-routes.js +1 -1
- package/packages/agent/src/api/server-helpers-auth.js +1 -1
- package/packages/agent/src/api/server-helpers.d.ts +2 -1
- package/packages/agent/src/api/server-helpers.d.ts.map +1 -1
- package/packages/agent/src/api/server-helpers.js +2 -110
- package/packages/agent/src/api/server-types.d.ts +14 -0
- package/packages/agent/src/api/server-types.d.ts.map +1 -1
- package/packages/agent/src/api/server.d.ts.map +1 -1
- package/packages/agent/src/api/server.js +3 -13
- package/packages/agent/src/api/workbench-routes.d.ts.map +1 -1
- package/packages/agent/src/api/workbench-routes.js +0 -14
- package/packages/agent/src/auth/credentials.d.ts.map +1 -1
- package/packages/agent/src/auth/credentials.js +8 -0
- package/packages/agent/src/providers/automation-terminal-bridge.d.ts +3 -0
- package/packages/agent/src/providers/automation-terminal-bridge.d.ts.map +1 -0
- package/packages/agent/src/providers/automation-terminal-bridge.js +71 -0
- package/packages/agent/src/providers/recent-conversations.d.ts.map +1 -1
- package/packages/agent/src/providers/recent-conversations.js +5 -0
- package/packages/agent/src/providers/relevant-conversations.d.ts.map +1 -1
- package/packages/agent/src/providers/relevant-conversations.js +5 -0
- package/packages/agent/src/providers/user-name.d.ts.map +1 -1
- package/packages/agent/src/providers/user-name.js +1 -1
- package/packages/agent/src/runtime/eliza-plugin.d.ts.map +1 -1
- package/packages/agent/src/runtime/eliza-plugin.js +6 -0
- package/packages/agent/src/runtime/plugin-resolver.d.ts.map +1 -1
- package/packages/agent/src/runtime/plugin-resolver.js +115 -0
- package/packages/agent/src/services/browser-workspace-types.d.ts +5 -1
- package/packages/agent/src/services/browser-workspace-types.d.ts.map +1 -1
- package/packages/agent/src/services/browser-workspace.d.ts.map +1 -1
- package/packages/agent/src/services/browser-workspace.js +178 -7
- package/packages/agent/src/services/escalation.d.ts +0 -11
- package/packages/agent/src/services/escalation.d.ts.map +1 -1
- package/packages/agent/src/services/escalation.js +19 -58
- package/packages/agent/src/services/hosted-tools.d.ts +70 -0
- package/packages/agent/src/services/hosted-tools.d.ts.map +1 -0
- package/packages/agent/src/services/hosted-tools.js +87 -0
- package/packages/agent/src/services/owner-name.d.ts +4 -0
- package/packages/agent/src/services/owner-name.d.ts.map +1 -0
- package/packages/agent/src/services/owner-name.js +46 -0
- package/packages/agent/src/services/registry-client-queries.d.ts +1 -1
- package/packages/agent/src/services/registry-client-queries.js +1 -1
- package/packages/agent/src/services/registry-client-types.d.ts +1 -1
- package/packages/app-core/src/api/client-automations.d.ts +8 -0
- package/packages/app-core/src/api/client-automations.d.ts.map +1 -0
- package/packages/app-core/src/api/client-automations.js +7 -0
- package/packages/app-core/src/api/client-browser-workspace.d.ts +3 -0
- package/packages/app-core/src/api/client-browser-workspace.d.ts.map +1 -1
- package/packages/app-core/src/api/client-browser-workspace.js +3 -0
- package/packages/app-core/src/api/client-chat.d.ts +8 -2
- package/packages/app-core/src/api/client-chat.d.ts.map +1 -1
- package/packages/app-core/src/api/client-chat.js +14 -1
- package/packages/app-core/src/api/client-types-chat.d.ts +4 -0
- package/packages/app-core/src/api/client-types-chat.d.ts.map +1 -1
- package/packages/app-core/src/api/client-types-config.d.ts +67 -2
- package/packages/app-core/src/api/client-types-config.d.ts.map +1 -1
- package/packages/app-core/src/api/client.d.ts +1 -0
- package/packages/app-core/src/api/client.d.ts.map +1 -1
- package/packages/app-core/src/api/client.js +1 -0
- package/packages/app-core/src/components/apps/helpers.d.ts +1 -1
- package/packages/app-core/src/components/apps/helpers.d.ts.map +1 -1
- package/packages/app-core/src/components/apps/helpers.js +5 -9
- package/packages/app-core/src/components/apps/internal-tool-apps.d.ts.map +1 -1
- package/packages/app-core/src/components/apps/internal-tool-apps.js +10 -1
- package/packages/app-core/src/components/conversations/conversation-sidebar-model.d.ts.map +1 -1
- package/packages/app-core/src/components/conversations/conversation-sidebar-model.js +6 -0
- package/packages/app-core/src/components/pages/AutomationRoomChatPane.d.ts +16 -0
- package/packages/app-core/src/components/pages/AutomationRoomChatPane.d.ts.map +1 -0
- package/packages/app-core/src/components/pages/AutomationRoomChatPane.js +202 -0
- package/packages/app-core/src/components/pages/AutomationsView.d.ts +1 -4
- package/packages/app-core/src/components/pages/AutomationsView.d.ts.map +1 -1
- package/packages/app-core/src/components/pages/AutomationsView.js +565 -265
- package/packages/app-core/src/components/pages/BrowserWorkspaceView.d.ts.map +1 -1
- package/packages/app-core/src/components/pages/BrowserWorkspaceView.js +118 -24
- package/packages/app-core/src/components/pages/automation-conversations.d.ts +15 -0
- package/packages/app-core/src/components/pages/automation-conversations.d.ts.map +1 -0
- package/packages/app-core/src/components/pages/automation-conversations.js +190 -0
- package/packages/app-core/src/components/pages/plugin-list-utils.js +4 -4
- package/packages/app-core/src/config/boot-config-store.d.ts +1 -1
- package/packages/app-core/src/config/boot-config-store.d.ts.map +1 -1
- package/packages/app-core/src/state/useOnboardingCallbacks.d.ts +14 -0
- package/packages/app-core/src/state/useOnboardingCallbacks.d.ts.map +1 -1
- package/packages/app-core/src/state/useOnboardingCallbacks.js +1 -1
- package/packages/app-core/src/state/usePluginsSkillsState.d.ts +2 -2
- package/packages/app-core/src/utils/desktop-workspace.d.ts +2 -2
- package/packages/app-core/src/utils/desktop-workspace.d.ts.map +1 -1
- package/packages/app-core/src/utils/desktop-workspace.js +7 -2
- package/packages/shared/src/contracts/lifeops.d.ts +26 -0
- package/packages/shared/src/contracts/lifeops.d.ts.map +1 -1
- package/packages/shared/src/contracts/lifeops.js +28 -0
- package/packages/typescript/src/actions.d.ts.map +1 -1
- package/packages/typescript/src/actions.js +12 -0
- package/packages/typescript/src/features/advanced-capabilities/actions/scheduleFollowUp.d.ts.map +1 -1
- package/packages/typescript/src/features/advanced-capabilities/actions/scheduleFollowUp.js +8 -2
- package/packages/typescript/src/features/advanced-capabilities/providers/settings.d.ts.map +1 -1
- package/packages/typescript/src/features/advanced-capabilities/providers/settings.js +11 -3
- package/packages/typescript/src/features/basic-capabilities/providers/actions.d.ts.map +1 -1
- package/packages/typescript/src/features/basic-capabilities/providers/actions.js +23 -1
- package/packages/typescript/src/features/basic-capabilities/providers/non-actionable-chatter.d.ts +5 -0
- package/packages/typescript/src/features/basic-capabilities/providers/non-actionable-chatter.d.ts.map +1 -0
- package/packages/typescript/src/features/basic-capabilities/providers/non-actionable-chatter.js +22 -0
- package/packages/typescript/src/features/basic-capabilities/providers/providers.d.ts.map +1 -1
- package/packages/typescript/src/features/basic-capabilities/providers/providers.js +10 -5
- package/packages/typescript/src/features/knowledge/documents-provider.d.ts.map +1 -1
- package/packages/typescript/src/features/knowledge/documents-provider.js +1 -0
- package/packages/typescript/src/features/shared/schedule-follow-up-response.d.ts +10 -0
- package/packages/typescript/src/features/shared/schedule-follow-up-response.d.ts.map +1 -0
- package/packages/typescript/src/features/shared/schedule-follow-up-response.js +49 -0
- package/packages/typescript/src/features/trajectories/TrajectoriesService.d.ts +1 -0
- package/packages/typescript/src/features/trajectories/TrajectoriesService.d.ts.map +1 -1
- package/packages/typescript/src/features/trajectories/TrajectoriesService.js +21 -2
- package/packages/typescript/src/features/trust/providers/settings.d.ts.map +1 -1
- package/packages/typescript/src/features/trust/providers/settings.js +11 -1
- package/packages/typescript/src/prompts.d.ts +4 -4
- package/packages/typescript/src/prompts.d.ts.map +1 -1
- package/packages/typescript/src/prompts.js +10 -2
- package/packages/typescript/src/services/message.d.ts +1 -0
- package/packages/typescript/src/services/message.d.ts.map +1 -1
- package/packages/typescript/src/services/message.js +483 -18
- package/packages/typescript/src/types/components.d.ts +6 -0
- package/packages/typescript/src/types/components.d.ts.map +1 -1
- package/packages/typescript/src/utils/context-routing.d.ts.map +1 -1
- package/packages/typescript/src/utils/context-routing.js +5 -1
- package/packages/typescript/src/utils/toon.d.ts.map +1 -1
- package/packages/typescript/src/utils/toon.js +23 -2
- package/packages/typescript/src/utils.d.ts +1 -0
- package/packages/typescript/src/utils.d.ts.map +1 -1
- package/packages/typescript/src/utils.js +1 -0
- package/apps/app-lifeops/src/types/app-blocker-settings-card.d.ts +0 -2
- package/apps/app-lifeops/src/types/app-blocker-settings-card.d.ts.map +0 -1
- package/apps/app-lifeops/src/types/index.d.ts +0 -3
- package/apps/app-lifeops/src/types/index.d.ts.map +0 -1
- package/apps/app-lifeops/src/types/index.js +0 -1
- package/apps/app-lifeops/src/types/website-blocker-settings-card.d.ts +0 -2
- package/apps/app-lifeops/src/types/website-blocker-settings-card.d.ts.map +0 -1
- package/apps/app-lifeops/src/types/website-blocker-settings-card.js +0 -1
- package/packages/agent/src/contracts/lifeops.d.ts +0 -2
- package/packages/agent/src/contracts/lifeops.d.ts.map +0 -1
- package/packages/agent/src/contracts/lifeops.js +0 -1
- package/packages/app-core/src/components/pages/N8nWorkflowsPanel.d.ts +0 -18
- package/packages/app-core/src/components/pages/N8nWorkflowsPanel.d.ts.map +0 -1
- package/packages/app-core/src/components/pages/N8nWorkflowsPanel.js +0 -542
- /package/apps/app-lifeops/src/{types/app-blocker-settings-card.js → lifeops/subscriptions-types.js} +0 -0
|
@@ -1,23 +1,27 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
/**
|
|
3
|
-
* AutomationsView — unified
|
|
4
|
-
*
|
|
5
|
-
* Combines the previous "Heartbeats" (triggers) and "Tasks" views into a
|
|
6
|
-
* single /automations route with filter tabs.
|
|
3
|
+
* AutomationsView — unified list/detail UI for coordinator and workflow automations.
|
|
7
4
|
*/
|
|
8
5
|
import { Button, FieldLabel, Input, PageLayout, PagePanel, Sidebar, SidebarCollapsedActionButton, SidebarContent, SidebarPanel, SidebarScrollRegion, StatusBadge, Textarea, } from "@elizaos/ui";
|
|
9
|
-
import { CheckCircle2, Circle, Clock3,
|
|
6
|
+
import { CheckCircle2, Circle, Clock3, GitBranch, Mail, Plus, Settings, Signal, SquareTerminal, Workflow, Zap, } from "lucide-react";
|
|
10
7
|
import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, } from "react";
|
|
11
8
|
import { client } from "../../api";
|
|
12
9
|
import { useApp } from "../../state";
|
|
13
10
|
import { confirmDesktopAction } from "../../utils";
|
|
14
11
|
import { formatDateTime, formatDurationMs } from "../../utils/format";
|
|
15
12
|
import { WidgetHost } from "../../widgets";
|
|
13
|
+
import { AutomationRoomChatPane } from "./AutomationRoomChatPane";
|
|
16
14
|
import { HeartbeatForm } from "./HeartbeatForm";
|
|
17
15
|
import { buildCreateRequest, buildUpdateRequest, emptyForm, formFromTrigger, loadUserTemplates, localizedExecutionStatus, railMonogram, saveUserTemplates, scheduleLabel, toneForLastStatus, validateForm, } from "./heartbeat-utils";
|
|
18
|
-
import {
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
import { buildAutomationResponseRoutingMetadata, buildCoordinatorConversationMetadata, buildCoordinatorTriggerConversationMetadata, buildWorkflowConversationMetadata, buildWorkflowDraftConversationMetadata, getAutomationBridgeConversationId, resolveAutomationConversation, } from "./automation-conversations";
|
|
17
|
+
const WORKFLOW_DRAFT_TITLE = "New Workflow Draft";
|
|
18
|
+
const WORKFLOW_SYSTEM_ADDENDUM = "You are in a workflow-specific automation room. Focus only on this " +
|
|
19
|
+
"workflow. Use the linked terminal conversation only when it directly " +
|
|
20
|
+
"informs the workflow. Request keys and connector setup when needed, and " +
|
|
21
|
+
"prefer owner-scoped LifeOps integrations for personal services.";
|
|
22
|
+
const COORDINATOR_SYSTEM_ADDENDUM = "You are in a workflow-specific automation room for a coordinator " +
|
|
23
|
+
"automation. Focus only on this automation. Use the linked terminal " +
|
|
24
|
+
"conversation only when it directly informs the automation.";
|
|
21
25
|
const SYSTEM_TASK_NAMES = new Set([
|
|
22
26
|
"EMBEDDING_DRAIN",
|
|
23
27
|
"PROACTIVE_AGENT",
|
|
@@ -25,123 +29,239 @@ const SYSTEM_TASK_NAMES = new Set([
|
|
|
25
29
|
"TRIGGER_DISPATCH",
|
|
26
30
|
"heartbeat",
|
|
27
31
|
]);
|
|
32
|
+
const NODE_CLASS_ORDER = [
|
|
33
|
+
"agent",
|
|
34
|
+
"action",
|
|
35
|
+
"context",
|
|
36
|
+
"integration",
|
|
37
|
+
"trigger",
|
|
38
|
+
"flow-control",
|
|
39
|
+
];
|
|
40
|
+
function createWorkflowDraftId() {
|
|
41
|
+
return globalThis.crypto.randomUUID();
|
|
42
|
+
}
|
|
28
43
|
function isSystemTask(task) {
|
|
29
|
-
if (SYSTEM_TASK_NAMES.has(task.name))
|
|
44
|
+
if (SYSTEM_TASK_NAMES.has(task.name)) {
|
|
30
45
|
return true;
|
|
31
|
-
|
|
46
|
+
}
|
|
32
47
|
const tags = new Set(task.tags ?? []);
|
|
33
|
-
|
|
34
|
-
return true;
|
|
35
|
-
return false;
|
|
48
|
+
return tags.has("queue") && tags.has("repeat");
|
|
36
49
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
50
|
+
function getSelectionKind(item) {
|
|
51
|
+
if (!item)
|
|
52
|
+
return null;
|
|
53
|
+
if (item.type === "n8n_workflow")
|
|
54
|
+
return "workflow";
|
|
55
|
+
if (item.task)
|
|
56
|
+
return "task";
|
|
57
|
+
if (item.trigger)
|
|
58
|
+
return "trigger";
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
function getAutomationSearchText(item) {
|
|
62
|
+
return [item.title, item.description]
|
|
63
|
+
.map((value) => value.trim().toLowerCase())
|
|
64
|
+
.filter(Boolean)
|
|
65
|
+
.join("\n");
|
|
66
|
+
}
|
|
67
|
+
function getAutomationBridgeIdForItem(item, activeConversationId, conversations) {
|
|
68
|
+
return (item?.room?.terminalBridgeConversationId ??
|
|
69
|
+
item?.room?.sourceConversationId ??
|
|
70
|
+
getAutomationBridgeConversationId(activeConversationId, conversations));
|
|
71
|
+
}
|
|
72
|
+
function getWorkflowNodeCount(item) {
|
|
73
|
+
return item.workflow?.nodeCount ?? item.workflow?.nodes?.length ?? 0;
|
|
74
|
+
}
|
|
75
|
+
function getAutomationIndicatorTone(item) {
|
|
76
|
+
if (item.type === "n8n_workflow") {
|
|
77
|
+
return item.enabled ? "accent" : undefined;
|
|
78
|
+
}
|
|
79
|
+
if (item.task) {
|
|
80
|
+
return item.task.isCompleted ? undefined : "accent";
|
|
81
|
+
}
|
|
82
|
+
if (item.trigger) {
|
|
83
|
+
return item.trigger.enabled ? "accent" : undefined;
|
|
84
|
+
}
|
|
85
|
+
return undefined;
|
|
86
|
+
}
|
|
87
|
+
function buildTriggerSchedulePrompt(trigger) {
|
|
88
|
+
if (trigger.triggerType === "interval") {
|
|
89
|
+
return `Schedule: interval every ${trigger.intervalMs ?? 0}ms.`;
|
|
90
|
+
}
|
|
91
|
+
if (trigger.triggerType === "once") {
|
|
92
|
+
return `Schedule: run once at ${trigger.scheduledAtIso ?? "an unspecified time"}.`;
|
|
93
|
+
}
|
|
94
|
+
if (trigger.triggerType === "cron") {
|
|
95
|
+
return `Schedule: cron ${trigger.cronExpression ?? ""}.`;
|
|
96
|
+
}
|
|
97
|
+
return `Schedule type: ${trigger.triggerType}.`;
|
|
98
|
+
}
|
|
99
|
+
function buildWorkflowCompilationPrompt(item) {
|
|
100
|
+
const lines = [
|
|
101
|
+
"Compile this coordinator automation into an n8n workflow.",
|
|
102
|
+
`Automation title: ${item.title}`,
|
|
103
|
+
`Description: ${item.description || "No additional description provided."}`,
|
|
104
|
+
"Keep the workflow in this dedicated automation room.",
|
|
105
|
+
"Use runtime actions and providers as workflow nodes when they fit the job.",
|
|
106
|
+
"Use owner-scoped LifeOps nodes for Gmail, Calendar, Signal, Telegram, Discord, and GitHub when they are set up. If not, request the required setup or keys.",
|
|
107
|
+
];
|
|
108
|
+
if (item.task) {
|
|
109
|
+
lines.push(`Task description: ${item.task.description || "No task description."}`);
|
|
110
|
+
}
|
|
111
|
+
if (item.trigger) {
|
|
112
|
+
lines.push(`Coordinator instructions: ${item.trigger.instructions}`);
|
|
113
|
+
lines.push(buildTriggerSchedulePrompt(item.trigger));
|
|
114
|
+
}
|
|
115
|
+
if (item.schedules.length > 0) {
|
|
116
|
+
lines.push("Existing schedules:");
|
|
117
|
+
for (const schedule of item.schedules) {
|
|
118
|
+
lines.push(`- ${buildTriggerSchedulePrompt(schedule)}`);
|
|
47
119
|
}
|
|
48
120
|
}
|
|
49
|
-
|
|
121
|
+
lines.push("Ask follow-up questions only when workflow intent is genuinely ambiguous.");
|
|
122
|
+
return lines.join("\n");
|
|
123
|
+
}
|
|
124
|
+
function getNodeClassLabel(className) {
|
|
125
|
+
switch (className) {
|
|
126
|
+
case "agent":
|
|
127
|
+
return "Agent";
|
|
128
|
+
case "action":
|
|
129
|
+
return "Actions";
|
|
130
|
+
case "context":
|
|
131
|
+
return "Context";
|
|
132
|
+
case "integration":
|
|
133
|
+
return "Integrations";
|
|
134
|
+
case "trigger":
|
|
135
|
+
return "Triggers";
|
|
136
|
+
case "flow-control":
|
|
137
|
+
return "Flow Control";
|
|
138
|
+
default:
|
|
139
|
+
return className;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function getNodeIcon(node) {
|
|
143
|
+
if (node.source === "lifeops") {
|
|
144
|
+
if (node.id === "lifeops:gmail")
|
|
145
|
+
return _jsx(Mail, { className: "h-3.5 w-3.5" });
|
|
146
|
+
if (node.id === "lifeops:signal")
|
|
147
|
+
return _jsx(Signal, { className: "h-3.5 w-3.5" });
|
|
148
|
+
if (node.id === "lifeops:github") {
|
|
149
|
+
return _jsx(GitBranch, { className: "h-3.5 w-3.5" });
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
if (node.class === "agent") {
|
|
153
|
+
return _jsx(SquareTerminal, { className: "h-3.5 w-3.5" });
|
|
154
|
+
}
|
|
155
|
+
if (node.class === "integration") {
|
|
156
|
+
return _jsx(Workflow, { className: "h-3.5 w-3.5" });
|
|
157
|
+
}
|
|
158
|
+
if (node.class === "context") {
|
|
159
|
+
return _jsx(Settings, { className: "h-3.5 w-3.5" });
|
|
160
|
+
}
|
|
161
|
+
if (node.class === "trigger") {
|
|
162
|
+
return _jsx(Clock3, { className: "h-3.5 w-3.5" });
|
|
163
|
+
}
|
|
164
|
+
return _jsx(Zap, { className: "h-3.5 w-3.5" });
|
|
50
165
|
}
|
|
51
|
-
// ── View controller hook ──────────────────────────────────────────
|
|
52
166
|
function useAutomationsViewController() {
|
|
53
|
-
const { triggers = [], triggersLoaded = false, triggersLoading = false, triggersSaving = false, triggerRunsById = {},
|
|
167
|
+
const { triggers = [], triggersLoaded = false, triggersLoading = false, triggersSaving = false, triggerRunsById = {}, triggerError = null, loadTriggers = async () => { }, createTrigger = async () => null, updateTrigger = async () => null, deleteTrigger = async () => true, runTriggerNow = async () => true, loadTriggerRuns = async () => { }, loadTriggerHealth = async () => { }, ensureTriggersLoaded = async () => {
|
|
54
168
|
await loadTriggers(triggersLoaded ? { silent: true } : undefined);
|
|
55
169
|
}, t, uiLanguage, } = useApp();
|
|
56
|
-
// ── Workbench tasks state ───────────────────────────────────────
|
|
57
|
-
const [workbenchTasks, setWorkbenchTasks] = useState([]);
|
|
58
|
-
const [tasksLoading, setTasksLoading] = useState(false);
|
|
59
|
-
const [_tasksLoaded, setTasksLoaded] = useState(false);
|
|
60
170
|
const [taskError, setTaskError] = useState(null);
|
|
61
171
|
const [taskSaving, setTaskSaving] = useState(false);
|
|
62
|
-
const
|
|
63
|
-
|
|
172
|
+
const [form, setForm] = useState(emptyForm);
|
|
173
|
+
const [editingId, setEditingId] = useState(null);
|
|
174
|
+
const [selectedItemId, setSelectedItemId] = useState(null);
|
|
175
|
+
const [selectedItemKind, setSelectedItemKind] = useState(null);
|
|
176
|
+
const [formError, setFormError] = useState(null);
|
|
177
|
+
const [editorOpen, setEditorOpen] = useState(false);
|
|
178
|
+
const [editorMode, setEditorMode] = useState("trigger");
|
|
179
|
+
const [userTemplates, setUserTemplates] = useState(loadUserTemplates);
|
|
180
|
+
const [templateNotice, setTemplateNotice] = useState(null);
|
|
181
|
+
const [taskFormName, setTaskFormName] = useState("");
|
|
182
|
+
const [taskFormDescription, setTaskFormDescription] = useState("");
|
|
183
|
+
const [editingTaskId, setEditingTaskId] = useState(null);
|
|
184
|
+
const [filter, setFilter] = useState("all");
|
|
185
|
+
const [automationItems, setAutomationItems] = useState([]);
|
|
186
|
+
const [automationNodes, setAutomationNodes] = useState([]);
|
|
187
|
+
const [automationsLoading, setAutomationsLoading] = useState(false);
|
|
188
|
+
const [automationsLoaded, setAutomationsLoaded] = useState(false);
|
|
189
|
+
const [automationsError, setAutomationsError] = useState(null);
|
|
190
|
+
const [n8nStatus, setN8nStatus] = useState(null);
|
|
191
|
+
const [workflowFetchError, setWorkflowFetchError] = useState(null);
|
|
192
|
+
const didBootstrapDataRef = useRef(false);
|
|
193
|
+
const lastSelectedIdRef = useRef(null);
|
|
194
|
+
const refreshAutomations = useCallback(async () => {
|
|
195
|
+
setAutomationsLoading(true);
|
|
64
196
|
try {
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
197
|
+
const [automationData, nodeCatalog] = await Promise.all([
|
|
198
|
+
client.listAutomations(),
|
|
199
|
+
client.getAutomationNodeCatalog(),
|
|
200
|
+
]);
|
|
201
|
+
setAutomationItems(automationData.automations ?? []);
|
|
202
|
+
setAutomationNodes(nodeCatalog.nodes ?? []);
|
|
203
|
+
setN8nStatus(automationData.n8nStatus ?? null);
|
|
204
|
+
setWorkflowFetchError(automationData.workflowFetchError ?? null);
|
|
205
|
+
setAutomationsError(null);
|
|
206
|
+
return automationData;
|
|
68
207
|
}
|
|
69
|
-
catch (
|
|
70
|
-
|
|
208
|
+
catch (error) {
|
|
209
|
+
setAutomationsError(error instanceof Error ? error.message : "Failed to load automations");
|
|
210
|
+
return null;
|
|
71
211
|
}
|
|
72
212
|
finally {
|
|
73
|
-
|
|
74
|
-
|
|
213
|
+
setAutomationsLoaded(true);
|
|
214
|
+
setAutomationsLoading(false);
|
|
75
215
|
}
|
|
76
216
|
}, []);
|
|
77
217
|
const createWorkbenchTask = useCallback(async (data) => {
|
|
78
218
|
setTaskSaving(true);
|
|
79
219
|
try {
|
|
80
220
|
const res = await client.createWorkbenchTask(data);
|
|
81
|
-
const created = res.task;
|
|
82
|
-
setWorkbenchTasks((prev) => [...prev, created]);
|
|
83
221
|
setTaskError(null);
|
|
84
|
-
|
|
222
|
+
await refreshAutomations();
|
|
223
|
+
return res.task;
|
|
85
224
|
}
|
|
86
|
-
catch (
|
|
87
|
-
setTaskError(
|
|
225
|
+
catch (error) {
|
|
226
|
+
setTaskError(error instanceof Error ? error.message : "Failed to create task");
|
|
88
227
|
return null;
|
|
89
228
|
}
|
|
90
229
|
finally {
|
|
91
230
|
setTaskSaving(false);
|
|
92
231
|
}
|
|
93
|
-
}, []);
|
|
232
|
+
}, [refreshAutomations]);
|
|
94
233
|
const updateWorkbenchTask = useCallback(async (id, data) => {
|
|
95
234
|
setTaskSaving(true);
|
|
96
235
|
try {
|
|
97
236
|
const res = await client.updateWorkbenchTask(id, data);
|
|
98
|
-
const updated = res.task;
|
|
99
|
-
setWorkbenchTasks((prev) => prev.map((item) => (item.id === updated.id ? updated : item)));
|
|
100
237
|
setTaskError(null);
|
|
101
|
-
|
|
238
|
+
await refreshAutomations();
|
|
239
|
+
return res.task;
|
|
102
240
|
}
|
|
103
|
-
catch (
|
|
104
|
-
setTaskError(
|
|
241
|
+
catch (error) {
|
|
242
|
+
setTaskError(error instanceof Error ? error.message : "Failed to update task");
|
|
105
243
|
return null;
|
|
106
244
|
}
|
|
107
245
|
finally {
|
|
108
246
|
setTaskSaving(false);
|
|
109
247
|
}
|
|
110
|
-
}, []);
|
|
248
|
+
}, [refreshAutomations]);
|
|
111
249
|
const deleteWorkbenchTask = useCallback(async (id) => {
|
|
112
250
|
setTaskSaving(true);
|
|
113
251
|
try {
|
|
114
252
|
await client.deleteWorkbenchTask(id);
|
|
115
|
-
setWorkbenchTasks((prev) => prev.filter((item) => item.id !== id));
|
|
116
253
|
setTaskError(null);
|
|
254
|
+
await refreshAutomations();
|
|
117
255
|
return true;
|
|
118
256
|
}
|
|
119
|
-
catch (
|
|
120
|
-
setTaskError(
|
|
257
|
+
catch (error) {
|
|
258
|
+
setTaskError(error instanceof Error ? error.message : "Failed to delete task");
|
|
121
259
|
return false;
|
|
122
260
|
}
|
|
123
261
|
finally {
|
|
124
262
|
setTaskSaving(false);
|
|
125
263
|
}
|
|
126
|
-
}, []);
|
|
127
|
-
// ── Trigger form state (reused from HeartbeatsView) ─────────────
|
|
128
|
-
const [form, setForm] = useState(emptyForm);
|
|
129
|
-
const [editingId, setEditingId] = useState(null);
|
|
130
|
-
const [selectedItemId, setSelectedItemId] = useState(null);
|
|
131
|
-
const [selectedItemKind, setSelectedItemKind] = useState(null);
|
|
132
|
-
const [formError, setFormError] = useState(null);
|
|
133
|
-
const [editorOpen, setEditorOpen] = useState(false);
|
|
134
|
-
const [editorMode, setEditorMode] = useState("trigger");
|
|
135
|
-
const lastSelectedIdRef = useRef(null);
|
|
136
|
-
const [userTemplates, setUserTemplates] = useState(loadUserTemplates);
|
|
137
|
-
const [templateNotice, setTemplateNotice] = useState(null);
|
|
138
|
-
const didBootstrapDataRef = useRef(false);
|
|
139
|
-
// ── Task create form state ──────────────────────────────────────
|
|
140
|
-
const [taskFormName, setTaskFormName] = useState("");
|
|
141
|
-
const [taskFormDescription, setTaskFormDescription] = useState("");
|
|
142
|
-
const [editingTaskId, setEditingTaskId] = useState(null);
|
|
143
|
-
// ── Filter state ────────────────────────────────────────────────
|
|
144
|
-
const [filter, setFilter] = useState("all");
|
|
264
|
+
}, [refreshAutomations]);
|
|
145
265
|
const saveFormAsTemplate = useCallback(() => {
|
|
146
266
|
const name = form.displayName.trim();
|
|
147
267
|
if (!name)
|
|
@@ -153,35 +273,30 @@ function useAutomationsViewController() {
|
|
|
153
273
|
interval: form.durationValue || "1",
|
|
154
274
|
unit: form.durationUnit,
|
|
155
275
|
};
|
|
156
|
-
setUserTemplates((
|
|
157
|
-
const next = [...
|
|
276
|
+
setUserTemplates((previous) => {
|
|
277
|
+
const next = [...previous, template];
|
|
158
278
|
saveUserTemplates(next);
|
|
159
279
|
return next;
|
|
160
280
|
});
|
|
161
281
|
}, [form]);
|
|
162
282
|
const deleteUserTemplate = useCallback((id) => {
|
|
163
|
-
setUserTemplates((
|
|
164
|
-
const next =
|
|
283
|
+
setUserTemplates((previous) => {
|
|
284
|
+
const next = previous.filter((template) => template.id !== id);
|
|
165
285
|
saveUserTemplates(next);
|
|
166
286
|
return next;
|
|
167
287
|
});
|
|
168
288
|
}, []);
|
|
169
|
-
// ── Bootstrap data ──────────────────────────────────────────────
|
|
170
289
|
useEffect(() => {
|
|
171
290
|
if (didBootstrapDataRef.current)
|
|
172
291
|
return;
|
|
173
292
|
didBootstrapDataRef.current = true;
|
|
174
293
|
void loadTriggerHealth();
|
|
175
294
|
void ensureTriggersLoaded();
|
|
176
|
-
void
|
|
177
|
-
}, [ensureTriggersLoaded, loadTriggerHealth,
|
|
178
|
-
// ── Cross-component filter navigation ─────────────────────────
|
|
179
|
-
// HeartbeatForm dispatches this event when the user clicks "Go to Workflows"
|
|
180
|
-
// from the workflow-unavailable state inside the kind section.
|
|
295
|
+
void refreshAutomations();
|
|
296
|
+
}, [ensureTriggersLoaded, loadTriggerHealth, refreshAutomations]);
|
|
181
297
|
useEffect(() => {
|
|
182
298
|
const handler = (event) => {
|
|
183
|
-
const detail = event
|
|
184
|
-
.detail;
|
|
299
|
+
const detail = event.detail;
|
|
185
300
|
if (detail?.filter) {
|
|
186
301
|
setFilter(detail.filter);
|
|
187
302
|
}
|
|
@@ -189,63 +304,19 @@ function useAutomationsViewController() {
|
|
|
189
304
|
window.addEventListener("milady:automations:setFilter", handler);
|
|
190
305
|
return () => window.removeEventListener("milady:automations:setFilter", handler);
|
|
191
306
|
}, []);
|
|
192
|
-
|
|
193
|
-
const allItems = useMemo(() => {
|
|
194
|
-
const items = [];
|
|
195
|
-
for (const trigger of triggers) {
|
|
196
|
-
items.push({
|
|
197
|
-
kind: "trigger",
|
|
198
|
-
id: `trigger:${trigger.id}`,
|
|
199
|
-
name: trigger.displayName,
|
|
200
|
-
trigger,
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
// Separate user tasks from system tasks
|
|
204
|
-
const userTasks = [];
|
|
205
|
-
const systemTasks = [];
|
|
206
|
-
for (const task of workbenchTasks) {
|
|
207
|
-
if (isSystemTask(task)) {
|
|
208
|
-
systemTasks.push(task);
|
|
209
|
-
}
|
|
210
|
-
else {
|
|
211
|
-
userTasks.push(task);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
// Add user-created tasks
|
|
215
|
-
for (const task of userTasks) {
|
|
216
|
-
items.push({
|
|
217
|
-
kind: "task",
|
|
218
|
-
id: `task:${task.id}`,
|
|
219
|
-
name: task.name,
|
|
220
|
-
task,
|
|
221
|
-
system: false,
|
|
222
|
-
});
|
|
223
|
-
}
|
|
224
|
-
// Add deduplicated system tasks
|
|
225
|
-
for (const task of deduplicateSystemTasks(systemTasks)) {
|
|
226
|
-
items.push({
|
|
227
|
-
kind: "task",
|
|
228
|
-
id: `task:${task.id}`,
|
|
229
|
-
name: task.name,
|
|
230
|
-
task,
|
|
231
|
-
system: true,
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
|
-
return items;
|
|
235
|
-
}, [triggers, workbenchTasks]);
|
|
307
|
+
const allItems = automationItems;
|
|
236
308
|
const filteredItems = useMemo(() => {
|
|
237
309
|
switch (filter) {
|
|
310
|
+
case "coordinator":
|
|
311
|
+
return allItems.filter((item) => item.type === "coordinator_text");
|
|
312
|
+
case "workflows":
|
|
313
|
+
return allItems.filter((item) => item.type === "n8n_workflow");
|
|
238
314
|
case "scheduled":
|
|
239
|
-
return allItems.filter((item) => item.
|
|
240
|
-
case "my-tasks":
|
|
241
|
-
return allItems.filter((item) => item.kind === "task" && !item.system);
|
|
242
|
-
case "system":
|
|
243
|
-
return allItems.filter((item) => (item.kind === "task" && item.system) || item.kind === "trigger");
|
|
315
|
+
return allItems.filter((item) => item.schedules.length > 0);
|
|
244
316
|
default:
|
|
245
317
|
return allItems;
|
|
246
318
|
}
|
|
247
319
|
}, [allItems, filter]);
|
|
248
|
-
// Clear stale selection
|
|
249
320
|
useEffect(() => {
|
|
250
321
|
if (!selectedItemId)
|
|
251
322
|
return;
|
|
@@ -253,31 +324,30 @@ function useAutomationsViewController() {
|
|
|
253
324
|
setSelectedItemId(null);
|
|
254
325
|
setSelectedItemKind(null);
|
|
255
326
|
}
|
|
256
|
-
}, [
|
|
327
|
+
}, [allItems, selectedItemId]);
|
|
257
328
|
useEffect(() => {
|
|
258
329
|
if (selectedItemId) {
|
|
259
330
|
lastSelectedIdRef.current = selectedItemId;
|
|
260
331
|
}
|
|
261
332
|
}, [selectedItemId]);
|
|
262
|
-
// Auto-select first item
|
|
263
333
|
useEffect(() => {
|
|
264
334
|
if (editorOpen ||
|
|
265
335
|
editingId ||
|
|
266
336
|
editingTaskId ||
|
|
267
337
|
selectedItemId ||
|
|
268
|
-
allItems.length === 0)
|
|
338
|
+
allItems.length === 0) {
|
|
269
339
|
return;
|
|
340
|
+
}
|
|
270
341
|
const preferred = lastSelectedIdRef.current;
|
|
271
342
|
const next = preferred && allItems.some((item) => item.id === preferred)
|
|
272
343
|
? preferred
|
|
273
344
|
: (allItems[0]?.id ?? null);
|
|
274
|
-
if (next)
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
}, [
|
|
280
|
-
// Escape key
|
|
345
|
+
if (!next)
|
|
346
|
+
return;
|
|
347
|
+
const item = allItems.find((candidate) => candidate.id === next) ?? null;
|
|
348
|
+
setSelectedItemId(next);
|
|
349
|
+
setSelectedItemKind(getSelectionKind(item));
|
|
350
|
+
}, [allItems, editingId, editingTaskId, editorOpen, selectedItemId]);
|
|
281
351
|
useEffect(() => {
|
|
282
352
|
if (!editorOpen)
|
|
283
353
|
return undefined;
|
|
@@ -295,7 +365,6 @@ function useAutomationsViewController() {
|
|
|
295
365
|
window.addEventListener("keydown", onKeyDown);
|
|
296
366
|
return () => window.removeEventListener("keydown", onKeyDown);
|
|
297
367
|
}, [editorOpen]);
|
|
298
|
-
// ── Editor helpers ──────────────────────────────────────────────
|
|
299
368
|
const resetEditor = () => {
|
|
300
369
|
setForm(emptyForm);
|
|
301
370
|
setEditingId(null);
|
|
@@ -349,6 +418,7 @@ function useAutomationsViewController() {
|
|
|
349
418
|
if (updated) {
|
|
350
419
|
setSelectedItemId(`trigger:${updated.id}`);
|
|
351
420
|
setSelectedItemKind("trigger");
|
|
421
|
+
await refreshAutomations();
|
|
352
422
|
closeEditor();
|
|
353
423
|
}
|
|
354
424
|
return;
|
|
@@ -358,6 +428,7 @@ function useAutomationsViewController() {
|
|
|
358
428
|
setSelectedItemId(`trigger:${created.id}`);
|
|
359
429
|
setSelectedItemKind("trigger");
|
|
360
430
|
void loadTriggerRuns(created.id);
|
|
431
|
+
await refreshAutomations();
|
|
361
432
|
closeEditor();
|
|
362
433
|
}
|
|
363
434
|
};
|
|
@@ -409,6 +480,7 @@ function useAutomationsViewController() {
|
|
|
409
480
|
setSelectedItemId(null);
|
|
410
481
|
setSelectedItemKind(null);
|
|
411
482
|
}
|
|
483
|
+
await refreshAutomations();
|
|
412
484
|
closeEditor();
|
|
413
485
|
};
|
|
414
486
|
const onDeleteTask = async (taskId) => {
|
|
@@ -436,6 +508,8 @@ function useAutomationsViewController() {
|
|
|
436
508
|
setSelectedItemId(`trigger:${triggerId}`);
|
|
437
509
|
setSelectedItemKind("trigger");
|
|
438
510
|
await runTriggerNow(triggerId);
|
|
511
|
+
await loadTriggerRuns(triggerId);
|
|
512
|
+
await refreshAutomations();
|
|
439
513
|
};
|
|
440
514
|
const onToggleTriggerEnabled = async (triggerId, currentlyEnabled) => {
|
|
441
515
|
const updated = await updateTrigger(triggerId, {
|
|
@@ -444,11 +518,13 @@ function useAutomationsViewController() {
|
|
|
444
518
|
if (updated && editingId === updated.id) {
|
|
445
519
|
setForm(formFromTrigger(updated));
|
|
446
520
|
}
|
|
521
|
+
await refreshAutomations();
|
|
447
522
|
};
|
|
448
523
|
const onToggleTaskCompleted = async (taskId, currentlyCompleted) => {
|
|
449
|
-
await updateWorkbenchTask(taskId, {
|
|
524
|
+
await updateWorkbenchTask(taskId, {
|
|
525
|
+
isCompleted: !currentlyCompleted,
|
|
526
|
+
});
|
|
450
527
|
};
|
|
451
|
-
// ── Resolved selection ──────────────────────────────────────────
|
|
452
528
|
const resolvedSelectedItem = useMemo(() => {
|
|
453
529
|
if (editorOpen || editingId || editingTaskId)
|
|
454
530
|
return null;
|
|
@@ -456,40 +532,36 @@ function useAutomationsViewController() {
|
|
|
456
532
|
return allItems.find((item) => item.id === selectedItemId) ?? null;
|
|
457
533
|
}
|
|
458
534
|
return allItems[0] ?? null;
|
|
459
|
-
}, [
|
|
535
|
+
}, [allItems, editingId, editingTaskId, editorOpen, selectedItemId]);
|
|
460
536
|
const modalTitle = editorMode === "trigger"
|
|
461
537
|
? editingId
|
|
462
538
|
? t("heartbeatsview.editTitle", {
|
|
463
539
|
name: form.displayName.trim() || "Task",
|
|
464
540
|
defaultValue: "Edit {{name}}",
|
|
465
541
|
})
|
|
466
|
-
: "New
|
|
542
|
+
: "New Schedule"
|
|
467
543
|
: editingTaskId
|
|
468
|
-
? "Edit
|
|
469
|
-
: "New
|
|
544
|
+
? "Edit Coordinator"
|
|
545
|
+
: "New Coordinator";
|
|
470
546
|
const editorEnabled = editingId != null
|
|
471
547
|
? (triggers.find((trigger) => trigger.id === editingId)?.enabled ??
|
|
472
548
|
form.enabled)
|
|
473
549
|
: form.enabled;
|
|
474
550
|
const hasItems = allItems.length > 0;
|
|
475
|
-
const isLoading = triggersLoading ||
|
|
476
|
-
const combinedError = triggerError || taskError;
|
|
551
|
+
const isLoading = triggersLoading || automationsLoading;
|
|
552
|
+
const combinedError = automationsError || triggerError || taskError;
|
|
477
553
|
const showFirstRunEmptyState = !isLoading && !combinedError && !hasItems;
|
|
478
554
|
const showDetailPane = Boolean(editorOpen || editingId || editingTaskId || resolvedSelectedItem);
|
|
479
555
|
return {
|
|
480
|
-
// Filter
|
|
481
556
|
filter,
|
|
482
557
|
setFilter,
|
|
483
|
-
// Items
|
|
484
558
|
allItems,
|
|
485
559
|
filteredItems,
|
|
486
|
-
// Selection
|
|
487
560
|
selectedItemId,
|
|
488
561
|
selectedItemKind,
|
|
489
562
|
setSelectedItemId,
|
|
490
563
|
setSelectedItemKind,
|
|
491
564
|
resolvedSelectedItem,
|
|
492
|
-
// Trigger editor
|
|
493
565
|
form,
|
|
494
566
|
setForm,
|
|
495
567
|
setField,
|
|
@@ -498,7 +570,6 @@ function useAutomationsViewController() {
|
|
|
498
570
|
editorOpen,
|
|
499
571
|
setEditorOpen,
|
|
500
572
|
editorMode,
|
|
501
|
-
setEditorMode,
|
|
502
573
|
formError,
|
|
503
574
|
setFormError,
|
|
504
575
|
editorEnabled,
|
|
@@ -506,7 +577,6 @@ function useAutomationsViewController() {
|
|
|
506
577
|
templateNotice,
|
|
507
578
|
setTemplateNotice,
|
|
508
579
|
userTemplates,
|
|
509
|
-
// Task editor
|
|
510
580
|
taskFormName,
|
|
511
581
|
setTaskFormName,
|
|
512
582
|
taskFormDescription,
|
|
@@ -514,7 +584,6 @@ function useAutomationsViewController() {
|
|
|
514
584
|
editingTaskId,
|
|
515
585
|
setEditingTaskId,
|
|
516
586
|
taskSaving,
|
|
517
|
-
// Actions
|
|
518
587
|
closeEditor,
|
|
519
588
|
openCreateTrigger,
|
|
520
589
|
openCreateTask,
|
|
@@ -530,13 +599,17 @@ function useAutomationsViewController() {
|
|
|
530
599
|
saveFormAsTemplate,
|
|
531
600
|
deleteUserTemplate,
|
|
532
601
|
loadTriggerRuns,
|
|
533
|
-
|
|
602
|
+
refreshAutomations,
|
|
603
|
+
automationNodes,
|
|
604
|
+
automationsLoading,
|
|
605
|
+
automationsLoaded,
|
|
606
|
+
automationsError,
|
|
607
|
+
n8nStatus,
|
|
608
|
+
workflowFetchError,
|
|
534
609
|
triggers,
|
|
535
|
-
workbenchTasks,
|
|
536
610
|
triggerRunsById,
|
|
537
611
|
triggersSaving,
|
|
538
612
|
triggersLoading,
|
|
539
|
-
tasksLoading,
|
|
540
613
|
triggerError,
|
|
541
614
|
taskError,
|
|
542
615
|
hasItems,
|
|
@@ -544,7 +617,6 @@ function useAutomationsViewController() {
|
|
|
544
617
|
combinedError,
|
|
545
618
|
showFirstRunEmptyState,
|
|
546
619
|
showDetailPane,
|
|
547
|
-
// I18n
|
|
548
620
|
t,
|
|
549
621
|
uiLanguage,
|
|
550
622
|
};
|
|
@@ -557,39 +629,104 @@ function useAutomationsViewContext() {
|
|
|
557
629
|
}
|
|
558
630
|
return context;
|
|
559
631
|
}
|
|
560
|
-
// ── Filter tabs ───────────────────────────────────────────────────
|
|
561
632
|
function FilterTabs() {
|
|
562
633
|
const { filter, setFilter, allItems } = useAutomationsViewContext();
|
|
563
|
-
const triggerCount = allItems.filter((i) => i.kind === "trigger").length;
|
|
564
|
-
const _userTaskCount = allItems.filter((i) => i.kind === "task" && !i.system).length;
|
|
565
|
-
const _systemCount = allItems.filter((i) => i.kind === "task" && i.system).length + triggerCount;
|
|
566
634
|
const filters = [
|
|
567
|
-
{ key: "all", label: "All" },
|
|
568
|
-
{
|
|
569
|
-
|
|
570
|
-
|
|
635
|
+
{ key: "all", label: "All", count: allItems.length },
|
|
636
|
+
{
|
|
637
|
+
key: "coordinator",
|
|
638
|
+
label: "Coordinator",
|
|
639
|
+
count: allItems.filter((item) => item.type === "coordinator_text").length,
|
|
640
|
+
},
|
|
641
|
+
{
|
|
642
|
+
key: "workflows",
|
|
643
|
+
label: "Workflows",
|
|
644
|
+
count: allItems.filter((item) => item.type === "n8n_workflow").length,
|
|
645
|
+
},
|
|
646
|
+
{
|
|
647
|
+
key: "scheduled",
|
|
648
|
+
label: "Scheduled",
|
|
649
|
+
count: allItems.filter((item) => item.schedules.length > 0).length,
|
|
650
|
+
},
|
|
571
651
|
];
|
|
572
|
-
return (_jsx("div", { className: "flex gap-1 px-1 pb-2", children: filters.map(({ key, label }) => (
|
|
652
|
+
return (_jsx("div", { className: "flex gap-1 px-1 pb-2", children: filters.map(({ key, label, count }) => (_jsxs("button", { type: "button", onClick: () => setFilter(key), className: `rounded-lg px-2.5 py-1 text-xs font-medium transition-colors ${filter === key
|
|
573
653
|
? "bg-accent/15 text-accent"
|
|
574
|
-
: "text-muted hover:
|
|
654
|
+
: "text-muted hover:bg-bg/50 hover:text-txt"}`, children: [label, " ", _jsx("span", { className: filter === key ? "text-accent/80" : "text-muted/70", children: count })] }, key))) }));
|
|
575
655
|
}
|
|
576
|
-
// ── Task form (inline in editor) ──────────────────────────────────
|
|
577
656
|
function TaskForm() {
|
|
578
657
|
const { taskFormName, setTaskFormName, taskFormDescription, setTaskFormDescription, editingTaskId, formError, taskSaving, onSubmitTask, onDeleteTask, closeEditor, modalTitle, t, } = useAutomationsViewContext();
|
|
579
|
-
return (_jsxs(PagePanel, { variant: "padded", className: "space-y-5", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("h3", { className: "text-lg font-semibold text-txt", children: modalTitle }), _jsx(Button, { variant: "ghost", size: "sm", onClick: closeEditor, children: t("common.cancel") })] }), formError && (_jsx("div", { className: "rounded-lg border border-danger/20 bg-danger/10 p-3 text-sm text-danger", children: formError })), _jsxs("div", { className: "space-y-3", children: [_jsxs("div", { children: [_jsx(FieldLabel, { children: "Name" }), _jsx(Input, { value: taskFormName, onChange: (
|
|
580
|
-
|
|
581
|
-
|
|
658
|
+
return (_jsxs(PagePanel, { variant: "padded", className: "space-y-5", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("h3", { className: "text-lg font-semibold text-txt", children: modalTitle }), _jsx(Button, { variant: "ghost", size: "sm", onClick: closeEditor, children: t("common.cancel") })] }), formError && (_jsx("div", { className: "rounded-lg border border-danger/20 bg-danger/10 p-3 text-sm text-danger", children: formError })), _jsxs("div", { className: "space-y-3", children: [_jsxs("div", { children: [_jsx(FieldLabel, { children: "Name" }), _jsx(Input, { value: taskFormName, onChange: (event) => setTaskFormName(event.target.value), placeholder: "Coordinator automation name...", autoFocus: true })] }), _jsxs("div", { children: [_jsx(FieldLabel, { children: "Description" }), _jsx(Textarea, { value: taskFormDescription, onChange: (event) => setTaskFormDescription(event.target.value), placeholder: "What should the coordinator do...", rows: 4 })] })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Button, { variant: "default", size: "sm", disabled: taskSaving || !taskFormName.trim(), onClick: () => void onSubmitTask(), children: editingTaskId ? "Save Coordinator" : "Create Coordinator" }), editingTaskId && (_jsx(Button, { variant: "outline", size: "sm", className: "border-danger/30 text-danger hover:bg-danger/10", onClick: () => void onDeleteTask(editingTaskId), children: t("triggersview.Delete") }))] })] }));
|
|
659
|
+
}
|
|
660
|
+
function WorkflowRuntimeNotice({ status, workflowFetchError, busy, onRefresh, onStartLocal, }) {
|
|
661
|
+
if (!status && !workflowFetchError) {
|
|
662
|
+
return null;
|
|
663
|
+
}
|
|
664
|
+
if (status?.mode === "disabled") {
|
|
665
|
+
return (_jsx(PagePanel, { variant: "padded", className: "mb-4 border border-border/30 bg-bg/30", children: _jsxs("div", { className: "flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between", children: [_jsxs("div", { className: "space-y-1", children: [_jsx("div", { className: "text-sm font-semibold text-txt", children: "Workflow execution needs n8n." }), _jsx("p", { className: "text-sm text-muted", children: "Coordinator automations stay usable without n8n. Workflow automations become deployable once Eliza Cloud or local n8n is available." })] }), status.platform !== "mobile" && (_jsx(Button, { variant: "outline", size: "sm", disabled: busy, onClick: onStartLocal, children: "Enable Local n8n" }))] }) }));
|
|
666
|
+
}
|
|
667
|
+
if (workflowFetchError) {
|
|
668
|
+
return (_jsx(PagePanel, { variant: "padded", className: "mb-4 border border-danger/20 bg-danger/5", children: _jsxs("div", { className: "flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between", children: [_jsxs("div", { className: "space-y-1", children: [_jsx("div", { className: "text-sm font-semibold text-danger", children: "Workflow backend unavailable" }), _jsx("p", { className: "text-sm text-danger/90", children: workflowFetchError })] }), _jsxs("div", { className: "flex items-center gap-2", children: [status?.mode === "local" && status.status !== "ready" && (_jsx(Button, { variant: "outline", size: "sm", disabled: busy, onClick: onStartLocal, children: "Start Local n8n" })), _jsx(Button, { variant: "outline", size: "sm", disabled: busy, onClick: onRefresh, children: "Refresh" })] })] }) }));
|
|
669
|
+
}
|
|
670
|
+
if (status?.mode === "local" && status.status !== "ready") {
|
|
671
|
+
return (_jsx(PagePanel, { variant: "padded", className: "mb-4 border border-warning/20 bg-warning/5", children: _jsxs("div", { className: "flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between", children: [_jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "text-sm font-semibold text-warning", children: ["Local n8n is ", status.status, "."] }), _jsx("p", { className: "text-sm text-muted", children: "Draft rooms still work. Workflow deploy, activate, and delete operations resume when local n8n is ready." })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Button, { variant: "outline", size: "sm", disabled: busy, onClick: onStartLocal, children: "Start Local n8n" }), _jsx(Button, { variant: "outline", size: "sm", disabled: busy, onClick: onRefresh, children: "Refresh" })] })] }) }));
|
|
672
|
+
}
|
|
673
|
+
if (status?.mode === "cloud" && status.cloudHealth === "degraded") {
|
|
674
|
+
return (_jsx(PagePanel, { variant: "padded", className: "mb-4 border border-warning/20 bg-warning/5", children: _jsxs("div", { className: "flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between", children: [_jsxs("div", { className: "space-y-1", children: [_jsx("div", { className: "text-sm font-semibold text-warning", children: "Eliza Cloud workflow gateway is degraded." }), _jsx("p", { className: "text-sm text-muted", children: "Chat rooms remain usable while workflow execution and sync may be delayed." })] }), _jsx(Button, { variant: "outline", size: "sm", disabled: busy, onClick: onRefresh, children: "Refresh" })] }) }));
|
|
675
|
+
}
|
|
676
|
+
return null;
|
|
582
677
|
}
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
678
|
+
function AutomationNodePalette({ nodes, title, subtitle, }) {
|
|
679
|
+
const groupedNodes = useMemo(() => NODE_CLASS_ORDER.map((className) => ({
|
|
680
|
+
className,
|
|
681
|
+
nodes: nodes.filter((node) => node.class === className),
|
|
682
|
+
})).filter((group) => group.nodes.length > 0), [nodes]);
|
|
683
|
+
return (_jsxs(PagePanel, { variant: "padded", className: "space-y-4", children: [_jsxs("div", { className: "space-y-1", children: [_jsx("div", { className: "text-xs font-semibold uppercase tracking-wider text-muted", children: title }), _jsx("p", { className: "text-sm text-muted", children: subtitle })] }), _jsxs("div", { className: "flex flex-wrap gap-2 text-xs", children: [_jsxs("span", { className: "rounded-full bg-bg/40 px-2.5 py-1 text-muted", children: [nodes.length, " total"] }), _jsxs("span", { className: "rounded-full bg-ok/10 px-2.5 py-1 text-ok", children: [nodes.filter((node) => node.availability === "enabled").length, " enabled"] }), _jsxs("span", { className: "rounded-full bg-warning/10 px-2.5 py-1 text-warning", children: [nodes.filter((node) => node.availability === "disabled").length, " setup required"] })] }), _jsx("div", { className: "space-y-4", children: groupedNodes.map((group) => (_jsxs("div", { className: "space-y-2", children: [_jsx("div", { className: "text-xs font-semibold uppercase tracking-wider text-muted", children: getNodeClassLabel(group.className) }), _jsx("div", { className: "grid gap-3 xl:grid-cols-2", children: group.nodes.map((node) => (_jsx("div", { className: `rounded-xl border px-4 py-3 ${node.availability === "enabled"
|
|
684
|
+
? "border-border/30 bg-bg/25"
|
|
685
|
+
: "border-warning/20 bg-warning/5"}`, children: _jsxs("div", { className: "flex items-start gap-3", children: [_jsx("div", { className: `mt-0.5 rounded-lg p-2 ${node.availability === "enabled"
|
|
686
|
+
? "bg-accent/10 text-accent"
|
|
687
|
+
: "bg-warning/10 text-warning"}`, children: getNodeIcon(node) }), _jsxs("div", { className: "min-w-0 flex-1 space-y-1", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx("div", { className: "text-sm font-semibold text-txt", children: node.label }), _jsx(StatusBadge, { label: node.availability === "enabled" ? "Ready" : "Setup", variant: node.availability === "enabled" ? "success" : "warning", withDot: true }), node.ownerScoped && (_jsx("span", { className: "rounded-full bg-bg/40 px-2 py-0.5 text-[11px] text-muted", children: "Owner scoped" }))] }), _jsx("p", { className: "text-sm text-muted", children: node.description }), node.disabledReason && (_jsx("p", { className: "text-xs text-warning", children: node.disabledReason }))] })] }) }, node.id))) })] }, group.className))) })] }));
|
|
688
|
+
}
|
|
689
|
+
function TaskAutomationDetailPane({ automation, nodes, onAutomationMutated, onPromoteToWorkflow, }) {
|
|
690
|
+
const { activeConversationId, conversations } = useApp();
|
|
691
|
+
const { openEditTask, onDeleteTask, onToggleTaskCompleted, t } = useAutomationsViewContext();
|
|
692
|
+
const task = automation.task;
|
|
693
|
+
const [chatCollapsed, setChatCollapsed] = useState(false);
|
|
694
|
+
const composerRef = useRef(null);
|
|
695
|
+
if (!task) {
|
|
696
|
+
return null;
|
|
697
|
+
}
|
|
698
|
+
const bridgeConversationId = getAutomationBridgeIdForItem(automation, activeConversationId, conversations);
|
|
699
|
+
const metadata = buildCoordinatorConversationMetadata(task.id, bridgeConversationId);
|
|
700
|
+
return (_jsxs("div", { className: "space-y-6", children: [_jsxs("div", { className: "flex flex-col gap-5 lg:flex-row lg:items-start lg:justify-between", children: [_jsxs("div", { className: "max-w-3xl space-y-3", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx(FieldLabel, { variant: "kicker", children: automation.system ? (_jsxs(_Fragment, { children: [_jsx(Settings, { className: "mr-1.5 inline h-3.5 w-3.5" }), "System Automation"] })) : (_jsxs(_Fragment, { children: [_jsx(SquareTerminal, { className: "mr-1.5 inline h-3.5 w-3.5" }), "Coordinator Automation"] })) }), _jsx(StatusBadge, { label: automation.system
|
|
701
|
+
? "System"
|
|
702
|
+
: task.isCompleted
|
|
703
|
+
? "Completed"
|
|
704
|
+
: "Active", variant: automation.system
|
|
705
|
+
? "muted"
|
|
706
|
+
: task.isCompleted
|
|
707
|
+
? "muted"
|
|
708
|
+
: "success", withDot: true })] }), _jsx("h2", { className: "text-2xl font-semibold text-txt sm:text-[2rem]", children: automation.title }), automation.description && (_jsx("p", { className: "text-sm leading-relaxed text-muted", children: automation.description })), task.tags.length > 0 && (_jsx("div", { className: "flex flex-wrap gap-1.5", children: task.tags.map((tag) => (_jsx("span", { className: "rounded-md bg-bg/50 px-2 py-0.5 text-xs text-muted", children: tag }, tag))) }))] }), !automation.system && (_jsxs("div", { className: "flex shrink-0 flex-wrap items-center gap-2 lg:justify-end", children: [_jsx(Button, { variant: "outline", size: "sm", className: `h-8 px-3 text-xs ${task.isCompleted
|
|
709
|
+
? "border-ok/30 text-ok hover:bg-ok/10"
|
|
710
|
+
: "border-accent/30 text-accent hover:bg-accent/10"}`, onClick: () => void onToggleTaskCompleted(task.id, task.isCompleted), children: task.isCompleted ? "Reopen" : "Complete" }), _jsxs(Button, { variant: "outline", size: "sm", className: "h-8 px-3 text-xs", onClick: () => void onPromoteToWorkflow(automation), children: [_jsx(GitBranch, { className: "mr-1.5 h-3.5 w-3.5" }), "Compile to Workflow"] }), _jsx(Button, { variant: "outline", size: "sm", className: "h-8 px-3 text-xs", onClick: () => openEditTask(task), children: t("triggersview.Edit") }), _jsx(Button, { variant: "outline", size: "sm", className: "h-8 px-3 text-xs border-danger/30 text-danger hover:bg-danger/10", onClick: () => void onDeleteTask(task.id), children: t("triggersview.Delete") })] }))] }), !automation.system && (_jsx(AutomationRoomChatPane, { assistantLabel: t("automations.chat.assistantLabel"), collapsed: chatCollapsed, composerRef: composerRef, metadata: metadata, onAutomationMutated: onAutomationMutated, onToggleCollapse: () => setChatCollapsed((value) => !value), placeholder: "Ask the coordinator to plan or execute this automation.", systemAddendum: COORDINATOR_SYSTEM_ADDENDUM, title: automation.title })), _jsx(AutomationNodePalette, { nodes: nodes, title: "Available Automation Nodes", subtitle: "These are the runtime capabilities the coordinator can reference while building or converting this automation." })] }));
|
|
711
|
+
}
|
|
712
|
+
function TriggerAutomationDetailPane({ automation, nodes, onAutomationMutated, onPromoteToWorkflow, }) {
|
|
713
|
+
const { activeConversationId, conversations } = useApp();
|
|
714
|
+
const { t, uiLanguage, openEditTrigger, onRunSelectedTrigger, onToggleTriggerEnabled, loadTriggerRuns, triggerRunsById, setForm, setEditorOpen, setEditingId, setSelectedItemId, setSelectedItemKind, } = useAutomationsViewContext();
|
|
715
|
+
const trigger = automation.trigger;
|
|
716
|
+
const [chatCollapsed, setChatCollapsed] = useState(false);
|
|
717
|
+
const composerRef = useRef(null);
|
|
718
|
+
if (!trigger) {
|
|
719
|
+
return null;
|
|
720
|
+
}
|
|
721
|
+
const bridgeConversationId = getAutomationBridgeIdForItem(automation, activeConversationId, conversations);
|
|
722
|
+
const metadata = buildCoordinatorTriggerConversationMetadata(trigger.id, bridgeConversationId);
|
|
586
723
|
const selectedRuns = triggerRunsById[trigger.id] ?? [];
|
|
587
724
|
const hasLoadedRuns = Object.hasOwn(triggerRunsById, trigger.id);
|
|
588
725
|
useEffect(() => {
|
|
589
726
|
if (!hasLoadedRuns) {
|
|
590
727
|
void loadTriggerRuns(trigger.id);
|
|
591
728
|
}
|
|
592
|
-
}, [
|
|
729
|
+
}, [hasLoadedRuns, loadTriggerRuns, trigger.id]);
|
|
593
730
|
const { failureCount, successCount } = selectedRuns.reduce((counts, run) => {
|
|
594
731
|
const tone = toneForLastStatus(run.status);
|
|
595
732
|
if (tone === "success")
|
|
@@ -598,13 +735,9 @@ function TriggerDetailPane({ trigger }) {
|
|
|
598
735
|
counts.failureCount += 1;
|
|
599
736
|
return counts;
|
|
600
737
|
}, { failureCount: 0, successCount: 0 });
|
|
601
|
-
return (_jsxs("div", { className: "
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
name: trigger.workflowName ?? trigger.workflowId ?? "",
|
|
605
|
-
}) })] }), _jsx("button", { type: "button", className: "text-xs font-medium text-accent underline-offset-2 hover:underline", onClick: () => setFilter("workflows"), children: t("automations.openInWorkflowsTab") })] })) : (_jsx("p", { className: "text-sm leading-relaxed text-muted sm:text-sm", children: trigger.instructions }))] }), _jsxs("div", { className: "flex shrink-0 flex-wrap items-center gap-2 lg:justify-end", children: [_jsx(Button, { variant: "outline", size: "sm", className: `h-8 px-3 text-xs ${trigger.enabled ? "border-warning/30 text-warning hover:bg-warning/10" : "border-ok/30 text-ok hover:bg-ok/10"}`, onClick: () => void onToggleTriggerEnabled(trigger.id, trigger.enabled), children: trigger.enabled
|
|
606
|
-
? t("heartbeatsview.pause")
|
|
607
|
-
: t("heartbeatsview.resume") }), _jsx(Button, { variant: "outline", size: "sm", className: "h-8 px-3 text-xs", onClick: () => openEditTrigger(trigger), children: t("triggersview.Edit") }), _jsx(Button, { variant: "outline", size: "sm", className: "h-8 px-3 text-xs", onClick: () => {
|
|
738
|
+
return (_jsxs("div", { className: "space-y-6", children: [_jsxs("div", { className: "flex flex-col gap-5 lg:flex-row lg:items-start lg:justify-between", children: [_jsxs("div", { className: "max-w-3xl space-y-3", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsxs(FieldLabel, { variant: "kicker", children: [_jsx(Clock3, { className: "mr-1.5 inline h-3.5 w-3.5" }), "Scheduled Coordinator Automation"] }), _jsx(StatusBadge, { label: trigger.enabled ? "Active" : "Paused", variant: trigger.enabled ? "success" : "muted", withDot: true })] }), _jsx("h2", { className: "text-2xl font-semibold text-txt sm:text-[2rem]", children: automation.title }), _jsx("p", { className: "text-sm leading-relaxed text-muted", children: automation.description })] }), _jsxs("div", { className: "flex shrink-0 flex-wrap items-center gap-2 lg:justify-end", children: [_jsx(Button, { variant: "outline", size: "sm", className: `h-8 px-3 text-xs ${trigger.enabled
|
|
739
|
+
? "border-warning/30 text-warning hover:bg-warning/10"
|
|
740
|
+
: "border-ok/30 text-ok hover:bg-ok/10"}`, onClick: () => void onToggleTriggerEnabled(trigger.id, trigger.enabled), children: trigger.enabled ? "Pause" : "Resume" }), _jsxs(Button, { variant: "outline", size: "sm", className: "h-8 px-3 text-xs", onClick: () => void onPromoteToWorkflow(automation), children: [_jsx(GitBranch, { className: "mr-1.5 h-3.5 w-3.5" }), "Compile to Workflow"] }), _jsx(Button, { variant: "outline", size: "sm", className: "h-8 px-3 text-xs", onClick: () => openEditTrigger(trigger), children: t("triggersview.Edit") }), _jsx(Button, { variant: "outline", size: "sm", className: "h-8 px-3 text-xs", onClick: () => {
|
|
608
741
|
setForm({
|
|
609
742
|
...formFromTrigger(trigger),
|
|
610
743
|
displayName: `${trigger.displayName} (copy)`,
|
|
@@ -613,108 +746,275 @@ function TriggerDetailPane({ trigger }) {
|
|
|
613
746
|
setEditingId(null);
|
|
614
747
|
setSelectedItemId(null);
|
|
615
748
|
setSelectedItemKind(null);
|
|
616
|
-
}, children: t("heartbeatsview.duplicate") }), _jsx(Button, { variant: "outline", size: "sm", className: "h-8 px-3 text-xs", onClick: () => void onRunSelectedTrigger(trigger.id), children: t("triggersview.RunNow") })] })] }), _jsxs("dl", { className: "
|
|
617
|
-
fallback:
|
|
749
|
+
}, children: t("heartbeatsview.duplicate") }), _jsx(Button, { variant: "outline", size: "sm", className: "h-8 px-3 text-xs", onClick: () => void onRunSelectedTrigger(trigger.id), children: t("triggersview.RunNow") })] })] }), _jsxs("dl", { className: "grid gap-4 text-sm sm:grid-cols-2 xl:grid-cols-4", children: [_jsxs(PagePanel.SummaryCard, { className: "px-4 py-4", children: [_jsx("dt", { className: "text-xs-tight font-semibold uppercase tracking-wider text-muted", children: "Schedule" }), _jsx("dd", { className: "mt-1 font-medium text-txt", children: scheduleLabel(trigger, t, uiLanguage) })] }), _jsxs(PagePanel.SummaryCard, { className: "px-4 py-4", children: [_jsx("dt", { className: "text-xs-tight font-semibold uppercase tracking-wider text-muted", children: "Last Run" }), _jsx("dd", { className: "mt-1 font-medium text-txt", children: formatDateTime(trigger.lastRunAtIso, {
|
|
750
|
+
fallback: "Not yet run",
|
|
618
751
|
locale: uiLanguage,
|
|
619
|
-
}) })] }), _jsxs(PagePanel.SummaryCard, { className: "px-4 py-4", children: [_jsx("dt", { className: "text-xs-tight font-semibold uppercase tracking-wider text-muted", children:
|
|
620
|
-
fallback:
|
|
752
|
+
}) })] }), _jsxs(PagePanel.SummaryCard, { className: "px-4 py-4", children: [_jsx("dt", { className: "text-xs-tight font-semibold uppercase tracking-wider text-muted", children: "Next Run" }), _jsx("dd", { className: "mt-1 font-medium text-txt", children: formatDateTime(trigger.nextRunAtMs, {
|
|
753
|
+
fallback: "Not scheduled",
|
|
621
754
|
locale: uiLanguage,
|
|
622
|
-
}) })] }),
|
|
623
|
-
count: selectedRuns.length,
|
|
624
|
-
}) }), successCount > 0 && (_jsxs("span", { className: "text-ok", children: [successCount, " \u2713"] })), failureCount > 0 && (_jsxs("span", { className: "text-danger", children: [failureCount, " \u2717"] }))] })] }))] }), _jsxs(PagePanel, { variant: "padded", className: "space-y-4", children: [_jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsx("div", { className: "text-xs font-semibold uppercase tracking-wider text-muted", children: t("triggersview.RunHistory") }), _jsx(Button, { variant: "outline", size: "sm", className: "h-7 px-3 text-xs-tight", onClick: () => void loadTriggerRuns(trigger.id), children: t("common.refresh") })] }), !hasLoadedRuns ? (_jsxs("div", { className: "flex items-center gap-2 py-6 text-sm text-muted/70", children: [_jsx("div", { className: "h-4 w-4 animate-spin rounded-full border-2 border-muted/30 border-t-muted/80" }), t("databaseview.Loading")] })) : selectedRuns.length === 0 ? (_jsx("div", { className: "py-8 text-center text-sm text-muted/60", children: t("heartbeatsview.noRunsYetMessage") })) : (_jsx("div", { className: "space-y-2", children: selectedRuns.map((run) => (_jsxs("div", { className: "rounded-lg border border-border/30 bg-bg/30 px-4 py-3", children: [_jsxs("div", { className: "mb-1 flex items-center justify-between gap-2", children: [_jsx(StatusBadge, { label: localizedExecutionStatus(run.status, t), variant: toneForLastStatus(run.status) }), _jsx("span", { className: "font-mono text-xs-tight text-muted/70", children: formatDateTime(run.startedAt, { locale: uiLanguage }) })] }), _jsxs("div", { className: "text-xs-tight text-muted/80", children: [formatDurationMs(run.latencyMs, { t }), " \u00B7", " ", _jsx("span", { className: "rounded bg-bg/40 px-1 py-0.5 font-mono text-muted/60", children: run.source })] }), run.error && (_jsx("div", { className: "mt-2 whitespace-pre-wrap rounded-lg border border-danger/20 bg-danger/10 p-2 font-mono text-xs text-danger/90", children: run.error }))] }, run.triggerRunId))) }))] })] }));
|
|
755
|
+
}) })] }), _jsxs(PagePanel.SummaryCard, { className: "px-4 py-4", children: [_jsx("dt", { className: "text-xs-tight font-semibold uppercase tracking-wider text-muted", children: "Runs" }), _jsxs("dd", { className: "mt-1 flex items-center gap-2 text-sm font-medium", children: [_jsx("span", { className: "text-txt", children: selectedRuns.length }), successCount > 0 && _jsxs("span", { className: "text-ok", children: [successCount, " \u2713"] }), failureCount > 0 && (_jsxs("span", { className: "text-danger", children: [failureCount, " \u2717"] }))] })] })] }), _jsxs(PagePanel, { variant: "padded", className: "space-y-4", children: [_jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsx("div", { className: "text-xs font-semibold uppercase tracking-wider text-muted", children: "Run History" }), _jsx(Button, { variant: "outline", size: "sm", className: "h-7 px-3 text-xs-tight", onClick: () => void loadTriggerRuns(trigger.id), children: t("common.refresh") })] }), !hasLoadedRuns ? (_jsxs("div", { className: "flex items-center gap-2 py-6 text-sm text-muted/70", children: [_jsx("div", { className: "h-4 w-4 animate-spin rounded-full border-2 border-muted/30 border-t-muted/80" }), t("databaseview.Loading")] })) : selectedRuns.length === 0 ? (_jsx("div", { className: "py-8 text-center text-sm text-muted/60", children: t("heartbeatsview.noRunsYetMessage") })) : (_jsx("div", { className: "space-y-2", children: selectedRuns.map((run) => (_jsxs("div", { className: "rounded-lg border border-border/30 bg-bg/30 px-4 py-3", children: [_jsxs("div", { className: "mb-1 flex items-center justify-between gap-2", children: [_jsx(StatusBadge, { label: localizedExecutionStatus(run.status, t), variant: toneForLastStatus(run.status) }), _jsx("span", { className: "font-mono text-xs-tight text-muted/70", children: formatDateTime(run.startedAt, { locale: uiLanguage }) })] }), _jsxs("div", { className: "text-xs-tight text-muted/80", children: [formatDurationMs(run.latencyMs, { t }), " \u00B7", " ", _jsx("span", { className: "rounded bg-bg/40 px-1 py-0.5 font-mono text-muted/60", children: run.source })] }), run.error && (_jsx("div", { className: "mt-2 whitespace-pre-wrap rounded-lg border border-danger/20 bg-danger/10 p-2 font-mono text-xs text-danger/90", children: run.error }))] }, run.triggerRunId))) }))] }), _jsx(AutomationRoomChatPane, { assistantLabel: t("automations.chat.assistantLabel"), collapsed: chatCollapsed, composerRef: composerRef, metadata: metadata, onAutomationMutated: onAutomationMutated, onToggleCollapse: () => setChatCollapsed((value) => !value), placeholder: "Ask the coordinator to refine or convert this scheduled automation.", systemAddendum: COORDINATOR_SYSTEM_ADDENDUM, title: automation.title }), _jsx(AutomationNodePalette, { nodes: nodes, title: "Available Automation Nodes", subtitle: "These nodes stay visible even when setup is missing so you can design the workflow shape before connecting services." })] }));
|
|
625
756
|
}
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
const
|
|
629
|
-
|
|
757
|
+
function WorkflowAutomationDetailPane({ automation, nodes, n8nStatus, workflowFetchError, workflowBusyId, workflowOpsBusy, onConversationResolved, onDeleteWorkflow, onRefreshWorkflows, onStartLocalN8n, onToggleWorkflowActive, onWorkflowMutated, }) {
|
|
758
|
+
const { activeConversationId, conversations, t, uiLanguage } = useApp();
|
|
759
|
+
const [chatCollapsed, setChatCollapsed] = useState(false);
|
|
760
|
+
const composerRef = useRef(null);
|
|
761
|
+
const bridgeConversationId = getAutomationBridgeIdForItem(automation, activeConversationId, conversations);
|
|
762
|
+
const metadata = automation.workflowId && !automation.isDraft
|
|
763
|
+
? buildWorkflowConversationMetadata(automation.workflowId, automation.title, bridgeConversationId)
|
|
764
|
+
: buildWorkflowDraftConversationMetadata(automation.draftId ?? createWorkflowDraftId(), bridgeConversationId);
|
|
765
|
+
const nodeCount = getWorkflowNodeCount(automation);
|
|
766
|
+
const busy = workflowOpsBusy || (automation.workflowId != null && workflowBusyId === automation.workflowId);
|
|
767
|
+
useEffect(() => {
|
|
768
|
+
setChatCollapsed(false);
|
|
769
|
+
}, [automation.id]);
|
|
770
|
+
return (_jsxs("div", { className: "space-y-6", children: [_jsx(WorkflowRuntimeNotice, { status: n8nStatus, workflowFetchError: workflowFetchError, busy: busy, onRefresh: () => void onRefreshWorkflows(), onStartLocal: () => void onStartLocalN8n() }), _jsx(AutomationRoomChatPane, { assistantLabel: t("automations.chat.assistantLabel"), collapsed: chatCollapsed, composerRef: composerRef, metadata: metadata, onConversationResolved: onConversationResolved, onAutomationMutated: onWorkflowMutated, onToggleCollapse: () => setChatCollapsed((value) => !value), placeholder: automation.isDraft
|
|
771
|
+
? "Describe the workflow you want to build."
|
|
772
|
+
: "Refine or debug this workflow with the automation agent.", systemAddendum: WORKFLOW_SYSTEM_ADDENDUM, title: automation.title || WORKFLOW_DRAFT_TITLE }), _jsxs(PagePanel, { variant: "padded", className: "space-y-5", children: [_jsxs("div", { className: "flex flex-col gap-5 lg:flex-row lg:items-start lg:justify-between", children: [_jsxs("div", { className: "max-w-3xl space-y-3", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsxs(FieldLabel, { variant: "kicker", children: [_jsx(Workflow, { className: "mr-1.5 inline h-3.5 w-3.5" }), automation.isDraft ? "Workflow Draft" : "Workflow Automation"] }), _jsx(StatusBadge, { label: automation.isDraft
|
|
773
|
+
? "Draft"
|
|
774
|
+
: automation.enabled
|
|
775
|
+
? "Active"
|
|
776
|
+
: "Paused", variant: automation.isDraft
|
|
777
|
+
? "warning"
|
|
778
|
+
: automation.enabled
|
|
779
|
+
? "success"
|
|
780
|
+
: "muted", withDot: true }), automation.hasBackingWorkflow ? (_jsx("span", { className: "rounded-full bg-ok/10 px-2 py-0.5 text-[11px] text-ok", children: "Backed by n8n" })) : (_jsx("span", { className: "rounded-full bg-warning/10 px-2 py-0.5 text-[11px] text-warning", children: "Room only" }))] }), _jsx("h2", { className: "text-2xl font-semibold text-txt sm:text-[2rem]", children: automation.title }), _jsx("p", { className: "text-sm leading-relaxed text-muted", children: automation.description ||
|
|
781
|
+
(automation.isDraft
|
|
782
|
+
? "Develop this workflow in chat, then have the agent create and deploy the backing n8n workflow."
|
|
783
|
+
: "Workflow automation.") })] }), automation.workflow && automation.workflowId && (_jsxs("div", { className: "flex shrink-0 flex-wrap items-center gap-2 lg:justify-end", children: [_jsx(Button, { variant: "outline", size: "sm", className: `h-8 px-3 text-xs ${automation.workflow.active
|
|
784
|
+
? "border-warning/30 text-warning hover:bg-warning/10"
|
|
785
|
+
: "border-ok/30 text-ok hover:bg-ok/10"}`, disabled: busy, onClick: () => void onToggleWorkflowActive(automation), children: automation.workflow.active ? "Deactivate" : "Activate" }), _jsx(Button, { variant: "outline", size: "sm", className: "h-8 px-3 text-xs border-danger/30 text-danger hover:bg-danger/10", disabled: busy, onClick: () => void onDeleteWorkflow(automation), children: "Delete Workflow" })] }))] }), _jsxs("dl", { className: "grid gap-4 text-sm sm:grid-cols-2 xl:grid-cols-4", children: [_jsxs(PagePanel.SummaryCard, { className: "px-4 py-4", children: [_jsx("dt", { className: "text-xs-tight font-semibold uppercase tracking-wider text-muted", children: "Workflow ID" }), _jsx("dd", { className: "mt-1 break-all font-mono text-xs text-txt", children: automation.workflowId ?? automation.draftId ?? "Pending" })] }), _jsxs(PagePanel.SummaryCard, { className: "px-4 py-4", children: [_jsx("dt", { className: "text-xs-tight font-semibold uppercase tracking-wider text-muted", children: "Nodes" }), _jsx("dd", { className: "mt-1 font-medium text-txt", children: nodeCount })] }), _jsxs(PagePanel.SummaryCard, { className: "px-4 py-4", children: [_jsx("dt", { className: "text-xs-tight font-semibold uppercase tracking-wider text-muted", children: "Attached Schedules" }), _jsx("dd", { className: "mt-1 font-medium text-txt", children: automation.schedules.length })] }), _jsxs(PagePanel.SummaryCard, { className: "px-4 py-4", children: [_jsx("dt", { className: "text-xs-tight font-semibold uppercase tracking-wider text-muted", children: "Updated" }), _jsx("dd", { className: "mt-1 font-medium text-txt", children: formatDateTime(automation.updatedAt, {
|
|
786
|
+
fallback: "Unknown",
|
|
787
|
+
locale: uiLanguage,
|
|
788
|
+
}) })] })] }), automation.schedules.length > 0 && (_jsxs("div", { className: "space-y-2", children: [_jsx("div", { className: "text-xs font-semibold uppercase tracking-wider text-muted", children: "Workflow Schedules" }), _jsx("div", { className: "space-y-2", children: automation.schedules.map((schedule) => (_jsxs("div", { className: "rounded-lg border border-border/30 bg-bg/20 px-4 py-3", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx("div", { className: "font-medium text-txt", children: schedule.displayName }), _jsx(StatusBadge, { label: schedule.enabled ? "Active" : "Paused", variant: schedule.enabled ? "success" : "muted", withDot: true })] }), _jsx("div", { className: "mt-1 text-sm text-muted", children: scheduleLabel(schedule, t, uiLanguage) })] }, schedule.id))) })] })), automation.workflow?.nodes && automation.workflow.nodes.length > 0 && (_jsxs("div", { className: "space-y-2", children: [_jsx("div", { className: "text-xs font-semibold uppercase tracking-wider text-muted", children: "Backing Workflow Graph" }), _jsx("div", { className: "space-y-2", children: automation.workflow.nodes.map((node) => (_jsxs("div", { className: "flex items-center justify-between rounded-lg border border-border/30 bg-bg/20 px-4 py-3 text-sm", children: [_jsx("span", { className: "font-medium text-txt", children: node.name ?? "Unnamed node" }), _jsx("span", { className: "font-mono text-xs text-muted", children: node.type?.split(".").pop() ?? "node" })] }, node.id ?? `${node.name}-${node.type}`))) })] }))] }), _jsx(AutomationNodePalette, { nodes: nodes, title: "Workflow Node Catalog", subtitle: "Runtime actions, providers, code-agent nodes, and owner-scoped LifeOps integrations are available here as workflow building blocks." })] }));
|
|
789
|
+
}
|
|
790
|
+
function AutomationSidebarItem({ item, selected, onClick, onDoubleClick, }) {
|
|
791
|
+
const { t, uiLanguage } = useAutomationsViewContext();
|
|
792
|
+
if (item.type === "n8n_workflow") {
|
|
793
|
+
const nodeCount = getWorkflowNodeCount(item);
|
|
794
|
+
return (_jsx(SidebarContent.Item, { onClick: onClick, onDoubleClick: onDoubleClick, active: selected, className: "h-auto", children: _jsxs("div", { className: "flex min-w-0 flex-col gap-1.5", children: [_jsxs("div", { className: "flex items-center justify-between gap-1", children: [_jsxs("div", { className: "flex items-center gap-1.5 truncate", children: [_jsx(Workflow, { className: "h-3 w-3 shrink-0 text-muted/60" }), _jsx("span", { className: "truncate text-sm font-semibold text-txt", children: item.title })] }), _jsx(StatusBadge, { label: item.isDraft ? "Draft" : item.enabled ? "Active" : "Paused", variant: item.isDraft ? "warning" : item.enabled ? "success" : "muted", withDot: true })] }), _jsxs("div", { className: "mt-0.5 flex items-center justify-between gap-2 text-xs-tight text-muted", children: [_jsx("span", { className: "truncate", children: item.hasBackingWorkflow
|
|
795
|
+
? `${nodeCount} workflow nodes`
|
|
796
|
+
: "Room draft or workflow shadow" }), item.schedules.length > 0 && (_jsxs("span", { children: [item.schedules.length, " schedule(s)"] }))] })] }) }));
|
|
797
|
+
}
|
|
798
|
+
if (item.trigger) {
|
|
799
|
+
const trigger = item.trigger;
|
|
800
|
+
return (_jsx(SidebarContent.Item, { onClick: onClick, onDoubleClick: onDoubleClick, active: selected, className: "h-auto", children: _jsxs("div", { className: "flex min-w-0 flex-col gap-1.5", children: [_jsxs("div", { className: "flex items-center justify-between gap-1", children: [_jsxs("div", { className: "flex items-center gap-1.5 truncate", children: [_jsx(Clock3, { className: "h-3 w-3 shrink-0 text-muted/60" }), _jsx("span", { className: "truncate text-sm font-semibold text-txt", children: item.title })] }), _jsx(StatusBadge, { label: trigger.enabled ? "Active" : "Paused", variant: trigger.enabled ? "success" : "muted", withDot: true })] }), _jsxs("div", { className: "mt-0.5 flex items-center justify-between gap-2 text-xs-tight text-muted", children: [_jsx("span", { className: "truncate", children: scheduleLabel(trigger, t, uiLanguage) }), trigger.lastStatus && (_jsx(StatusBadge, { label: localizedExecutionStatus(trigger.lastStatus, t), variant: toneForLastStatus(trigger.lastStatus) }))] })] }) }));
|
|
801
|
+
}
|
|
802
|
+
if (item.task) {
|
|
803
|
+
const task = item.task;
|
|
804
|
+
return (_jsx(SidebarContent.Item, { onClick: onClick, onDoubleClick: onDoubleClick, active: selected, className: `h-auto ${item.system ? "opacity-60" : ""}`, children: _jsxs("div", { className: "flex min-w-0 flex-col gap-1.5", children: [_jsxs("div", { className: "flex items-center justify-between gap-1", children: [_jsxs("div", { className: "flex items-center gap-1.5 truncate", children: [item.system ? (_jsx(Settings, { className: "h-3 w-3 shrink-0 text-muted/50" })) : task.isCompleted ? (_jsx(CheckCircle2, { className: "h-3 w-3 shrink-0 text-ok/60" })) : (_jsx(Circle, { className: "h-3 w-3 shrink-0 text-muted/60" })), _jsx("span", { className: `truncate text-sm font-semibold ${item.system
|
|
805
|
+
? "text-muted"
|
|
806
|
+
: task.isCompleted
|
|
807
|
+
? "text-muted line-through"
|
|
808
|
+
: "text-txt"}`, children: item.title })] }), _jsx(StatusBadge, { label: item.system ? "System" : task.isCompleted ? "Done" : "Active", variant: item.system ? "muted" : task.isCompleted ? "muted" : "success", withDot: true })] }), item.description && (_jsx("div", { className: "mt-0.5 truncate text-xs-tight text-muted", children: item.description }))] }) }));
|
|
809
|
+
}
|
|
810
|
+
return null;
|
|
630
811
|
}
|
|
631
|
-
// ── Main layout ───────────────────────────────────────────────────
|
|
632
812
|
function AutomationsLayout() {
|
|
813
|
+
const { activeConversationId, conversations, } = useApp();
|
|
633
814
|
const ctx = useAutomationsViewContext();
|
|
634
|
-
const { closeEditor,
|
|
635
|
-
// Ref forwarded to the n8n scoped chat composer so "New workflow" can focus it.
|
|
815
|
+
const { closeEditor, editorEnabled, editingId, editingTaskId, editorOpen, editorMode, form, formError, loadTriggerRuns, modalTitle, onDeleteTrigger, onRunSelectedTrigger, onSubmitTrigger, onToggleTriggerEnabled, openCreateTrigger, openCreateTask, openEditTrigger, saveFormAsTemplate, selectedItemId, setEditingId, setEditorOpen, setField, setFilter, setForm, setFormError, setSelectedItemId, setSelectedItemKind, showDetailPane, showFirstRunEmptyState, resolvedSelectedItem, t, templateNotice, triggers, filteredItems, triggerRunsById, triggersLoading, triggersSaving, uiLanguage, automationNodes, combinedError, isLoading, n8nStatus, workflowFetchError, } = ctx;
|
|
636
816
|
const workflowComposerRef = useRef(null);
|
|
637
|
-
const focusWorkflowComposer = useCallback((seed = "Create a new workflow that ") => {
|
|
638
|
-
const ta = workflowComposerRef.current;
|
|
639
|
-
if (!ta)
|
|
640
|
-
return;
|
|
641
|
-
ta.focus();
|
|
642
|
-
if (!ta.value.startsWith(seed)) {
|
|
643
|
-
const nativeSetter = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, "value")?.set;
|
|
644
|
-
nativeSetter?.call(ta, seed);
|
|
645
|
-
ta.dispatchEvent(new Event("input", { bubbles: true }));
|
|
646
|
-
}
|
|
647
|
-
}, []);
|
|
648
817
|
const [searchQuery, setSearchQuery] = useState("");
|
|
649
|
-
const
|
|
818
|
+
const [pageNotice, setPageNotice] = useState(null);
|
|
819
|
+
const [workflowBusyId, setWorkflowBusyId] = useState(null);
|
|
820
|
+
const [workflowOpsBusy, setWorkflowOpsBusy] = useState(false);
|
|
821
|
+
const [activeWorkflowConversation, setActiveWorkflowConversation] = useState(null);
|
|
650
822
|
const normalizedSearchQuery = searchQuery.trim().toLowerCase();
|
|
651
823
|
const visibleItems = useMemo(() => {
|
|
652
824
|
if (!normalizedSearchQuery)
|
|
653
825
|
return filteredItems;
|
|
654
|
-
return filteredItems.filter((item) => item
|
|
655
|
-
}, [
|
|
826
|
+
return filteredItems.filter((item) => getAutomationSearchText(item).includes(normalizedSearchQuery));
|
|
827
|
+
}, [filteredItems, normalizedSearchQuery]);
|
|
656
828
|
const mobileSidebarLabel = editorOpen || editingId || editingTaskId
|
|
657
829
|
? modalTitle
|
|
658
|
-
: (resolvedSelectedItem?.
|
|
659
|
-
const selectItem = (item) => {
|
|
830
|
+
: (resolvedSelectedItem?.title ?? "Automations");
|
|
831
|
+
const selectItem = useCallback((item) => {
|
|
660
832
|
setSelectedItemId(item.id);
|
|
661
|
-
setSelectedItemKind(item
|
|
833
|
+
setSelectedItemKind(getSelectionKind(item));
|
|
662
834
|
setEditorOpen(false);
|
|
663
835
|
setEditingId(null);
|
|
664
|
-
|
|
836
|
+
ctx.setEditingTaskId(null);
|
|
837
|
+
if (item.trigger) {
|
|
665
838
|
void loadTriggerRuns(item.trigger.id);
|
|
666
839
|
}
|
|
667
|
-
};
|
|
668
|
-
const
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
840
|
+
}, [ctx, loadTriggerRuns, setEditingId, setEditorOpen, setSelectedItemId, setSelectedItemKind]);
|
|
841
|
+
const findAutomationForConversation = useCallback((data, conversationId) => data?.automations.find((item) => item.room?.conversationId === conversationId) ?? null, []);
|
|
842
|
+
const refreshAutomationsWithDraftBinding = useCallback(async (draftConversation) => {
|
|
843
|
+
const previousWorkflowIds = new Set(ctx.allItems
|
|
844
|
+
.filter((item) => item.type === "n8n_workflow" &&
|
|
845
|
+
item.workflowId != null &&
|
|
846
|
+
!item.isDraft)
|
|
847
|
+
.map((item) => item.workflowId));
|
|
848
|
+
const data = await ctx.refreshAutomations();
|
|
849
|
+
if (!draftConversation ||
|
|
850
|
+
draftConversation.metadata?.scope !== "automation-workflow-draft" ||
|
|
851
|
+
draftConversation.metadata.automationType !== "n8n_workflow") {
|
|
852
|
+
return data;
|
|
853
|
+
}
|
|
854
|
+
const createdWorkflows = data?.automations.filter((item) => item.type === "n8n_workflow" &&
|
|
855
|
+
item.workflowId != null &&
|
|
856
|
+
!item.isDraft &&
|
|
857
|
+
!previousWorkflowIds.has(item.workflowId)) ?? [];
|
|
858
|
+
if (createdWorkflows.length !== 1) {
|
|
859
|
+
return data;
|
|
860
|
+
}
|
|
861
|
+
const createdWorkflow = createdWorkflows[0];
|
|
862
|
+
const reboundMetadata = buildWorkflowConversationMetadata(createdWorkflow.workflowId, createdWorkflow.title, draftConversation.metadata.terminalBridgeConversationId);
|
|
863
|
+
const { conversation } = await client.updateConversation(draftConversation.id, {
|
|
864
|
+
title: createdWorkflow.title,
|
|
865
|
+
metadata: reboundMetadata,
|
|
866
|
+
});
|
|
867
|
+
setActiveWorkflowConversation(conversation);
|
|
868
|
+
return await ctx.refreshAutomations();
|
|
869
|
+
}, [ctx]);
|
|
870
|
+
const createWorkflowDraft = useCallback(async (options) => {
|
|
871
|
+
setPageNotice(null);
|
|
872
|
+
const draftId = createWorkflowDraftId();
|
|
873
|
+
const bridgeConversationId = getAutomationBridgeIdForItem(resolvedSelectedItem, activeConversationId, conversations);
|
|
874
|
+
const metadata = buildWorkflowDraftConversationMetadata(draftId, bridgeConversationId);
|
|
875
|
+
try {
|
|
876
|
+
const conversation = await resolveAutomationConversation({
|
|
877
|
+
title: options?.title?.trim() || WORKFLOW_DRAFT_TITLE,
|
|
878
|
+
metadata,
|
|
879
|
+
});
|
|
880
|
+
setActiveWorkflowConversation(conversation);
|
|
881
|
+
if (options?.initialPrompt?.trim()) {
|
|
882
|
+
await client.sendConversationMessage(conversation.id, `[SYSTEM]${WORKFLOW_SYSTEM_ADDENDUM}[/SYSTEM]\n\n${options.initialPrompt.trim()}`, "DM", undefined, undefined, buildAutomationResponseRoutingMetadata(metadata));
|
|
883
|
+
}
|
|
884
|
+
const data = options?.initialPrompt
|
|
885
|
+
? await refreshAutomationsWithDraftBinding(conversation)
|
|
886
|
+
: await ctx.refreshAutomations();
|
|
887
|
+
const resolvedItem = findAutomationForConversation(data, conversation.id);
|
|
888
|
+
setFilter("workflows");
|
|
889
|
+
setSelectedItemId(resolvedItem?.id ?? `workflow-draft:${draftId}`);
|
|
890
|
+
setSelectedItemKind("workflow");
|
|
891
|
+
setEditorOpen(false);
|
|
892
|
+
setEditingId(null);
|
|
893
|
+
ctx.setEditingTaskId(null);
|
|
894
|
+
window.requestAnimationFrame(() => {
|
|
895
|
+
workflowComposerRef.current?.focus();
|
|
896
|
+
});
|
|
897
|
+
}
|
|
898
|
+
catch (error) {
|
|
899
|
+
setPageNotice(error instanceof Error
|
|
900
|
+
? error.message
|
|
901
|
+
: "Failed to create the workflow draft room.");
|
|
902
|
+
}
|
|
903
|
+
}, [
|
|
904
|
+
activeConversationId,
|
|
905
|
+
conversations,
|
|
906
|
+
ctx,
|
|
907
|
+
findAutomationForConversation,
|
|
908
|
+
refreshAutomationsWithDraftBinding,
|
|
909
|
+
resolvedSelectedItem,
|
|
910
|
+
setEditingId,
|
|
911
|
+
setEditorOpen,
|
|
912
|
+
setFilter,
|
|
913
|
+
setSelectedItemId,
|
|
914
|
+
setSelectedItemKind,
|
|
915
|
+
]);
|
|
916
|
+
const promoteAutomationToWorkflow = useCallback(async (item) => {
|
|
917
|
+
await createWorkflowDraft({
|
|
918
|
+
title: `${item.title} Workflow`,
|
|
919
|
+
initialPrompt: buildWorkflowCompilationPrompt(item),
|
|
920
|
+
});
|
|
921
|
+
}, [createWorkflowDraft]);
|
|
922
|
+
const handleWorkflowMutated = useCallback(() => {
|
|
923
|
+
void refreshAutomationsWithDraftBinding(activeWorkflowConversation);
|
|
924
|
+
}, [activeWorkflowConversation, refreshAutomationsWithDraftBinding]);
|
|
925
|
+
const handleRefreshWorkflows = useCallback(async () => {
|
|
926
|
+
setPageNotice(null);
|
|
927
|
+
const data = await refreshAutomationsWithDraftBinding(activeWorkflowConversation);
|
|
928
|
+
if (!data && ctx.automationsError) {
|
|
929
|
+
setPageNotice(ctx.automationsError);
|
|
930
|
+
}
|
|
931
|
+
}, [activeWorkflowConversation, ctx.automationsError, refreshAutomationsWithDraftBinding]);
|
|
932
|
+
const handleStartLocalN8n = useCallback(async () => {
|
|
933
|
+
setWorkflowOpsBusy(true);
|
|
934
|
+
setPageNotice(null);
|
|
935
|
+
try {
|
|
936
|
+
await client.startN8nSidecar();
|
|
937
|
+
await ctx.refreshAutomations();
|
|
938
|
+
}
|
|
939
|
+
catch (error) {
|
|
940
|
+
setPageNotice(error instanceof Error ? error.message : "Failed to start local n8n.");
|
|
941
|
+
}
|
|
942
|
+
finally {
|
|
943
|
+
setWorkflowOpsBusy(false);
|
|
944
|
+
}
|
|
945
|
+
}, [ctx]);
|
|
946
|
+
const handleToggleWorkflowActive = useCallback(async (item) => {
|
|
947
|
+
if (!item.workflowId || !item.workflow) {
|
|
948
|
+
return;
|
|
949
|
+
}
|
|
950
|
+
setWorkflowBusyId(item.workflowId);
|
|
951
|
+
setPageNotice(null);
|
|
952
|
+
try {
|
|
953
|
+
if (item.workflow.active) {
|
|
954
|
+
await client.deactivateN8nWorkflow(item.workflowId);
|
|
955
|
+
}
|
|
956
|
+
else {
|
|
957
|
+
await client.activateN8nWorkflow(item.workflowId);
|
|
958
|
+
}
|
|
959
|
+
await ctx.refreshAutomations();
|
|
960
|
+
}
|
|
961
|
+
catch (error) {
|
|
962
|
+
setPageNotice(error instanceof Error
|
|
963
|
+
? error.message
|
|
964
|
+
: "Failed to update workflow state.");
|
|
965
|
+
}
|
|
966
|
+
finally {
|
|
967
|
+
setWorkflowBusyId(null);
|
|
968
|
+
}
|
|
969
|
+
}, [ctx]);
|
|
970
|
+
const handleDeleteWorkflow = useCallback(async (item) => {
|
|
971
|
+
if (!item.workflowId) {
|
|
972
|
+
return;
|
|
973
|
+
}
|
|
974
|
+
const confirmed = await confirmDesktopAction({
|
|
975
|
+
title: "Delete Workflow",
|
|
976
|
+
message: `Delete ${item.title}?`,
|
|
977
|
+
confirmLabel: "Delete Workflow",
|
|
978
|
+
cancelLabel: t("common.cancel"),
|
|
979
|
+
type: "warning",
|
|
980
|
+
});
|
|
981
|
+
if (!confirmed)
|
|
982
|
+
return;
|
|
983
|
+
setWorkflowBusyId(item.workflowId);
|
|
984
|
+
setPageNotice(null);
|
|
985
|
+
try {
|
|
986
|
+
await client.deleteN8nWorkflow(item.workflowId);
|
|
987
|
+
await ctx.refreshAutomations();
|
|
988
|
+
}
|
|
989
|
+
catch (error) {
|
|
990
|
+
setPageNotice(error instanceof Error ? error.message : "Failed to delete workflow.");
|
|
991
|
+
}
|
|
992
|
+
finally {
|
|
993
|
+
setWorkflowBusyId(null);
|
|
994
|
+
}
|
|
995
|
+
}, [ctx, t]);
|
|
996
|
+
const automationsSidebar = (_jsx(Sidebar, { testId: "automations-sidebar", collapsible: true, contentIdentity: "automations", collapseButtonTestId: "automations-sidebar-collapse-toggle", expandButtonTestId: "automations-sidebar-expand-toggle", collapseButtonAriaLabel: "Collapse automations", expandButtonAriaLabel: "Expand automations", header: null, collapsedRailAction: _jsx(SidebarCollapsedActionButton, { "aria-label": "New coordinator automation", onClick: openCreateTask, children: _jsx(Plus, { className: "h-4 w-4" }) }), collapsedRailItems: visibleItems.map((item) => (_jsx(SidebarContent.RailItem, { "aria-label": item.title, title: item.title, active: item.id === selectedItemId, indicatorTone: getAutomationIndicatorTone(item), onClick: () => selectItem(item), children: railMonogram(item.title) }, item.id))), children: _jsx(SidebarScrollRegion, { children: _jsxs(SidebarPanel, { children: [_jsxs("div", { className: "mb-3 space-y-2", children: [_jsx("input", { type: "text", value: searchQuery, onChange: (event) => setSearchQuery(event.target.value), placeholder: "Search automations", "aria-label": "Search automations", autoComplete: "off", spellCheck: false, className: "w-full rounded-lg border border-border/30 bg-bg/30 px-3 py-1.5 text-sm text-txt placeholder:text-muted/50 focus:border-accent/40 focus:outline-none" }), _jsxs("div", { className: "flex flex-wrap gap-2", children: [_jsxs(Button, { variant: "outline", size: "sm", className: "h-8 gap-1 px-3 text-xs font-medium", onClick: openCreateTask, children: [_jsx(SquareTerminal, { className: "h-3.5 w-3.5" }), "Coordinator"] }), _jsxs(Button, { variant: "outline", size: "sm", className: "h-8 gap-1 px-3 text-xs font-medium", onClick: openCreateTrigger, children: [_jsx(Clock3, { className: "h-3.5 w-3.5" }), "Schedule"] }), _jsxs(Button, { variant: "outline", size: "sm", className: "h-8 gap-1 px-3 text-xs font-medium", onClick: () => void createWorkflowDraft(), children: [_jsx(Workflow, { className: "h-3.5 w-3.5" }), "Workflow"] })] })] }), _jsx(FilterTabs, {}), isLoading && (_jsx(SidebarContent.Notice, { icon: _jsx("div", { className: "h-4 w-4 animate-spin rounded-full border-2 border-muted/30 border-t-muted/80" }), children: t("common.loading") })), !isLoading && normalizedSearchQuery && visibleItems.length === 0 ? (_jsx(SidebarContent.EmptyState, { className: "px-4 py-6", children: "No matching automations" })) : (visibleItems.map((item) => (_jsx(AutomationSidebarItem, { item: item, selected: selectedItemId === item.id, onClick: () => selectItem(item), onDoubleClick: item.task && !item.system
|
|
997
|
+
? () => ctx.openEditTask(item.task)
|
|
998
|
+
: item.trigger
|
|
999
|
+
? () => {
|
|
1000
|
+
ctx.openEditTrigger(item.trigger);
|
|
1001
|
+
void loadTriggerRuns(item.trigger.id);
|
|
1002
|
+
}
|
|
1003
|
+
: undefined }, item.id))))] }) }) }));
|
|
707
1004
|
return (_jsx(PageLayout, { className: "h-full bg-transparent", "data-testid": "automations-shell", sidebar: automationsSidebar, contentInnerClassName: "mx-auto w-full max-w-[96rem]", footer: _jsx(WidgetHost, { slot: "automations", className: "py-3" }), mobileSidebarLabel: mobileSidebarLabel, children: _jsxs("div", { className: "flex min-h-0 flex-1 flex-col", children: [showDetailPane ? (_jsx("button", { type: "button", className: "mb-3 flex items-center gap-2 rounded-2xl border border-border/30 bg-bg/25 px-4 py-3 text-base font-medium text-muted hover:text-txt md:hidden", onClick: () => {
|
|
708
1005
|
setSelectedItemId(null);
|
|
709
1006
|
setSelectedItemKind(null);
|
|
710
1007
|
setEditorOpen(false);
|
|
711
1008
|
setEditingId(null);
|
|
712
1009
|
ctx.setEditingTaskId(null);
|
|
713
|
-
}, children: "\u2190 Back" })) : null,
|
|
1010
|
+
}, children: "\u2190 Back" })) : null, (pageNotice || combinedError) && (_jsx(PagePanel, { variant: "padded", className: "mb-4 border border-danger/20 bg-danger/5", children: _jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsx("p", { className: "text-sm text-danger", children: pageNotice ?? combinedError }), pageNotice && (_jsx(Button, { variant: "ghost", size: "sm", className: "text-danger hover:bg-danger/10", onClick: () => setPageNotice(null), children: "Dismiss" }))] }) })), editorOpen || editingId || editingTaskId ? (editorMode === "task" || editingTaskId ? (_jsx(TaskForm, {})) : (_jsx(HeartbeatForm, { form: form, editingId: editingId, editorEnabled: editorEnabled, modalTitle: modalTitle, formError: formError, triggersSaving: triggersSaving, templateNotice: templateNotice, triggers: triggers, triggerRunsById: triggerRunsById, t: t, selectedTriggerId: editingId, setField: setField, setForm: setForm, setFormError: setFormError, closeEditor: closeEditor, onSubmit: onSubmitTrigger, onDelete: onDeleteTrigger, onRunSelectedTrigger: onRunSelectedTrigger, onToggleTriggerEnabled: onToggleTriggerEnabled, saveFormAsTemplate: saveFormAsTemplate, loadTriggerRuns: loadTriggerRuns }))) : resolvedSelectedItem?.type === "n8n_workflow" ? (_jsx(WorkflowAutomationDetailPane, { automation: resolvedSelectedItem, nodes: automationNodes, n8nStatus: n8nStatus, workflowFetchError: workflowFetchError, workflowBusyId: workflowBusyId, workflowOpsBusy: workflowOpsBusy, onConversationResolved: setActiveWorkflowConversation, onDeleteWorkflow: handleDeleteWorkflow, onRefreshWorkflows: handleRefreshWorkflows, onStartLocalN8n: handleStartLocalN8n, onToggleWorkflowActive: handleToggleWorkflowActive, onWorkflowMutated: handleWorkflowMutated })) : resolvedSelectedItem?.trigger ? (_jsx(TriggerAutomationDetailPane, { automation: resolvedSelectedItem, nodes: automationNodes, onAutomationMutated: () => {
|
|
1011
|
+
void ctx.refreshAutomations();
|
|
1012
|
+
}, onPromoteToWorkflow: promoteAutomationToWorkflow })) : resolvedSelectedItem?.task ? (_jsx(TaskAutomationDetailPane, { automation: resolvedSelectedItem, nodes: automationNodes, onAutomationMutated: () => {
|
|
1013
|
+
void ctx.refreshAutomations();
|
|
1014
|
+
}, onPromoteToWorkflow: promoteAutomationToWorkflow })) : (_jsx("div", { className: "flex min-h-0 flex-1 items-center justify-center px-8 py-10 text-center", children: _jsxs("div", { className: "space-y-3", children: [_jsx("h3", { className: "text-lg font-semibold text-txt-strong", children: showFirstRunEmptyState
|
|
714
1015
|
? "Create your first automation"
|
|
715
|
-
: "Select an
|
|
1016
|
+
: "Select an automation" }), showFirstRunEmptyState && (_jsx("p", { className: "text-sm text-muted", children: "Build a coordinator automation, schedule recurring work, or create an n8n workflow room." }))] }) }))] }) }));
|
|
716
1017
|
}
|
|
717
|
-
// ── Exports ───────────────────────────────────────────────────────
|
|
718
1018
|
export function AutomationsView() {
|
|
719
1019
|
const controller = useAutomationsViewController();
|
|
720
1020
|
return (_jsx(AutomationsViewContext.Provider, { value: controller, children: _jsx(AutomationsLayout, {}) }));
|