@agent-native/dispatch 0.0.0-beta-20260819173911 → 0.0.0-beta-20260819184657
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/actions/provider-api-register.d.ts +11 -11
- package/dist/components/automation-details-panel.d.ts +8 -0
- package/dist/components/automation-details-panel.d.ts.map +1 -0
- package/dist/components/automation-details-panel.js +80 -0
- package/dist/components/automation-details-panel.js.map +1 -0
- package/dist/lib/automations.d.ts +12 -0
- package/dist/lib/automations.d.ts.map +1 -1
- package/dist/lib/automations.js +9 -0
- package/dist/lib/automations.js.map +1 -1
- package/dist/routes/pages/admin.d.ts.map +1 -1
- package/dist/routes/pages/admin.js +6 -1
- package/dist/routes/pages/admin.js.map +1 -1
- package/dist/routes/pages/automations.d.ts.map +1 -1
- package/dist/routes/pages/automations.js +59 -21
- package/dist/routes/pages/automations.js.map +1 -1
- package/package.json +3 -3
- package/src/components/automation-details-panel.tsx +420 -0
- package/src/lib/automations.spec.ts +19 -1
- package/src/lib/automations.ts +23 -0
- package/src/routes/pages/admin.tsx +8 -1
- package/src/routes/pages/automations.tsx +177 -97
- package/dist/components/automation-run-history-dialog.d.ts +0 -9
- package/dist/components/automation-run-history-dialog.d.ts.map +0 -1
- package/dist/components/automation-run-history-dialog.js +0 -63
- package/dist/components/automation-run-history-dialog.js.map +0 -1
- package/src/components/automation-run-history-dialog.tsx +0 -247
|
@@ -50,8 +50,6 @@ declare const _default: import("node_modules/@agent-native/core/dist").ActionDef
|
|
|
50
50
|
id?: undefined;
|
|
51
51
|
provider?: undefined;
|
|
52
52
|
deleted?: undefined;
|
|
53
|
-
found?: undefined;
|
|
54
|
-
message?: undefined;
|
|
55
53
|
providers: {
|
|
56
54
|
id: string;
|
|
57
55
|
label: string;
|
|
@@ -62,44 +60,46 @@ declare const _default: import("node_modules/@agent-native/core/dist").ActionDef
|
|
|
62
60
|
updatedAt: number;
|
|
63
61
|
}[];
|
|
64
62
|
count: number;
|
|
63
|
+
found?: undefined;
|
|
65
64
|
registered?: undefined;
|
|
66
65
|
label?: undefined;
|
|
66
|
+
message?: undefined;
|
|
67
67
|
} | {
|
|
68
68
|
id?: undefined;
|
|
69
|
+
count?: undefined;
|
|
69
70
|
deleted?: undefined;
|
|
70
|
-
message?: undefined;
|
|
71
71
|
providers?: undefined;
|
|
72
|
-
count?: undefined;
|
|
73
72
|
found: boolean;
|
|
74
73
|
provider: import("node_modules/@agent-native/core/dist/provider-api").CustomProviderConfig;
|
|
75
74
|
registered?: undefined;
|
|
76
75
|
label?: undefined;
|
|
76
|
+
message?: undefined;
|
|
77
77
|
} | {
|
|
78
|
+
count?: undefined;
|
|
78
79
|
provider?: undefined;
|
|
79
80
|
deleted?: undefined;
|
|
80
|
-
message?: undefined;
|
|
81
81
|
providers?: undefined;
|
|
82
|
-
count?: undefined;
|
|
83
82
|
found: boolean;
|
|
84
83
|
id: string;
|
|
85
84
|
registered?: undefined;
|
|
86
85
|
label?: undefined;
|
|
86
|
+
message?: undefined;
|
|
87
87
|
} | {
|
|
88
|
+
count?: undefined;
|
|
88
89
|
provider?: undefined;
|
|
89
|
-
found?: undefined;
|
|
90
|
-
message?: undefined;
|
|
91
90
|
providers?: undefined;
|
|
92
|
-
|
|
91
|
+
found?: undefined;
|
|
93
92
|
deleted: boolean;
|
|
94
93
|
id: string;
|
|
95
94
|
registered?: undefined;
|
|
96
95
|
label?: undefined;
|
|
96
|
+
message?: undefined;
|
|
97
97
|
} | {
|
|
98
|
+
count?: undefined;
|
|
98
99
|
provider?: undefined;
|
|
99
100
|
deleted?: undefined;
|
|
100
|
-
found?: undefined;
|
|
101
101
|
providers?: undefined;
|
|
102
|
-
|
|
102
|
+
found?: undefined;
|
|
103
103
|
registered: boolean;
|
|
104
104
|
id: string;
|
|
105
105
|
label: string;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type DispatchAutomationItem } from "../lib/automations.js";
|
|
2
|
+
export interface AutomationDetailsPanelProps {
|
|
3
|
+
automation: DispatchAutomationItem;
|
|
4
|
+
isToggling?: boolean;
|
|
5
|
+
onToggle: () => void;
|
|
6
|
+
}
|
|
7
|
+
export declare function AutomationDetailsPanel({ automation, isToggling, onToggle, }: AutomationDetailsPanelProps): import("node_modules/@types/react").JSX.Element;
|
|
8
|
+
//# sourceMappingURL=automation-details-panel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"automation-details-panel.d.ts","sourceRoot":"","sources":["../../src/components/automation-details-panel.tsx"],"names":[],"mappings":"AAeA,OAAO,EAEL,KAAK,sBAAsB,EAC5B,MAAM,oBAAoB,CAAC;AAgB5B,MAAM,WAAW,2BAA2B;IAC1C,UAAU,EAAE,sBAAsB,CAAC;IACnC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB;AAkED,wBAAgB,sBAAsB,CAAC,EACrC,UAAU,EACV,UAAkB,EAClB,QAAQ,GACT,EAAE,2BAA2B,mDAuT7B"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useActionQuery } from "@agent-native/core/client/hooks";
|
|
3
|
+
import { IconAlertTriangle, IconClock, IconExternalLink, IconPlayerPause, IconPlayerPlay, } from "@tabler/icons-react";
|
|
4
|
+
import { Link } from "react-router";
|
|
5
|
+
import { automationScopeLabel, automationStatus, automationTarget, } from "../lib/automation-display.js";
|
|
6
|
+
import { automationRunScope, } from "../lib/automations.js";
|
|
7
|
+
import { ActionQueryError } from "./action-query-error.js";
|
|
8
|
+
import { Badge } from "./ui/badge.js";
|
|
9
|
+
import { Button } from "./ui/button.js";
|
|
10
|
+
import { Skeleton } from "./ui/skeleton.js";
|
|
11
|
+
function formatTimestamp(value) {
|
|
12
|
+
if (value == null)
|
|
13
|
+
return "Not recorded";
|
|
14
|
+
const date = new Date(value);
|
|
15
|
+
return Number.isNaN(date.getTime()) ? "Unknown time" : date.toLocaleString();
|
|
16
|
+
}
|
|
17
|
+
function formatDuration(run) {
|
|
18
|
+
if (!run.finishedAt)
|
|
19
|
+
return null;
|
|
20
|
+
const seconds = Math.max(0, Math.round((run.finishedAt - run.startedAt) / 1000));
|
|
21
|
+
return `${seconds}s`;
|
|
22
|
+
}
|
|
23
|
+
function runDebugPath(run) {
|
|
24
|
+
const params = new URLSearchParams();
|
|
25
|
+
if (run.runId)
|
|
26
|
+
params.set("runId", run.runId);
|
|
27
|
+
else if (run.threadId)
|
|
28
|
+
params.set("threadId", run.threadId);
|
|
29
|
+
else
|
|
30
|
+
return null;
|
|
31
|
+
return `/admin/thread-debug?${params.toString()}`;
|
|
32
|
+
}
|
|
33
|
+
function runStatusVariant(status) {
|
|
34
|
+
if (status === "error")
|
|
35
|
+
return "destructive";
|
|
36
|
+
if (status === "success")
|
|
37
|
+
return "default";
|
|
38
|
+
return "outline";
|
|
39
|
+
}
|
|
40
|
+
function DetailField({ label, value, mono = false, }) {
|
|
41
|
+
return (_jsxs("div", { className: "min-w-0", children: [_jsx("dt", { className: "text-[11px] uppercase tracking-wide text-muted-foreground", children: label }), _jsx("dd", { className: mono
|
|
42
|
+
? "mt-1 break-words font-mono text-xs text-foreground"
|
|
43
|
+
: "mt-1 break-words text-sm text-foreground", children: value })] }));
|
|
44
|
+
}
|
|
45
|
+
function runAsLabel(value) {
|
|
46
|
+
if (value === "creator")
|
|
47
|
+
return "Creator identity";
|
|
48
|
+
if (value === "shared")
|
|
49
|
+
return "Shared identity";
|
|
50
|
+
return "Default identity";
|
|
51
|
+
}
|
|
52
|
+
export function AutomationDetailsPanel({ automation, isToggling = false, onToggle, }) {
|
|
53
|
+
const runsQuery = useActionQuery("list-automation-runs", {
|
|
54
|
+
name: automation.name,
|
|
55
|
+
scope: automationRunScope(automation),
|
|
56
|
+
appId: automation.appId ?? "dispatch",
|
|
57
|
+
}, { staleTime: 5_000 });
|
|
58
|
+
const runs = runsQuery.data ?? [];
|
|
59
|
+
const status = automationStatus(automation);
|
|
60
|
+
const isScheduled = automation.triggerType !== "event";
|
|
61
|
+
const prompt = automation.body?.trim();
|
|
62
|
+
const hasExecutionTarget = Boolean(automation.executionHostId ||
|
|
63
|
+
automation.executionEngine ||
|
|
64
|
+
automation.executionCwd);
|
|
65
|
+
const hasDelivery = Boolean(automation.deliveryPlatform ||
|
|
66
|
+
automation.deliveryDestination ||
|
|
67
|
+
automation.deliveryThreadRef);
|
|
68
|
+
return (_jsxs("aside", { className: "flex min-h-[34rem] min-w-0 flex-col overflow-hidden rounded-lg border bg-card", children: [_jsx("header", { className: "flex items-start justify-between gap-3 border-b p-5", children: _jsxs("div", { className: "min-w-0", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: status.tone === "danger"
|
|
69
|
+
? "size-2 shrink-0 rounded-full bg-destructive"
|
|
70
|
+
: status.tone === "default"
|
|
71
|
+
? "size-2 shrink-0 rounded-full bg-primary"
|
|
72
|
+
: "size-2 shrink-0 rounded-full bg-muted-foreground/40" }), _jsx(Badge, { variant: status.tone === "danger" ? "destructive" : "outline", className: "h-5", children: status.label })] }), _jsx("h2", { className: "mt-2 break-words text-base font-semibold text-foreground", children: automation.name }), _jsxs("p", { className: "mt-1 text-xs text-muted-foreground", children: [isScheduled ? "Schedule" : "Event", " \u00B7", " ", automationScopeLabel(automation)] })] }) }), _jsxs("div", { className: "min-h-0 flex-1 space-y-5 overflow-y-auto p-5", children: [_jsxs("section", { children: [_jsx("h3", { className: "text-sm font-semibold text-foreground", children: "Prompt" }), prompt ? (_jsx("pre", { className: "mt-2 max-h-64 overflow-auto whitespace-pre-wrap break-words rounded-md bg-muted/40 p-3 font-sans text-sm leading-6 text-foreground", children: prompt })) : (_jsx("p", { className: "mt-2 rounded-md border border-dashed p-3 text-sm text-muted-foreground", children: "No prompt recorded." }))] }), _jsxs("section", { className: "border-t pt-5", children: [_jsx("h3", { className: "text-sm font-semibold text-foreground", children: "Configuration" }), _jsxs("dl", { className: "mt-3 grid grid-cols-2 gap-x-4 gap-y-4", children: [_jsx(DetailField, { label: "Trigger", value: isScheduled ? "Scheduled" : "Event-triggered" }), _jsx(DetailField, { label: isScheduled ? "Schedule" : "Event", value: automationTarget(automation) }), isScheduled ? (_jsxs(_Fragment, { children: [automation.schedule ? (_jsx(DetailField, { label: "Cron expression", value: automation.schedule, mono: true })) : null, _jsx(DetailField, { label: "Timezone", value: automation.timezone || "Account default" })] })) : null, _jsx(DetailField, { label: "Condition", value: automation.condition || "Always" }), _jsx(DetailField, { label: "Domain", value: automation.domain || "Default" }), _jsx(DetailField, { label: "Model", value: automation.model || "Default model" }), _jsx(DetailField, { label: "Mode", value: automation.mode || "Agentic" }), _jsx(DetailField, { label: "Runs as", value: runAsLabel(automation.runAs) }), _jsx(DetailField, { label: "Next run", value: automation.enabled
|
|
73
|
+
? formatTimestamp(automation.nextRun)
|
|
74
|
+
: "Paused" }), _jsx(DetailField, { label: "Last run", value: formatTimestamp(automation.lastRun) }), _jsx(DetailField, { label: "Last checked", value: formatTimestamp(automation.lastCheck) }), _jsx(DetailField, { label: "Created by", value: automation.createdBy || "Unknown" })] })] }), _jsxs("section", { className: "border-t pt-5", children: [_jsx("h3", { className: "text-sm font-semibold text-foreground", children: "Capabilities" }), automation.mcpTools && automation.mcpTools.length > 0 ? (_jsx("div", { className: "mt-2 flex flex-wrap gap-1.5", children: automation.mcpTools.map((toolName) => (_jsx("code", { className: "max-w-full break-all rounded bg-muted px-1.5 py-1 text-[11px] text-muted-foreground", children: toolName }, toolName))) })) : (_jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: "No explicit tool allowlist." }))] }), hasExecutionTarget ? (_jsxs("section", { className: "border-t pt-5", children: [_jsx("h3", { className: "text-sm font-semibold text-foreground", children: "Execution target" }), _jsxs("dl", { className: "mt-3 grid grid-cols-2 gap-x-4 gap-y-4", children: [_jsx(DetailField, { label: "Host", value: automation.executionHostId || "Server default", mono: Boolean(automation.executionHostId) }), _jsx(DetailField, { label: "Engine", value: automation.executionEngine || "Default" }), automation.executionCwd ? (_jsx(DetailField, { label: "Working directory", value: automation.executionCwd, mono: true })) : null] })] })) : null, hasDelivery ? (_jsxs("section", { className: "border-t pt-5", children: [_jsx("h3", { className: "text-sm font-semibold text-foreground", children: "Delivery" }), _jsxs("dl", { className: "mt-3 grid grid-cols-2 gap-x-4 gap-y-4", children: [automation.deliveryPlatform ? (_jsx(DetailField, { label: "Platform", value: automation.deliveryPlatform })) : null, automation.deliveryDestination ? (_jsx(DetailField, { label: "Destination", value: automation.deliveryDestination })) : null, automation.deliveryThreadRef ? (_jsx(DetailField, { label: "Thread reference", value: automation.deliveryThreadRef, mono: true })) : null] })] })) : null, automation.lastError ? (_jsxs("section", { className: "flex items-start gap-2 border-t border-destructive/30 pt-5", children: [_jsx(IconAlertTriangle, { className: "mt-0.5 size-4 shrink-0 text-destructive" }), _jsxs("div", { className: "min-w-0", children: [_jsx("h3", { className: "text-sm font-semibold text-destructive", children: "Latest scheduler result" }), _jsx("p", { className: "mt-1 break-words text-sm text-muted-foreground", children: automation.lastError })] })] })) : null, _jsxs("section", { className: "border-t pt-5", children: [_jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsx("h3", { className: "text-sm font-semibold text-foreground", children: "Past runs" }), runs.length > 0 ? (_jsxs(Badge, { variant: "outline", children: [runs.length, " shown"] })) : null] }), _jsx("div", { className: "mt-3", children: runsQuery.isLoading ? (_jsx("div", { className: "space-y-3 rounded-md border p-3", children: Array.from({ length: 3 }).map((_, index) => (_jsxs("div", { className: "space-y-2", children: [_jsx(Skeleton, { className: "h-4 w-2/3" }), _jsx(Skeleton, { className: "h-3 w-1/2" })] }, index))) })) : runsQuery.isError ? (_jsx(ActionQueryError, { error: runsQuery.error, onRetry: () => void runsQuery.refetch() })) : runs.length === 0 ? (_jsx("p", { className: "rounded-md border border-dashed p-3 text-sm text-muted-foreground", children: "No execution recorded yet." })) : (_jsx("ul", { className: "divide-y rounded-md border", children: runs.map((run) => {
|
|
75
|
+
const debugPath = runDebugPath(run);
|
|
76
|
+
const duration = formatDuration(run);
|
|
77
|
+
return (_jsxs("li", { className: "space-y-2 p-3", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2 text-sm", children: [_jsx(Badge, { variant: runStatusVariant(run.status), children: run.status }), _jsx("span", { className: "text-muted-foreground", children: formatTimestamp(run.startedAt) }), duration ? (_jsxs("span", { className: "inline-flex items-center gap-1 text-xs text-muted-foreground", children: [_jsx(IconClock, { className: "size-3.5" }), duration] })) : null, debugPath ? (_jsxs(Link, { to: debugPath, className: "ml-auto inline-flex items-center gap-1 text-xs font-medium text-foreground underline-offset-4 hover:underline", children: [_jsx(IconExternalLink, { className: "size-3.5" }), "Open debug"] })) : null] }), run.error ? (_jsx("p", { className: "break-words text-sm text-destructive", children: run.error })) : null] }, run.id));
|
|
78
|
+
}) })) })] })] }), _jsxs("footer", { className: "flex flex-wrap items-center gap-2 border-t p-4", children: [_jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: onToggle, disabled: automation.canUpdate === false || isToggling, children: [automation.enabled ? _jsx(IconPlayerPause, {}) : _jsx(IconPlayerPlay, {}), automation.enabled ? "Pause" : "Resume"] }), automation.canUpdate === false ? (_jsx("span", { className: "text-xs text-muted-foreground", children: "Read-only" })) : null] })] }));
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=automation-details-panel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"automation-details-panel.js","sourceRoot":"","sources":["../../src/components/automation-details-panel.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACjE,OAAO,EACL,iBAAiB,EACjB,SAAS,EACT,gBAAgB,EAChB,eAAe,EACf,cAAc,GACf,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEpC,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,kBAAkB,GAEnB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAkBzC,SAAS,eAAe,CAAC,KAAyC;IAChE,IAAI,KAAK,IAAI,IAAI;QAAE,OAAO,cAAc,CAAC;IACzC,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC;IAC7B,OAAO,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC;AAC/E,CAAC;AAED,SAAS,cAAc,CAAC,GAAkB;IACxC,IAAI,CAAC,GAAG,CAAC,UAAU;QAAE,OAAO,IAAI,CAAC;IACjC,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CACtB,CAAC,EACD,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC,CACpD,CAAC;IACF,OAAO,GAAG,OAAO,GAAG,CAAC;AACvB,CAAC;AAED,SAAS,YAAY,CAAC,GAAkB;IACtC,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAC;IACrC,IAAI,GAAG,CAAC,KAAK;QAAE,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC;SACzC,IAAI,GAAG,CAAC,QAAQ;QAAE,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC;;QACvD,OAAO,IAAI,CAAC;IACjB,OAAO,uBAAuB,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC;AACpD,CAAC;AAED,SAAS,gBAAgB,CACvB,MAA+B;IAE/B,IAAI,MAAM,KAAK,OAAO;QAAE,OAAO,aAAa,CAAC;IAC7C,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC3C,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,WAAW,CAAC,EACnB,KAAK,EACL,KAAK,EACL,IAAI,GAAG,KAAK,GAKb;IACC,OAAO,CACL,eAAK,SAAS,EAAC,SAAS,aACtB,aAAI,SAAS,EAAC,2DAA2D,YACtE,KAAK,GACH,EACL,aACE,SAAS,EACP,IAAI;oBACF,CAAC,CAAC,oDAAoD;oBACtD,CAAC,CAAC,0CAA0C,YAG/C,KAAK,GACH,IACD,CACP,CAAC;AACJ,CAAC;AAED,SAAS,UAAU,CAAC,KAAsC;IACxD,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,kBAAkB,CAAC;IACnD,IAAI,KAAK,KAAK,QAAQ;QAAE,OAAO,iBAAiB,CAAC;IACjD,OAAO,kBAAkB,CAAC;AAC5B,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,EACrC,UAAU,EACV,UAAU,GAAG,KAAK,EAClB,QAAQ,GACoB;IAC5B,MAAM,SAAS,GAAG,cAAc,CAC9B,sBAAsB,EACtB;QACE,IAAI,EAAE,UAAU,CAAC,IAAI;QACrB,KAAK,EAAE,kBAAkB,CAAC,UAAU,CAAC;QACrC,KAAK,EAAE,UAAU,CAAC,KAAK,IAAI,UAAU;KACtC,EACD,EAAE,SAAS,EAAE,KAAK,EAAE,CACrB,CAAC;IACF,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,IAAI,EAAE,CAAC;IAClC,MAAM,MAAM,GAAG,gBAAgB,CAAC,UAAU,CAAC,CAAC;IAC5C,MAAM,WAAW,GAAG,UAAU,CAAC,WAAW,KAAK,OAAO,CAAC;IACvD,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC;IACvC,MAAM,kBAAkB,GAAG,OAAO,CAChC,UAAU,CAAC,eAAe;QAC1B,UAAU,CAAC,eAAe;QAC1B,UAAU,CAAC,YAAY,CACxB,CAAC;IACF,MAAM,WAAW,GAAG,OAAO,CACzB,UAAU,CAAC,gBAAgB;QAC3B,UAAU,CAAC,mBAAmB;QAC9B,UAAU,CAAC,iBAAiB,CAC7B,CAAC;IAEF,OAAO,CACL,iBAAO,SAAS,EAAC,+EAA+E,aAC9F,iBAAQ,SAAS,EAAC,qDAAqD,YACrE,eAAK,SAAS,EAAC,SAAS,aACtB,eAAK,SAAS,EAAC,yBAAyB,aACtC,eACE,SAAS,EACP,MAAM,CAAC,IAAI,KAAK,QAAQ;wCACtB,CAAC,CAAC,6CAA6C;wCAC/C,CAAC,CAAC,MAAM,CAAC,IAAI,KAAK,SAAS;4CACzB,CAAC,CAAC,yCAAyC;4CAC3C,CAAC,CAAC,qDAAqD,GAE7D,EACF,KAAC,KAAK,IACJ,OAAO,EAAE,MAAM,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,EAC7D,SAAS,EAAC,KAAK,YAEd,MAAM,CAAC,KAAK,GACP,IACJ,EACN,aAAI,SAAS,EAAC,0DAA0D,YACrE,UAAU,CAAC,IAAI,GACb,EACL,aAAG,SAAS,EAAC,oCAAoC,aAC9C,WAAW,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,aAAI,GAAG,EACzC,oBAAoB,CAAC,UAAU,CAAC,IAC/B,IACA,GACC,EAET,eAAK,SAAS,EAAC,8CAA8C,aAC3D,8BACE,aAAI,SAAS,EAAC,uCAAuC,uBAAY,EAChE,MAAM,CAAC,CAAC,CAAC,CACR,cAAK,SAAS,EAAC,oIAAoI,YAChJ,MAAM,GACH,CACP,CAAC,CAAC,CAAC,CACF,YAAG,SAAS,EAAC,wEAAwE,oCAEjF,CACL,IACO,EAEV,mBAAS,SAAS,EAAC,eAAe,aAChC,aAAI,SAAS,EAAC,uCAAuC,8BAEhD,EACL,cAAI,SAAS,EAAC,uCAAuC,aACnD,KAAC,WAAW,IACV,KAAK,EAAC,SAAS,EACf,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,iBAAiB,GACpD,EACF,KAAC,WAAW,IACV,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,EACzC,KAAK,EAAE,gBAAgB,CAAC,UAAU,CAAC,GACnC,EACD,WAAW,CAAC,CAAC,CAAC,CACb,8BACG,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CACrB,KAAC,WAAW,IACV,KAAK,EAAC,iBAAiB,EACvB,KAAK,EAAE,UAAU,CAAC,QAAQ,EAC1B,IAAI,SACJ,CACH,CAAC,CAAC,CAAC,IAAI,EACR,KAAC,WAAW,IACV,KAAK,EAAC,UAAU,EAChB,KAAK,EAAE,UAAU,CAAC,QAAQ,IAAI,iBAAiB,GAC/C,IACD,CACJ,CAAC,CAAC,CAAC,IAAI,EACR,KAAC,WAAW,IACV,KAAK,EAAC,WAAW,EACjB,KAAK,EAAE,UAAU,CAAC,SAAS,IAAI,QAAQ,GACvC,EACF,KAAC,WAAW,IACV,KAAK,EAAC,QAAQ,EACd,KAAK,EAAE,UAAU,CAAC,MAAM,IAAI,SAAS,GACrC,EACF,KAAC,WAAW,IACV,KAAK,EAAC,OAAO,EACb,KAAK,EAAE,UAAU,CAAC,KAAK,IAAI,eAAe,GAC1C,EACF,KAAC,WAAW,IAAC,KAAK,EAAC,MAAM,EAAC,KAAK,EAAE,UAAU,CAAC,IAAI,IAAI,SAAS,GAAI,EACjE,KAAC,WAAW,IAAC,KAAK,EAAC,SAAS,EAAC,KAAK,EAAE,UAAU,CAAC,UAAU,CAAC,KAAK,CAAC,GAAI,EACpE,KAAC,WAAW,IACV,KAAK,EAAC,UAAU,EAChB,KAAK,EACH,UAAU,CAAC,OAAO;4CAChB,CAAC,CAAC,eAAe,CAAC,UAAU,CAAC,OAAO,CAAC;4CACrC,CAAC,CAAC,QAAQ,GAEd,EACF,KAAC,WAAW,IACV,KAAK,EAAC,UAAU,EAChB,KAAK,EAAE,eAAe,CAAC,UAAU,CAAC,OAAO,CAAC,GAC1C,EACF,KAAC,WAAW,IACV,KAAK,EAAC,cAAc,EACpB,KAAK,EAAE,eAAe,CAAC,UAAU,CAAC,SAAS,CAAC,GAC5C,EACF,KAAC,WAAW,IACV,KAAK,EAAC,YAAY,EAClB,KAAK,EAAE,UAAU,CAAC,SAAS,IAAI,SAAS,GACxC,IACC,IACG,EAEV,mBAAS,SAAS,EAAC,eAAe,aAChC,aAAI,SAAS,EAAC,uCAAuC,6BAEhD,EACJ,UAAU,CAAC,QAAQ,IAAI,UAAU,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CACvD,cAAK,SAAS,EAAC,6BAA6B,YACzC,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CACrC,eAEE,SAAS,EAAC,qFAAqF,YAE9F,QAAQ,IAHJ,QAAQ,CAIR,CACR,CAAC,GACE,CACP,CAAC,CAAC,CAAC,CACF,YAAG,SAAS,EAAC,oCAAoC,4CAE7C,CACL,IACO,EAET,kBAAkB,CAAC,CAAC,CAAC,CACpB,mBAAS,SAAS,EAAC,eAAe,aAChC,aAAI,SAAS,EAAC,uCAAuC,iCAEhD,EACL,cAAI,SAAS,EAAC,uCAAuC,aACnD,KAAC,WAAW,IACV,KAAK,EAAC,MAAM,EACZ,KAAK,EAAE,UAAU,CAAC,eAAe,IAAI,gBAAgB,EACrD,IAAI,EAAE,OAAO,CAAC,UAAU,CAAC,eAAe,CAAC,GACzC,EACF,KAAC,WAAW,IACV,KAAK,EAAC,QAAQ,EACd,KAAK,EAAE,UAAU,CAAC,eAAe,IAAI,SAAS,GAC9C,EACD,UAAU,CAAC,YAAY,CAAC,CAAC,CAAC,CACzB,KAAC,WAAW,IACV,KAAK,EAAC,mBAAmB,EACzB,KAAK,EAAE,UAAU,CAAC,YAAY,EAC9B,IAAI,SACJ,CACH,CAAC,CAAC,CAAC,IAAI,IACL,IACG,CACX,CAAC,CAAC,CAAC,IAAI,EAEP,WAAW,CAAC,CAAC,CAAC,CACb,mBAAS,SAAS,EAAC,eAAe,aAChC,aAAI,SAAS,EAAC,uCAAuC,yBAAc,EACnE,cAAI,SAAS,EAAC,uCAAuC,aAClD,UAAU,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAC7B,KAAC,WAAW,IACV,KAAK,EAAC,UAAU,EAChB,KAAK,EAAE,UAAU,CAAC,gBAAgB,GAClC,CACH,CAAC,CAAC,CAAC,IAAI,EACP,UAAU,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAChC,KAAC,WAAW,IACV,KAAK,EAAC,aAAa,EACnB,KAAK,EAAE,UAAU,CAAC,mBAAmB,GACrC,CACH,CAAC,CAAC,CAAC,IAAI,EACP,UAAU,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAC9B,KAAC,WAAW,IACV,KAAK,EAAC,kBAAkB,EACxB,KAAK,EAAE,UAAU,CAAC,iBAAiB,EACnC,IAAI,SACJ,CACH,CAAC,CAAC,CAAC,IAAI,IACL,IACG,CACX,CAAC,CAAC,CAAC,IAAI,EAEP,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CACtB,mBAAS,SAAS,EAAC,4DAA4D,aAC7E,KAAC,iBAAiB,IAAC,SAAS,EAAC,yCAAyC,GAAG,EACzE,eAAK,SAAS,EAAC,SAAS,aACtB,aAAI,SAAS,EAAC,wCAAwC,wCAEjD,EACL,YAAG,SAAS,EAAC,gDAAgD,YAC1D,UAAU,CAAC,SAAS,GACnB,IACA,IACE,CACX,CAAC,CAAC,CAAC,IAAI,EAER,mBAAS,SAAS,EAAC,eAAe,aAChC,eAAK,SAAS,EAAC,yCAAyC,aACtD,aAAI,SAAS,EAAC,uCAAuC,0BAAe,EACnE,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CACjB,MAAC,KAAK,IAAC,OAAO,EAAC,SAAS,aAAE,IAAI,CAAC,MAAM,cAAe,CACrD,CAAC,CAAC,CAAC,IAAI,IACJ,EACN,cAAK,SAAS,EAAC,MAAM,YAClB,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,CACrB,cAAK,SAAS,EAAC,iCAAiC,YAC7C,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,CAC3C,eAAiB,SAAS,EAAC,WAAW,aACpC,KAAC,QAAQ,IAAC,SAAS,EAAC,WAAW,GAAG,EAClC,KAAC,QAAQ,IAAC,SAAS,EAAC,WAAW,GAAG,KAF1B,KAAK,CAGT,CACP,CAAC,GACE,CACP,CAAC,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,CACtB,KAAC,gBAAgB,IACf,KAAK,EAAE,SAAS,CAAC,KAAK,EACtB,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,SAAS,CAAC,OAAO,EAAE,GACvC,CACH,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CACtB,YAAG,SAAS,EAAC,mEAAmE,2CAE5E,CACL,CAAC,CAAC,CAAC,CACF,aAAI,SAAS,EAAC,4BAA4B,YACvC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;wCAChB,MAAM,SAAS,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;wCACpC,MAAM,QAAQ,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;wCACrC,OAAO,CACL,cAAiB,SAAS,EAAC,eAAe,aACxC,eAAK,SAAS,EAAC,2CAA2C,aACxD,KAAC,KAAK,IAAC,OAAO,EAAE,gBAAgB,CAAC,GAAG,CAAC,MAAM,CAAC,YACzC,GAAG,CAAC,MAAM,GACL,EACR,eAAM,SAAS,EAAC,uBAAuB,YACpC,eAAe,CAAC,GAAG,CAAC,SAAS,CAAC,GAC1B,EACN,QAAQ,CAAC,CAAC,CAAC,CACV,gBAAM,SAAS,EAAC,8DAA8D,aAC5E,KAAC,SAAS,IAAC,SAAS,EAAC,UAAU,GAAG,EACjC,QAAQ,IACJ,CACR,CAAC,CAAC,CAAC,IAAI,EACP,SAAS,CAAC,CAAC,CAAC,CACX,MAAC,IAAI,IACH,EAAE,EAAE,SAAS,EACb,SAAS,EAAC,+GAA+G,aAEzH,KAAC,gBAAgB,IAAC,SAAS,EAAC,UAAU,GAAG,kBAEpC,CACR,CAAC,CAAC,CAAC,IAAI,IACJ,EACL,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CACX,YAAG,SAAS,EAAC,sCAAsC,YAChD,GAAG,CAAC,KAAK,GACR,CACL,CAAC,CAAC,CAAC,IAAI,KA5BD,GAAG,CAAC,EAAE,CA6BV,CACN,CAAC;oCACJ,CAAC,CAAC,GACC,CACN,GACG,IACE,IACN,EAEN,kBAAQ,SAAS,EAAC,gDAAgD,aAChE,MAAC,MAAM,IACL,IAAI,EAAC,QAAQ,EACb,OAAO,EAAC,SAAS,EACjB,IAAI,EAAC,IAAI,EACT,OAAO,EAAE,QAAQ,EACjB,QAAQ,EAAE,UAAU,CAAC,SAAS,KAAK,KAAK,IAAI,UAAU,aAErD,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,KAAC,eAAe,KAAG,CAAC,CAAC,CAAC,KAAC,cAAc,KAAG,EAC7D,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,IACjC,EACR,UAAU,CAAC,SAAS,KAAK,KAAK,CAAC,CAAC,CAAC,CAChC,eAAM,SAAS,EAAC,+BAA+B,0BAAiB,CACjE,CAAC,CAAC,CAAC,IAAI,IACD,IACH,CACT,CAAC;AACJ,CAAC","sourcesContent":["import { useActionQuery } from \"@agent-native/core/client/hooks\";\nimport {\n IconAlertTriangle,\n IconClock,\n IconExternalLink,\n IconPlayerPause,\n IconPlayerPlay,\n} from \"@tabler/icons-react\";\nimport { Link } from \"react-router\";\n\nimport {\n automationScopeLabel,\n automationStatus,\n automationTarget,\n} from \"../lib/automation-display\";\nimport {\n automationRunScope,\n type DispatchAutomationItem,\n} from \"../lib/automations\";\nimport { ActionQueryError } from \"./action-query-error\";\nimport { Badge } from \"./ui/badge\";\nimport { Button } from \"./ui/button\";\nimport { Skeleton } from \"./ui/skeleton\";\n\ninterface AutomationRun {\n id: string;\n runId: string | null;\n threadId: string | null;\n status: \"running\" | \"success\" | \"error\" | \"interrupted\";\n startedAt: number;\n finishedAt: number | null;\n error: string | null;\n}\n\nexport interface AutomationDetailsPanelProps {\n automation: DispatchAutomationItem;\n isToggling?: boolean;\n onToggle: () => void;\n}\n\nfunction formatTimestamp(value: number | string | null | undefined): string {\n if (value == null) return \"Not recorded\";\n const date = new Date(value);\n return Number.isNaN(date.getTime()) ? \"Unknown time\" : date.toLocaleString();\n}\n\nfunction formatDuration(run: AutomationRun): string | null {\n if (!run.finishedAt) return null;\n const seconds = Math.max(\n 0,\n Math.round((run.finishedAt - run.startedAt) / 1000),\n );\n return `${seconds}s`;\n}\n\nfunction runDebugPath(run: AutomationRun): string | null {\n const params = new URLSearchParams();\n if (run.runId) params.set(\"runId\", run.runId);\n else if (run.threadId) params.set(\"threadId\", run.threadId);\n else return null;\n return `/admin/thread-debug?${params.toString()}`;\n}\n\nfunction runStatusVariant(\n status: AutomationRun[\"status\"],\n): \"default\" | \"destructive\" | \"outline\" {\n if (status === \"error\") return \"destructive\";\n if (status === \"success\") return \"default\";\n return \"outline\";\n}\n\nfunction DetailField({\n label,\n value,\n mono = false,\n}: {\n label: string;\n value: string;\n mono?: boolean;\n}) {\n return (\n <div className=\"min-w-0\">\n <dt className=\"text-[11px] uppercase tracking-wide text-muted-foreground\">\n {label}\n </dt>\n <dd\n className={\n mono\n ? \"mt-1 break-words font-mono text-xs text-foreground\"\n : \"mt-1 break-words text-sm text-foreground\"\n }\n >\n {value}\n </dd>\n </div>\n );\n}\n\nfunction runAsLabel(value: DispatchAutomationItem[\"runAs\"]): string {\n if (value === \"creator\") return \"Creator identity\";\n if (value === \"shared\") return \"Shared identity\";\n return \"Default identity\";\n}\n\nexport function AutomationDetailsPanel({\n automation,\n isToggling = false,\n onToggle,\n}: AutomationDetailsPanelProps) {\n const runsQuery = useActionQuery<AutomationRun[]>(\n \"list-automation-runs\",\n {\n name: automation.name,\n scope: automationRunScope(automation),\n appId: automation.appId ?? \"dispatch\",\n },\n { staleTime: 5_000 },\n );\n const runs = runsQuery.data ?? [];\n const status = automationStatus(automation);\n const isScheduled = automation.triggerType !== \"event\";\n const prompt = automation.body?.trim();\n const hasExecutionTarget = Boolean(\n automation.executionHostId ||\n automation.executionEngine ||\n automation.executionCwd,\n );\n const hasDelivery = Boolean(\n automation.deliveryPlatform ||\n automation.deliveryDestination ||\n automation.deliveryThreadRef,\n );\n\n return (\n <aside className=\"flex min-h-[34rem] min-w-0 flex-col overflow-hidden rounded-lg border bg-card\">\n <header className=\"flex items-start justify-between gap-3 border-b p-5\">\n <div className=\"min-w-0\">\n <div className=\"flex items-center gap-2\">\n <span\n className={\n status.tone === \"danger\"\n ? \"size-2 shrink-0 rounded-full bg-destructive\"\n : status.tone === \"default\"\n ? \"size-2 shrink-0 rounded-full bg-primary\"\n : \"size-2 shrink-0 rounded-full bg-muted-foreground/40\"\n }\n />\n <Badge\n variant={status.tone === \"danger\" ? \"destructive\" : \"outline\"}\n className=\"h-5\"\n >\n {status.label}\n </Badge>\n </div>\n <h2 className=\"mt-2 break-words text-base font-semibold text-foreground\">\n {automation.name}\n </h2>\n <p className=\"mt-1 text-xs text-muted-foreground\">\n {isScheduled ? \"Schedule\" : \"Event\"} ·{\" \"}\n {automationScopeLabel(automation)}\n </p>\n </div>\n </header>\n\n <div className=\"min-h-0 flex-1 space-y-5 overflow-y-auto p-5\">\n <section>\n <h3 className=\"text-sm font-semibold text-foreground\">Prompt</h3>\n {prompt ? (\n <pre className=\"mt-2 max-h-64 overflow-auto whitespace-pre-wrap break-words rounded-md bg-muted/40 p-3 font-sans text-sm leading-6 text-foreground\">\n {prompt}\n </pre>\n ) : (\n <p className=\"mt-2 rounded-md border border-dashed p-3 text-sm text-muted-foreground\">\n No prompt recorded.\n </p>\n )}\n </section>\n\n <section className=\"border-t pt-5\">\n <h3 className=\"text-sm font-semibold text-foreground\">\n Configuration\n </h3>\n <dl className=\"mt-3 grid grid-cols-2 gap-x-4 gap-y-4\">\n <DetailField\n label=\"Trigger\"\n value={isScheduled ? \"Scheduled\" : \"Event-triggered\"}\n />\n <DetailField\n label={isScheduled ? \"Schedule\" : \"Event\"}\n value={automationTarget(automation)}\n />\n {isScheduled ? (\n <>\n {automation.schedule ? (\n <DetailField\n label=\"Cron expression\"\n value={automation.schedule}\n mono\n />\n ) : null}\n <DetailField\n label=\"Timezone\"\n value={automation.timezone || \"Account default\"}\n />\n </>\n ) : null}\n <DetailField\n label=\"Condition\"\n value={automation.condition || \"Always\"}\n />\n <DetailField\n label=\"Domain\"\n value={automation.domain || \"Default\"}\n />\n <DetailField\n label=\"Model\"\n value={automation.model || \"Default model\"}\n />\n <DetailField label=\"Mode\" value={automation.mode || \"Agentic\"} />\n <DetailField label=\"Runs as\" value={runAsLabel(automation.runAs)} />\n <DetailField\n label=\"Next run\"\n value={\n automation.enabled\n ? formatTimestamp(automation.nextRun)\n : \"Paused\"\n }\n />\n <DetailField\n label=\"Last run\"\n value={formatTimestamp(automation.lastRun)}\n />\n <DetailField\n label=\"Last checked\"\n value={formatTimestamp(automation.lastCheck)}\n />\n <DetailField\n label=\"Created by\"\n value={automation.createdBy || \"Unknown\"}\n />\n </dl>\n </section>\n\n <section className=\"border-t pt-5\">\n <h3 className=\"text-sm font-semibold text-foreground\">\n Capabilities\n </h3>\n {automation.mcpTools && automation.mcpTools.length > 0 ? (\n <div className=\"mt-2 flex flex-wrap gap-1.5\">\n {automation.mcpTools.map((toolName) => (\n <code\n key={toolName}\n className=\"max-w-full break-all rounded bg-muted px-1.5 py-1 text-[11px] text-muted-foreground\"\n >\n {toolName}\n </code>\n ))}\n </div>\n ) : (\n <p className=\"mt-2 text-sm text-muted-foreground\">\n No explicit tool allowlist.\n </p>\n )}\n </section>\n\n {hasExecutionTarget ? (\n <section className=\"border-t pt-5\">\n <h3 className=\"text-sm font-semibold text-foreground\">\n Execution target\n </h3>\n <dl className=\"mt-3 grid grid-cols-2 gap-x-4 gap-y-4\">\n <DetailField\n label=\"Host\"\n value={automation.executionHostId || \"Server default\"}\n mono={Boolean(automation.executionHostId)}\n />\n <DetailField\n label=\"Engine\"\n value={automation.executionEngine || \"Default\"}\n />\n {automation.executionCwd ? (\n <DetailField\n label=\"Working directory\"\n value={automation.executionCwd}\n mono\n />\n ) : null}\n </dl>\n </section>\n ) : null}\n\n {hasDelivery ? (\n <section className=\"border-t pt-5\">\n <h3 className=\"text-sm font-semibold text-foreground\">Delivery</h3>\n <dl className=\"mt-3 grid grid-cols-2 gap-x-4 gap-y-4\">\n {automation.deliveryPlatform ? (\n <DetailField\n label=\"Platform\"\n value={automation.deliveryPlatform}\n />\n ) : null}\n {automation.deliveryDestination ? (\n <DetailField\n label=\"Destination\"\n value={automation.deliveryDestination}\n />\n ) : null}\n {automation.deliveryThreadRef ? (\n <DetailField\n label=\"Thread reference\"\n value={automation.deliveryThreadRef}\n mono\n />\n ) : null}\n </dl>\n </section>\n ) : null}\n\n {automation.lastError ? (\n <section className=\"flex items-start gap-2 border-t border-destructive/30 pt-5\">\n <IconAlertTriangle className=\"mt-0.5 size-4 shrink-0 text-destructive\" />\n <div className=\"min-w-0\">\n <h3 className=\"text-sm font-semibold text-destructive\">\n Latest scheduler result\n </h3>\n <p className=\"mt-1 break-words text-sm text-muted-foreground\">\n {automation.lastError}\n </p>\n </div>\n </section>\n ) : null}\n\n <section className=\"border-t pt-5\">\n <div className=\"flex items-center justify-between gap-3\">\n <h3 className=\"text-sm font-semibold text-foreground\">Past runs</h3>\n {runs.length > 0 ? (\n <Badge variant=\"outline\">{runs.length} shown</Badge>\n ) : null}\n </div>\n <div className=\"mt-3\">\n {runsQuery.isLoading ? (\n <div className=\"space-y-3 rounded-md border p-3\">\n {Array.from({ length: 3 }).map((_, index) => (\n <div key={index} className=\"space-y-2\">\n <Skeleton className=\"h-4 w-2/3\" />\n <Skeleton className=\"h-3 w-1/2\" />\n </div>\n ))}\n </div>\n ) : runsQuery.isError ? (\n <ActionQueryError\n error={runsQuery.error}\n onRetry={() => void runsQuery.refetch()}\n />\n ) : runs.length === 0 ? (\n <p className=\"rounded-md border border-dashed p-3 text-sm text-muted-foreground\">\n No execution recorded yet.\n </p>\n ) : (\n <ul className=\"divide-y rounded-md border\">\n {runs.map((run) => {\n const debugPath = runDebugPath(run);\n const duration = formatDuration(run);\n return (\n <li key={run.id} className=\"space-y-2 p-3\">\n <div className=\"flex flex-wrap items-center gap-2 text-sm\">\n <Badge variant={runStatusVariant(run.status)}>\n {run.status}\n </Badge>\n <span className=\"text-muted-foreground\">\n {formatTimestamp(run.startedAt)}\n </span>\n {duration ? (\n <span className=\"inline-flex items-center gap-1 text-xs text-muted-foreground\">\n <IconClock className=\"size-3.5\" />\n {duration}\n </span>\n ) : null}\n {debugPath ? (\n <Link\n to={debugPath}\n className=\"ml-auto inline-flex items-center gap-1 text-xs font-medium text-foreground underline-offset-4 hover:underline\"\n >\n <IconExternalLink className=\"size-3.5\" />\n Open debug\n </Link>\n ) : null}\n </div>\n {run.error ? (\n <p className=\"break-words text-sm text-destructive\">\n {run.error}\n </p>\n ) : null}\n </li>\n );\n })}\n </ul>\n )}\n </div>\n </section>\n </div>\n\n <footer className=\"flex flex-wrap items-center gap-2 border-t p-4\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={onToggle}\n disabled={automation.canUpdate === false || isToggling}\n >\n {automation.enabled ? <IconPlayerPause /> : <IconPlayerPlay />}\n {automation.enabled ? \"Pause\" : \"Resume\"}\n </Button>\n {automation.canUpdate === false ? (\n <span className=\"text-xs text-muted-foreground\">Read-only</span>\n ) : null}\n </footer>\n </aside>\n );\n}\n"]}
|
|
@@ -4,6 +4,7 @@ export interface DispatchAutomationItem {
|
|
|
4
4
|
path: string;
|
|
5
5
|
owner: string;
|
|
6
6
|
appId?: string;
|
|
7
|
+
orgId?: string;
|
|
7
8
|
scope?: "personal" | "organization";
|
|
8
9
|
canUpdate?: boolean;
|
|
9
10
|
triggerType?: "schedule" | "event" | string;
|
|
@@ -21,7 +22,18 @@ export interface DispatchAutomationItem {
|
|
|
21
22
|
lastError?: string;
|
|
22
23
|
nextRun?: string;
|
|
23
24
|
createdBy?: string;
|
|
25
|
+
body?: string;
|
|
26
|
+
model?: string;
|
|
27
|
+
mcpTools?: string[];
|
|
28
|
+
runAs?: "creator" | "shared";
|
|
29
|
+
executionHostId?: string;
|
|
30
|
+
executionEngine?: string;
|
|
31
|
+
executionCwd?: string;
|
|
32
|
+
deliveryPlatform?: string;
|
|
33
|
+
deliveryDestination?: string;
|
|
34
|
+
deliveryThreadRef?: string;
|
|
24
35
|
}
|
|
36
|
+
export declare function automationRunScope(automation: Pick<DispatchAutomationItem, "owner" | "orgId" | "scope">): "personal" | "organization";
|
|
25
37
|
export interface SetDispatchAutomationEnabledInput {
|
|
26
38
|
owner: string;
|
|
27
39
|
path: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"automations.d.ts","sourceRoot":"","sources":["../../src/lib/automations.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,sBAAsB;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,UAAU,GAAG,cAAc,CAAC;IACpC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,UAAU,GAAG,OAAO,GAAG,MAAM,CAAC;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"automations.d.ts","sourceRoot":"","sources":["../../src/lib/automations.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,sBAAsB;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,UAAU,GAAG,cAAc,CAAC;IACpC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,UAAU,GAAG,OAAO,GAAG,MAAM,CAAC;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,KAAK,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC;IAC7B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,IAAI,CAAC,sBAAsB,EAAE,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC,GACpE,UAAU,GAAG,cAAc,CAQ7B;AAED,MAAM,WAAW,iCAAiC;IAChD,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;CAClB;AAcD,wBAAsB,uBAAuB,IAAI,OAAO,CACtD,sBAAsB,EAAE,CACzB,CAOA;AAED,wBAAsB,4BAA4B,CAChD,KAAK,EAAE,iCAAiC,GACvC,OAAO,CAAC,sBAAsB,CAAC,CAOjC"}
|
package/dist/lib/automations.js
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
import { agentNativePath } from "@agent-native/core/client/api-path";
|
|
2
|
+
export function automationRunScope(automation) {
|
|
3
|
+
if (automation.owner === "__shared__" && !automation.orgId) {
|
|
4
|
+
return "personal";
|
|
5
|
+
}
|
|
6
|
+
return automation.scope === "organization" ||
|
|
7
|
+
automation.owner.startsWith("__organization__:")
|
|
8
|
+
? "organization"
|
|
9
|
+
: "personal";
|
|
10
|
+
}
|
|
2
11
|
async function readAutomationResponse(response) {
|
|
3
12
|
const data = await response.json().catch(() => null);
|
|
4
13
|
if (!response.ok) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"automations.js","sourceRoot":"","sources":["../../src/lib/automations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAC;
|
|
1
|
+
{"version":3,"file":"automations.js","sourceRoot":"","sources":["../../src/lib/automations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAC;AAsCrE,MAAM,UAAU,kBAAkB,CAChC,UAAqE;IAErE,IAAI,UAAU,CAAC,KAAK,KAAK,YAAY,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;QAC3D,OAAO,UAAU,CAAC;IACpB,CAAC;IACD,OAAO,UAAU,CAAC,KAAK,KAAK,cAAc;QACxC,UAAU,CAAC,KAAK,CAAC,UAAU,CAAC,mBAAmB,CAAC;QAChD,CAAC,CAAC,cAAc;QAChB,CAAC,CAAC,UAAU,CAAC;AACjB,CAAC;AAQD,KAAK,UAAU,sBAAsB,CAAI,QAAkB;IACzD,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IACrD,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,OAAO,GACX,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,OAAO,IAAI,IAAI;YACjD,CAAC,CAAC,MAAM,CAAE,IAA4B,CAAC,KAAK,CAAC;YAC7C,CAAC,CAAC,2BAA2B,CAAC;QAClC,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC;IAC3B,CAAC;IACD,OAAO,IAAS,CAAC;AACnB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,uBAAuB;IAG3C,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,eAAe,CAAC,4BAA4B,CAAC,CAAC,CAAC;IAC5E,MAAM,IAAI,GAAG,MAAM,sBAAsB,CAAU,QAAQ,CAAC,CAAC;IAC7D,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;IAClE,CAAC;IACD,OAAO,IAAgC,CAAC;AAC1C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAChD,KAAwC;IAExC,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,eAAe,CAAC,4BAA4B,CAAC,EAAE;QAC1E,MAAM,EAAE,OAAO;QACf,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC;KAC5B,CAAC,CAAC;IACH,OAAO,sBAAsB,CAAyB,QAAQ,CAAC,CAAC;AAClE,CAAC","sourcesContent":["import { agentNativePath } from \"@agent-native/core/client/api-path\";\n\nexport interface DispatchAutomationItem {\n id: string;\n name: string;\n path: string;\n owner: string;\n appId?: string;\n orgId?: string;\n scope?: \"personal\" | \"organization\";\n canUpdate?: boolean;\n triggerType?: \"schedule\" | \"event\" | string;\n event?: string;\n schedule?: string;\n scheduleDescription?: string;\n condition?: string;\n mode?: string;\n domain?: string;\n enabled?: boolean;\n timezone?: string;\n lastStatus?: string;\n lastRun?: string;\n lastCheck?: string;\n lastError?: string;\n nextRun?: string;\n createdBy?: string;\n body?: string;\n model?: string;\n mcpTools?: string[];\n runAs?: \"creator\" | \"shared\";\n executionHostId?: string;\n executionEngine?: string;\n executionCwd?: string;\n deliveryPlatform?: string;\n deliveryDestination?: string;\n deliveryThreadRef?: string;\n}\n\nexport function automationRunScope(\n automation: Pick<DispatchAutomationItem, \"owner\" | \"orgId\" | \"scope\">,\n): \"personal\" | \"organization\" {\n if (automation.owner === \"__shared__\" && !automation.orgId) {\n return \"personal\";\n }\n return automation.scope === \"organization\" ||\n automation.owner.startsWith(\"__organization__:\")\n ? \"organization\"\n : \"personal\";\n}\n\nexport interface SetDispatchAutomationEnabledInput {\n owner: string;\n path: string;\n enabled: boolean;\n}\n\nasync function readAutomationResponse<T>(response: Response): Promise<T> {\n const data = await response.json().catch(() => null);\n if (!response.ok) {\n const message =\n data && typeof data === \"object\" && \"error\" in data\n ? String((data as { error?: unknown }).error)\n : \"Automation request failed\";\n throw new Error(message);\n }\n return data as T;\n}\n\nexport async function listDispatchAutomations(): Promise<\n DispatchAutomationItem[]\n> {\n const response = await fetch(agentNativePath(\"/_agent-native/automations\"));\n const rows = await readAutomationResponse<unknown>(response);\n if (!Array.isArray(rows)) {\n throw new Error(\"Automation list returned an invalid response\");\n }\n return rows as DispatchAutomationItem[];\n}\n\nexport async function setDispatchAutomationEnabled(\n input: SetDispatchAutomationEnabledInput,\n): Promise<DispatchAutomationItem> {\n const response = await fetch(agentNativePath(\"/_agent-native/automations\"), {\n method: \"PATCH\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify(input),\n });\n return readAutomationResponse<DispatchAutomationItem>(response);\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"admin.d.ts","sourceRoot":"","sources":["../../../src/routes/pages/admin.tsx"],"names":[],"mappings":"AAIA,wBAAgB,IAAI;;IAEnB;AAED,MAAM,CAAC,OAAO,UAAU,UAAU,
|
|
1
|
+
{"version":3,"file":"admin.d.ts","sourceRoot":"","sources":["../../../src/routes/pages/admin.tsx"],"names":[],"mappings":"AAIA,wBAAgB,IAAI;;IAEnB;AAED,MAAM,CAAC,OAAO,UAAU,UAAU,oDAajC"}
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { Outlet } from "react-router";
|
|
2
|
+
import { Outlet, useLocation } from "react-router";
|
|
3
3
|
import { AdminShell } from "../../components/admin-navigation.js";
|
|
4
4
|
export function meta() {
|
|
5
5
|
return [{ title: "Admin — Dispatch" }];
|
|
6
6
|
}
|
|
7
7
|
export default function AdminRoute() {
|
|
8
|
+
const location = useLocation();
|
|
9
|
+
const isAutomationsRoute = location.pathname === "/admin/automations" ||
|
|
10
|
+
location.pathname.startsWith("/admin/automations/");
|
|
11
|
+
if (isAutomationsRoute)
|
|
12
|
+
return _jsx(Outlet, {});
|
|
8
13
|
return (_jsx(AdminShell, { children: _jsx(Outlet, {}) }));
|
|
9
14
|
}
|
|
10
15
|
//# sourceMappingURL=admin.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"admin.js","sourceRoot":"","sources":["../../../src/routes/pages/admin.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"admin.js","sourceRoot":"","sources":["../../../src/routes/pages/admin.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEnD,OAAO,EAAE,UAAU,EAAE,MAAM,mCAAmC,CAAC;AAE/D,MAAM,UAAU,IAAI;IAClB,OAAO,CAAC,EAAE,KAAK,EAAE,kBAAkB,EAAE,CAAC,CAAC;AACzC,CAAC;AAED,MAAM,CAAC,OAAO,UAAU,UAAU;IAChC,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;IAC/B,MAAM,kBAAkB,GACtB,QAAQ,CAAC,QAAQ,KAAK,oBAAoB;QAC1C,QAAQ,CAAC,QAAQ,CAAC,UAAU,CAAC,qBAAqB,CAAC,CAAC;IAEtD,IAAI,kBAAkB;QAAE,OAAO,KAAC,MAAM,KAAG,CAAC;IAE1C,OAAO,CACL,KAAC,UAAU,cACT,KAAC,MAAM,KAAG,GACC,CACd,CAAC;AACJ,CAAC","sourcesContent":["import { Outlet, useLocation } from \"react-router\";\n\nimport { AdminShell } from \"../../components/admin-navigation\";\n\nexport function meta() {\n return [{ title: \"Admin — Dispatch\" }];\n}\n\nexport default function AdminRoute() {\n const location = useLocation();\n const isAutomationsRoute =\n location.pathname === \"/admin/automations\" ||\n location.pathname.startsWith(\"/admin/automations/\");\n\n if (isAutomationsRoute) return <Outlet />;\n\n return (\n <AdminShell>\n <Outlet />\n </AdminShell>\n );\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"automations.d.ts","sourceRoot":"","sources":["../../../src/routes/pages/automations.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"automations.d.ts","sourceRoot":"","sources":["../../../src/routes/pages/automations.tsx"],"names":[],"mappings":"AAyDA,wBAAgB,IAAI;;IAEnB;AAmID,MAAM,CAAC,OAAO,UAAU,gBAAgB,oDA4PvC"}
|
|
@@ -2,15 +2,16 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { sendToAgentChat } from "@agent-native/core/client/agent-chat";
|
|
3
3
|
import { PromptComposer } from "@agent-native/core/client/composer";
|
|
4
4
|
import { useChangeVersions } from "@agent-native/core/client/hooks";
|
|
5
|
-
import { IconFileSearch, IconListDetails, IconPlus, IconSettingsAutomation, } from "@tabler/icons-react";
|
|
5
|
+
import { IconFileSearch, IconListDetails, IconPlus, IconSearch, IconSettingsAutomation, } from "@tabler/icons-react";
|
|
6
6
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
7
|
-
import { useMemo, useState } from "react";
|
|
7
|
+
import { useEffect, useMemo, useState } from "react";
|
|
8
8
|
import { Link } from "react-router";
|
|
9
9
|
import { toast } from "sonner";
|
|
10
|
-
import {
|
|
10
|
+
import { AutomationDetailsPanel } from "../../components/automation-details-panel.js";
|
|
11
11
|
import { DispatchShell } from "../../components/dispatch-shell.js";
|
|
12
12
|
import { Badge } from "../../components/ui/badge.js";
|
|
13
13
|
import { Button } from "../../components/ui/button.js";
|
|
14
|
+
import { Input } from "../../components/ui/input.js";
|
|
14
15
|
import { Popover, PopoverContent, PopoverTrigger, } from "../../components/ui/popover.js";
|
|
15
16
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "../../components/ui/select.js";
|
|
16
17
|
import { Skeleton } from "../../components/ui/skeleton.js";
|
|
@@ -86,6 +87,7 @@ function CreateAutomationButton() {
|
|
|
86
87
|
}
|
|
87
88
|
export default function AutomationsRoute() {
|
|
88
89
|
const [view, setView] = useState("dispatch");
|
|
90
|
+
const [query, setQuery] = useState("");
|
|
89
91
|
const [detailsTarget, setDetailsTarget] = useState(null);
|
|
90
92
|
const automationsQuery = useAutomations();
|
|
91
93
|
const toggleAutomation = useToggleAutomation();
|
|
@@ -94,6 +96,24 @@ export default function AutomationsRoute() {
|
|
|
94
96
|
? automations
|
|
95
97
|
: automations.filter((item) => belongsToDispatch(item)), [automations, view]);
|
|
96
98
|
const ordered = useMemo(() => sortAutomations(visibleAutomations), [visibleAutomations]);
|
|
99
|
+
const filtered = useMemo(() => {
|
|
100
|
+
const normalized = query.trim().toLowerCase();
|
|
101
|
+
if (!normalized)
|
|
102
|
+
return ordered;
|
|
103
|
+
return ordered.filter((item) => [
|
|
104
|
+
item.name,
|
|
105
|
+
item.event,
|
|
106
|
+
item.schedule,
|
|
107
|
+
item.scheduleDescription,
|
|
108
|
+
item.body,
|
|
109
|
+
item.model,
|
|
110
|
+
item.domain,
|
|
111
|
+
]
|
|
112
|
+
.filter(Boolean)
|
|
113
|
+
.join(" ")
|
|
114
|
+
.toLowerCase()
|
|
115
|
+
.includes(normalized));
|
|
116
|
+
}, [ordered, query]);
|
|
97
117
|
const enabledCount = visibleAutomations.filter((item) => item.enabled).length;
|
|
98
118
|
const errorCount = visibleAutomations.filter((item) => item.enabled &&
|
|
99
119
|
(item.lastStatus === "error" || item.lastStatus === "skipped")).length;
|
|
@@ -102,23 +122,41 @@ export default function AutomationsRoute() {
|
|
|
102
122
|
? automationIdentity(toggleAutomation.variables)
|
|
103
123
|
: null
|
|
104
124
|
: null;
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
125
|
+
useEffect(() => {
|
|
126
|
+
if (!detailsTarget)
|
|
127
|
+
return;
|
|
128
|
+
const current = filtered.find((item) => automationIdentity(item) === automationIdentity(detailsTarget));
|
|
129
|
+
if (current) {
|
|
130
|
+
if (current !== detailsTarget)
|
|
131
|
+
setDetailsTarget(current);
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
setDetailsTarget(null);
|
|
135
|
+
}
|
|
136
|
+
}, [detailsTarget, filtered]);
|
|
137
|
+
return (_jsx(DispatchShell, { title: "Automations", description: "See scheduled and event-triggered jobs, inspect their checks and past runs, pause them, or ask the agent to create one.", children: _jsxs("section", { className: "flex flex-col gap-4", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [_jsxs("div", { className: "flex min-w-0 flex-1 flex-wrap items-center gap-3", children: [_jsxs("div", { className: "relative w-full max-w-sm", children: [_jsx(IconSearch, { size: 15, "aria-hidden": "true", className: "pointer-events-none absolute left-2.5 top-1/2 -translate-y-1/2 text-muted-foreground" }), _jsx(Input, { value: query, onChange: (event) => setQuery(event.target.value), placeholder: "Search automations", "aria-label": "Search automations", className: "h-8 pl-8 text-xs" })] }), _jsxs("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [_jsx(IconSettingsAutomation, { size: 16, className: "shrink-0" }), _jsxs("span", { children: [enabledCount, " enabled", errorCount > 0 ? ` · ${errorCount} errors` : ""] })] })] }), _jsxs("div", { className: "flex flex-wrap items-center justify-end gap-2", children: [_jsxs(Select, { value: view, onValueChange: (value) => {
|
|
138
|
+
if (value === "dispatch" || value === "all")
|
|
139
|
+
setView(value);
|
|
140
|
+
}, children: [_jsx(SelectTrigger, { className: "h-8 w-[10.5rem] text-xs", "aria-label": "Automation view", children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "dispatch", children: "Dispatch automations" }), _jsx(SelectItem, { value: "all", children: "All apps" })] })] }), _jsx(CreateAutomationButton, {})] })] }), _jsxs("div", { className: "grid gap-4 lg:grid-cols-[minmax(0,1fr)_minmax(24rem,32rem)]", children: [_jsx("div", { className: "min-w-0 divide-y rounded-lg bg-card", children: automationsQuery.isLoading && ordered.length === 0 ? (Array.from({ length: 4 }).map((_, index) => (_jsxs("div", { className: "px-4 py-3", children: [_jsx(Skeleton, { className: "h-4 w-40" }), _jsx(Skeleton, { className: "mt-2 h-3 w-28" })] }, index)))) : filtered.length > 0 ? (filtered.map((item) => {
|
|
141
|
+
const status = automationStatus(item);
|
|
142
|
+
const canUpdate = item.canUpdate !== false;
|
|
143
|
+
const isToggling = pendingToggleIdentity === automationIdentity(item);
|
|
144
|
+
const isSelected = detailsTarget !== null &&
|
|
145
|
+
automationIdentity(detailsTarget) ===
|
|
146
|
+
automationIdentity(item);
|
|
147
|
+
return (_jsxs("div", { className: cn("grid grid-cols-[minmax(0,1fr)_auto] gap-3 px-4 py-3 transition-colors", isSelected && "bg-accent/40"), children: [_jsxs("div", { className: "min-w-0", children: [_jsxs("button", { type: "button", className: "w-full min-w-0 cursor-pointer text-left outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-card", "aria-pressed": isSelected, onClick: () => setDetailsTarget(item), children: [_jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [_jsx(StatusDot, { tone: status.tone }), _jsx("span", { className: "truncate text-sm font-medium text-foreground", children: item.name })] }), _jsx("div", { className: "mt-1 truncate text-xs text-muted-foreground", children: automationTarget(item) }), _jsxs("div", { className: "mt-1 flex flex-wrap gap-x-3 gap-y-1 text-[11px] text-muted-foreground", children: [_jsxs("span", { children: ["Last ", automationLastRun(item)] }), _jsxs("span", { children: ["Next ", automationNextRun(item)] }), _jsx("span", { children: automationScopeLabel(item) }), item.lastCheck ? (_jsxs("span", { children: ["Checked ", automationLastCheck(item)] })) : null] })] }), item.lastError ? (_jsxs("div", { className: "mt-1 flex flex-wrap items-baseline gap-x-2 gap-y-1 text-xs text-destructive", children: [_jsx("span", { className: "min-w-0 break-words", children: item.lastError }), _jsxs(Link, { to: automationTroubleshootPath(item), className: "inline-flex shrink-0 items-center gap-1 font-medium text-foreground underline-offset-4 hover:underline", "aria-label": `Troubleshoot ${item.name} in Thread Debug`, children: [_jsx(IconFileSearch, { size: 13, "aria-hidden": "true" }), "Troubleshoot in Thread Debug"] })] })) : null] }), _jsxs("div", { className: "flex flex-col items-end gap-2", children: [_jsx(Badge, { variant: status.tone === "danger" ? "destructive" : "outline", className: "h-5", children: status.label }), _jsx(Switch, { checked: !!item.enabled, disabled: !canUpdate || isToggling, "aria-label": `${item.enabled ? "Disable" : "Enable"} automation ${item.name}`, onCheckedChange: (checked) => toggleAutomation.mutate({
|
|
148
|
+
owner: item.owner,
|
|
149
|
+
path: item.path,
|
|
150
|
+
enabled: checked,
|
|
151
|
+
}) })] })] }, item.id));
|
|
152
|
+
})) : (_jsx("div", { className: "px-4 py-10 text-center text-sm text-muted-foreground", children: query
|
|
153
|
+
? "No matching automations."
|
|
154
|
+
: view === "all"
|
|
155
|
+
? "No automations yet. Create one here, or ask Dispatch to set up a scheduled or event-triggered job."
|
|
156
|
+
: "No Dispatch automations yet. Switch to All apps to inspect workspace automations." })) }), detailsTarget ? (_jsx(AutomationDetailsPanel, { automation: detailsTarget, isToggling: pendingToggleIdentity === automationIdentity(detailsTarget), onToggle: () => toggleAutomation.mutate({
|
|
157
|
+
owner: detailsTarget.owner,
|
|
158
|
+
path: detailsTarget.path,
|
|
159
|
+
enabled: !detailsTarget.enabled,
|
|
160
|
+
}) })) : (_jsxs("div", { className: "flex min-h-[34rem] flex-col items-center justify-center rounded-lg border border-dashed bg-card p-6 text-center", children: [_jsx(IconListDetails, { size: 24, strokeWidth: 1.6, className: "text-muted-foreground" }), _jsx("p", { className: "mt-3 text-sm font-medium text-foreground", children: "Select an automation" }), _jsx("p", { className: "mt-1 max-w-xs text-sm text-muted-foreground", children: "Inspect its prompt, configuration, capabilities, and run history." })] }))] })] }) }));
|
|
123
161
|
}
|
|
124
162
|
//# sourceMappingURL=automations.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"automations.js","sourceRoot":"","sources":["../../../src/routes/pages/automations.tsx"],"names":[],"mappings":"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","sourcesContent":["import { sendToAgentChat } from \"@agent-native/core/client/agent-chat\";\nimport { PromptComposer } from \"@agent-native/core/client/composer\";\nimport { useChangeVersions } from \"@agent-native/core/client/hooks\";\nimport {\n IconFileSearch,\n IconListDetails,\n IconPlus,\n IconSettingsAutomation,\n} from \"@tabler/icons-react\";\nimport { useMutation, useQuery, useQueryClient } from \"@tanstack/react-query\";\nimport { useMemo, useState } from \"react\";\nimport { Link } from \"react-router\";\nimport { toast } from \"sonner\";\n\nimport { AutomationRunHistoryDialog } from \"../../components/automation-run-history-dialog\";\nimport { DispatchShell } from \"../../components/dispatch-shell\";\nimport { Badge } from \"../../components/ui/badge\";\nimport { Button } from \"../../components/ui/button\";\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from \"../../components/ui/popover\";\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"../../components/ui/select\";\nimport { Skeleton } from \"../../components/ui/skeleton\";\nimport { Switch } from \"../../components/ui/switch\";\nimport {\n automationIdentity,\n automationLastCheck,\n automationLastRun,\n automationNextRun,\n automationScopeLabel,\n automationStatus,\n automationTarget,\n automationTroubleshootPath,\n belongsToDispatch,\n sortAutomations,\n type AutomationStatusTone,\n} from \"../../lib/automation-display\";\nimport {\n listDispatchAutomations,\n setDispatchAutomationEnabled,\n type DispatchAutomationItem,\n type SetDispatchAutomationEnabledInput,\n} from \"../../lib/automations\";\nimport { cn } from \"../../lib/utils\";\n\nconst AUTOMATIONS_QUERY_KEY = [\"dispatch-automations\"] as const;\n\nexport function meta() {\n return [{ title: \"Automations — Dispatch\" }];\n}\n\nfunction StatusDot({ tone }: { tone: AutomationStatusTone }) {\n return (\n <span\n className={cn(\n \"size-2 shrink-0 rounded-full\",\n tone === \"success\" && \"bg-emerald-500\",\n tone === \"warning\" && \"bg-amber-500\",\n tone === \"danger\" && \"bg-destructive\",\n tone === \"muted\" && \"bg-muted-foreground/35\",\n tone === \"default\" && \"bg-primary\",\n )}\n />\n );\n}\n\nfunction useAutomations() {\n const version = useChangeVersions([\"action\", \"screen-refresh\"]);\n return useQuery<DispatchAutomationItem[]>({\n queryKey: [...AUTOMATIONS_QUERY_KEY, version],\n queryFn: listDispatchAutomations,\n placeholderData: (prev) => prev,\n staleTime: 5_000,\n });\n}\n\nfunction useToggleAutomation() {\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: setDispatchAutomationEnabled,\n onMutate: async (input: SetDispatchAutomationEnabledInput) => {\n await queryClient.cancelQueries({ queryKey: AUTOMATIONS_QUERY_KEY });\n const snapshots = queryClient.getQueriesData<DispatchAutomationItem[]>({\n queryKey: AUTOMATIONS_QUERY_KEY,\n });\n\n queryClient.setQueriesData<DispatchAutomationItem[]>(\n { queryKey: AUTOMATIONS_QUERY_KEY },\n (rows) =>\n rows?.map((item) =>\n automationIdentity(item) === automationIdentity(input)\n ? { ...item, enabled: input.enabled }\n : item,\n ),\n );\n\n return { snapshots };\n },\n onError: (err, _input, context) => {\n for (const [queryKey, data] of context?.snapshots ?? []) {\n queryClient.setQueryData(queryKey, data);\n }\n toast.error(\n `Could not update automation: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n },\n onSuccess: (updated) => {\n queryClient.setQueriesData<DispatchAutomationItem[]>(\n { queryKey: AUTOMATIONS_QUERY_KEY },\n (rows) =>\n rows?.map((item) =>\n automationIdentity(item) === automationIdentity(updated)\n ? updated\n : item,\n ),\n );\n },\n onSettled: () => {\n void queryClient.invalidateQueries({ queryKey: AUTOMATIONS_QUERY_KEY });\n },\n });\n}\n\nfunction CreateAutomationButton() {\n const [open, setOpen] = useState(false);\n const [scope, setScope] = useState<\"personal\" | \"organization\">(\"personal\");\n\n function handleSubmit(text: string) {\n const trimmed = text.trim();\n if (!trimmed) return;\n window.dispatchEvent(\n new CustomEvent(\"agent-panel:set-mode\", {\n detail: { mode: \"chat\" },\n }),\n );\n sendToAgentChat({\n message: trimmed,\n context: `The user wants to create a new automation. Scope: ${scope}. Use manage-automations with action=define to create it. Ask clarifying questions if needed about what event to trigger on, conditions, and what actions to take.`,\n submit: true,\n newTab: true,\n });\n setOpen(false);\n }\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n <Button size=\"sm\">\n <IconPlus size={14} />\n New automation\n </Button>\n </PopoverTrigger>\n <PopoverContent align=\"end\" className=\"w-[min(100vw-2rem,24rem)] p-3\">\n <p className=\"pb-2 text-sm font-semibold text-foreground\">\n New automation\n </p>\n <PromptComposer\n autoFocus\n placeholder=\"Describe what you want to automate...\"\n draftScope=\"dispatch-automations:create\"\n onSubmit={handleSubmit}\n />\n <select\n value={scope}\n onChange={(event) =>\n setScope(event.target.value as \"personal\" | \"organization\")\n }\n className=\"mt-2 w-full cursor-pointer rounded-md border border-input bg-background px-3 py-1.5 text-xs text-foreground\"\n >\n <option value=\"personal\">Personal</option>\n <option value=\"organization\">Organization</option>\n </select>\n </PopoverContent>\n </Popover>\n );\n}\n\nexport default function AutomationsRoute() {\n const [view, setView] = useState<\"dispatch\" | \"all\">(\"dispatch\");\n const [detailsTarget, setDetailsTarget] =\n useState<DispatchAutomationItem | null>(null);\n const automationsQuery = useAutomations();\n const toggleAutomation = useToggleAutomation();\n const automations = automationsQuery.data ?? [];\n const visibleAutomations = useMemo(\n () =>\n view === \"all\"\n ? automations\n : automations.filter((item) => belongsToDispatch(item)),\n [automations, view],\n );\n const ordered = useMemo(\n () => sortAutomations(visibleAutomations),\n [visibleAutomations],\n );\n const enabledCount = visibleAutomations.filter((item) => item.enabled).length;\n const errorCount = visibleAutomations.filter(\n (item) =>\n item.enabled &&\n (item.lastStatus === \"error\" || item.lastStatus === \"skipped\"),\n ).length;\n const pendingToggleIdentity = toggleAutomation.isPending\n ? toggleAutomation.variables\n ? automationIdentity(toggleAutomation.variables)\n : null\n : null;\n\n return (\n <DispatchShell\n title=\"Automations\"\n description=\"See scheduled and event-triggered jobs, inspect their checks and past runs, pause them, or ask the agent to create one.\"\n >\n <section className=\"flex flex-col gap-4\">\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <div className=\"flex min-w-0 items-center gap-2 text-sm text-muted-foreground\">\n <IconSettingsAutomation size={16} className=\"shrink-0\" />\n <span>\n {enabledCount} enabled\n {errorCount > 0 ? ` · ${errorCount} errors` : \"\"}\n </span>\n </div>\n <div className=\"flex flex-wrap items-center justify-end gap-2\">\n <Select\n value={view}\n onValueChange={(value) => {\n if (value === \"dispatch\" || value === \"all\") setView(value);\n }}\n >\n <SelectTrigger\n className=\"h-8 w-[10.5rem] text-xs\"\n aria-label=\"Automation view\"\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"dispatch\">Dispatch automations</SelectItem>\n <SelectItem value=\"all\">All apps</SelectItem>\n </SelectContent>\n </Select>\n <CreateAutomationButton />\n </div>\n </div>\n\n <div className=\"divide-y rounded-lg bg-card\">\n {automationsQuery.isLoading && ordered.length === 0 ? (\n Array.from({ length: 4 }).map((_, index) => (\n <div key={index} className=\"px-4 py-3\">\n <Skeleton className=\"h-4 w-40\" />\n <Skeleton className=\"mt-2 h-3 w-28\" />\n </div>\n ))\n ) : ordered.length > 0 ? (\n ordered.map((item) => {\n const status = automationStatus(item);\n const canUpdate = item.canUpdate !== false;\n const isToggling =\n pendingToggleIdentity === automationIdentity(item);\n return (\n <div\n key={item.id}\n className=\"grid grid-cols-[minmax(0,1fr)_auto] gap-3 px-4 py-3\"\n >\n <div className=\"min-w-0\">\n <div className=\"flex min-w-0 items-center gap-2\">\n <StatusDot tone={status.tone} />\n <span className=\"truncate text-sm font-medium text-foreground\">\n {item.name}\n </span>\n </div>\n <div className=\"mt-1 truncate text-xs text-muted-foreground\">\n {automationTarget(item)}\n </div>\n <div className=\"mt-1 flex flex-wrap gap-x-3 gap-y-1 text-[11px] text-muted-foreground\">\n <span>Last {automationLastRun(item)}</span>\n <span>Next {automationNextRun(item)}</span>\n <span>{automationScopeLabel(item)}</span>\n {item.lastCheck ? (\n <span>Checked {automationLastCheck(item)}</span>\n ) : null}\n </div>\n {item.lastError ? (\n <div className=\"mt-1 flex flex-wrap items-baseline gap-x-2 gap-y-1 text-xs text-destructive\">\n <span className=\"min-w-0 break-words\">\n {item.lastError}\n </span>\n <Link\n to={automationTroubleshootPath(item)}\n className=\"inline-flex shrink-0 items-center gap-1 font-medium text-foreground underline-offset-4 hover:underline\"\n aria-label={`Troubleshoot ${item.name} in Thread Debug`}\n >\n <IconFileSearch size={13} aria-hidden=\"true\" />\n Troubleshoot in Thread Debug\n </Link>\n </div>\n ) : null}\n </div>\n <div className=\"flex flex-col items-end gap-2\">\n <div className=\"flex items-center gap-2\">\n <Badge\n variant={\n status.tone === \"danger\" ? \"destructive\" : \"outline\"\n }\n className=\"h-5\"\n >\n {status.label}\n </Badge>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-7 px-2 text-xs\"\n onClick={() => setDetailsTarget(item)}\n >\n <IconListDetails size={14} />\n Details\n </Button>\n </div>\n <Switch\n checked={!!item.enabled}\n disabled={!canUpdate || isToggling}\n aria-label={`${item.enabled ? \"Disable\" : \"Enable\"} automation ${item.name}`}\n onCheckedChange={(checked) =>\n toggleAutomation.mutate({\n owner: item.owner,\n path: item.path,\n enabled: checked,\n })\n }\n />\n </div>\n </div>\n );\n })\n ) : (\n <div className=\"px-4 py-10 text-center text-sm text-muted-foreground\">\n {view === \"all\"\n ? \"No automations yet. Create one here, or ask Dispatch to set up a scheduled or event-triggered job.\"\n : \"No Dispatch automations yet. Switch to All apps to inspect workspace automations.\"}\n </div>\n )}\n </div>\n </section>\n <AutomationRunHistoryDialog\n automation={detailsTarget}\n open={detailsTarget !== null}\n onOpenChange={(open) => {\n if (!open) setDetailsTarget(null);\n }}\n />\n </DispatchShell>\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"automations.js","sourceRoot":"","sources":["../../../src/routes/pages/automations.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AACvE,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACpE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,EACL,cAAc,EACd,eAAe,EACf,QAAQ,EACR,UAAU,EACV,sBAAsB,GACvB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC9E,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACrD,OAAO,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AACpC,OAAO,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAE/B,OAAO,EAAE,sBAAsB,EAAE,MAAM,2CAA2C,CAAC;AACnF,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAChE,OAAO,EAAE,KAAK,EAAE,MAAM,2BAA2B,CAAC;AAClD,OAAO,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AACpD,OAAO,EAAE,KAAK,EAAE,MAAM,2BAA2B,CAAC;AAClD,OAAO,EACL,OAAO,EACP,cAAc,EACd,cAAc,GACf,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACL,MAAM,EACN,aAAa,EACb,UAAU,EACV,aAAa,EACb,WAAW,GACZ,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AACxD,OAAO,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AACpD,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,iBAAiB,EACjB,iBAAiB,EACjB,oBAAoB,EACpB,gBAAgB,EAChB,gBAAgB,EAChB,0BAA0B,EAC1B,iBAAiB,EACjB,eAAe,GAEhB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACL,uBAAuB,EACvB,4BAA4B,GAG7B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,EAAE,EAAE,MAAM,iBAAiB,CAAC;AAErC,MAAM,qBAAqB,GAAG,CAAC,sBAAsB,CAAU,CAAC;AAEhE,MAAM,UAAU,IAAI;IAClB,OAAO,CAAC,EAAE,KAAK,EAAE,wBAAwB,EAAE,CAAC,CAAC;AAC/C,CAAC;AAED,SAAS,SAAS,CAAC,EAAE,IAAI,EAAkC;IACzD,OAAO,CACL,eACE,SAAS,EAAE,EAAE,CACX,8BAA8B,EAC9B,IAAI,KAAK,SAAS,IAAI,gBAAgB,EACtC,IAAI,KAAK,SAAS,IAAI,cAAc,EACpC,IAAI,KAAK,QAAQ,IAAI,gBAAgB,EACrC,IAAI,KAAK,OAAO,IAAI,wBAAwB,EAC5C,IAAI,KAAK,SAAS,IAAI,YAAY,CACnC,GACD,CACH,CAAC;AACJ,CAAC;AAED,SAAS,cAAc;IACrB,MAAM,OAAO,GAAG,iBAAiB,CAAC,CAAC,QAAQ,EAAE,gBAAgB,CAAC,CAAC,CAAC;IAChE,OAAO,QAAQ,CAA2B;QACxC,QAAQ,EAAE,CAAC,GAAG,qBAAqB,EAAE,OAAO,CAAC;QAC7C,OAAO,EAAE,uBAAuB;QAChC,eAAe,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI;QAC/B,SAAS,EAAE,KAAK;KACjB,CAAC,CAAC;AACL,CAAC;AAED,SAAS,mBAAmB;IAC1B,MAAM,WAAW,GAAG,cAAc,EAAE,CAAC;IAErC,OAAO,WAAW,CAAC;QACjB,UAAU,EAAE,4BAA4B;QACxC,QAAQ,EAAE,KAAK,EAAE,KAAwC,EAAE,EAAE;YAC3D,MAAM,WAAW,CAAC,aAAa,CAAC,EAAE,QAAQ,EAAE,qBAAqB,EAAE,CAAC,CAAC;YACrE,MAAM,SAAS,GAAG,WAAW,CAAC,cAAc,CAA2B;gBACrE,QAAQ,EAAE,qBAAqB;aAChC,CAAC,CAAC;YAEH,WAAW,CAAC,cAAc,CACxB,EAAE,QAAQ,EAAE,qBAAqB,EAAE,EACnC,CAAC,IAAI,EAAE,EAAE,CACP,IAAI,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CACjB,kBAAkB,CAAC,IAAI,CAAC,KAAK,kBAAkB,CAAC,KAAK,CAAC;gBACpD,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE;gBACrC,CAAC,CAAC,IAAI,CACT,CACJ,CAAC;YAEF,OAAO,EAAE,SAAS,EAAE,CAAC;QACvB,CAAC;QACD,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE;YAChC,KAAK,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,OAAO,EAAE,SAAS,IAAI,EAAE,EAAE,CAAC;gBACxD,WAAW,CAAC,YAAY,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;YAC3C,CAAC;YACD,KAAK,CAAC,KAAK,CACT,gCACE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CACjD,EAAE,CACH,CAAC;QACJ,CAAC;QACD,SAAS,EAAE,CAAC,OAAO,EAAE,EAAE;YACrB,WAAW,CAAC,cAAc,CACxB,EAAE,QAAQ,EAAE,qBAAqB,EAAE,EACnC,CAAC,IAAI,EAAE,EAAE,CACP,IAAI,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CACjB,kBAAkB,CAAC,IAAI,CAAC,KAAK,kBAAkB,CAAC,OAAO,CAAC;gBACtD,CAAC,CAAC,OAAO;gBACT,CAAC,CAAC,IAAI,CACT,CACJ,CAAC;QACJ,CAAC;QACD,SAAS,EAAE,GAAG,EAAE;YACd,KAAK,WAAW,CAAC,iBAAiB,CAAC,EAAE,QAAQ,EAAE,qBAAqB,EAAE,CAAC,CAAC;QAC1E,CAAC;KACF,CAAC,CAAC;AACL,CAAC;AAED,SAAS,sBAAsB;IAC7B,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IACxC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAA8B,UAAU,CAAC,CAAC;IAE5E,SAAS,YAAY,CAAC,IAAY;QAChC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,CAAC,OAAO;YAAE,OAAO;QACrB,MAAM,CAAC,aAAa,CAClB,IAAI,WAAW,CAAC,sBAAsB,EAAE;YACtC,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE;SACzB,CAAC,CACH,CAAC;QACF,eAAe,CAAC;YACd,OAAO,EAAE,OAAO;YAChB,OAAO,EAAE,qDAAqD,KAAK,oKAAoK;YACvO,MAAM,EAAE,IAAI;YACZ,MAAM,EAAE,IAAI;SACb,CAAC,CAAC;QACH,OAAO,CAAC,KAAK,CAAC,CAAC;IACjB,CAAC;IAED,OAAO,CACL,MAAC,OAAO,IAAC,IAAI,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,aACxC,KAAC,cAAc,IAAC,OAAO,kBACrB,MAAC,MAAM,IAAC,IAAI,EAAC,IAAI,aACf,KAAC,QAAQ,IAAC,IAAI,EAAE,EAAE,GAAI,sBAEf,GACM,EACjB,MAAC,cAAc,IAAC,KAAK,EAAC,KAAK,EAAC,SAAS,EAAC,+BAA+B,aACnE,YAAG,SAAS,EAAC,4CAA4C,+BAErD,EACJ,KAAC,cAAc,IACb,SAAS,QACT,WAAW,EAAC,uCAAuC,EACnD,UAAU,EAAC,6BAA6B,EACxC,QAAQ,EAAE,YAAY,GACtB,EACF,kBACE,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE,CAClB,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,KAAoC,CAAC,EAE7D,SAAS,EAAC,6GAA6G,aAEvH,iBAAQ,KAAK,EAAC,UAAU,yBAAkB,EAC1C,iBAAQ,KAAK,EAAC,cAAc,6BAAsB,IAC3C,IACM,IACT,CACX,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,OAAO,UAAU,gBAAgB;IACtC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAqB,UAAU,CAAC,CAAC;IACjE,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC;IACvC,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAAC,GACrC,QAAQ,CAAgC,IAAI,CAAC,CAAC;IAChD,MAAM,gBAAgB,GAAG,cAAc,EAAE,CAAC;IAC1C,MAAM,gBAAgB,GAAG,mBAAmB,EAAE,CAAC;IAC/C,MAAM,WAAW,GAAG,gBAAgB,CAAC,IAAI,IAAI,EAAE,CAAC;IAChD,MAAM,kBAAkB,GAAG,OAAO,CAChC,GAAG,EAAE,CACH,IAAI,KAAK,KAAK;QACZ,CAAC,CAAC,WAAW;QACb,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,EAC3D,CAAC,WAAW,EAAE,IAAI,CAAC,CACpB,CAAC;IACF,MAAM,OAAO,GAAG,OAAO,CACrB,GAAG,EAAE,CAAC,eAAe,CAAC,kBAAkB,CAAC,EACzC,CAAC,kBAAkB,CAAC,CACrB,CAAC;IACF,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,EAAE;QAC5B,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAC9C,IAAI,CAAC,UAAU;YAAE,OAAO,OAAO,CAAC;QAChC,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAC7B;YACE,IAAI,CAAC,IAAI;YACT,IAAI,CAAC,KAAK;YACV,IAAI,CAAC,QAAQ;YACb,IAAI,CAAC,mBAAmB;YACxB,IAAI,CAAC,IAAI;YACT,IAAI,CAAC,KAAK;YACV,IAAI,CAAC,MAAM;SACZ;aACE,MAAM,CAAC,OAAO,CAAC;aACf,IAAI,CAAC,GAAG,CAAC;aACT,WAAW,EAAE;aACb,QAAQ,CAAC,UAAU,CAAC,CACxB,CAAC;IACJ,CAAC,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;IACrB,MAAM,YAAY,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC;IAC9E,MAAM,UAAU,GAAG,kBAAkB,CAAC,MAAM,CAC1C,CAAC,IAAI,EAAE,EAAE,CACP,IAAI,CAAC,OAAO;QACZ,CAAC,IAAI,CAAC,UAAU,KAAK,OAAO,IAAI,IAAI,CAAC,UAAU,KAAK,SAAS,CAAC,CACjE,CAAC,MAAM,CAAC;IACT,MAAM,qBAAqB,GAAG,gBAAgB,CAAC,SAAS;QACtD,CAAC,CAAC,gBAAgB,CAAC,SAAS;YAC1B,CAAC,CAAC,kBAAkB,CAAC,gBAAgB,CAAC,SAAS,CAAC;YAChD,CAAC,CAAC,IAAI;QACR,CAAC,CAAC,IAAI,CAAC;IAET,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,aAAa;YAAE,OAAO;QAC3B,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAC3B,CAAC,IAAI,EAAE,EAAE,CAAC,kBAAkB,CAAC,IAAI,CAAC,KAAK,kBAAkB,CAAC,aAAa,CAAC,CACzE,CAAC;QACF,IAAI,OAAO,EAAE,CAAC;YACZ,IAAI,OAAO,KAAK,aAAa;gBAAE,gBAAgB,CAAC,OAAO,CAAC,CAAC;QAC3D,CAAC;aAAM,CAAC;YACN,gBAAgB,CAAC,IAAI,CAAC,CAAC;QACzB,CAAC;IACH,CAAC,EAAE,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC,CAAC;IAE9B,OAAO,CACL,KAAC,aAAa,IACZ,KAAK,EAAC,aAAa,EACnB,WAAW,EAAC,yHAAyH,YAErI,mBAAS,SAAS,EAAC,qBAAqB,aACtC,eAAK,SAAS,EAAC,mDAAmD,aAChE,eAAK,SAAS,EAAC,kDAAkD,aAC/D,eAAK,SAAS,EAAC,0BAA0B,aACvC,KAAC,UAAU,IACT,IAAI,EAAE,EAAE,iBACI,MAAM,EAClB,SAAS,EAAC,sFAAsF,GAChG,EACF,KAAC,KAAK,IACJ,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,EACjD,WAAW,EAAC,oBAAoB,gBACrB,oBAAoB,EAC/B,SAAS,EAAC,kBAAkB,GAC5B,IACE,EACN,eAAK,SAAS,EAAC,uDAAuD,aACpE,KAAC,sBAAsB,IAAC,IAAI,EAAE,EAAE,EAAE,SAAS,EAAC,UAAU,GAAG,EACzD,2BACG,YAAY,cACZ,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,UAAU,SAAS,CAAC,CAAC,CAAC,EAAE,IAC3C,IACH,IACF,EACN,eAAK,SAAS,EAAC,+CAA+C,aAC5D,MAAC,MAAM,IACL,KAAK,EAAE,IAAI,EACX,aAAa,EAAE,CAAC,KAAK,EAAE,EAAE;wCACvB,IAAI,KAAK,KAAK,UAAU,IAAI,KAAK,KAAK,KAAK;4CAAE,OAAO,CAAC,KAAK,CAAC,CAAC;oCAC9D,CAAC,aAED,KAAC,aAAa,IACZ,SAAS,EAAC,yBAAyB,gBACxB,iBAAiB,YAE5B,KAAC,WAAW,KAAG,GACD,EAChB,MAAC,aAAa,eACZ,KAAC,UAAU,IAAC,KAAK,EAAC,UAAU,qCAAkC,EAC9D,KAAC,UAAU,IAAC,KAAK,EAAC,KAAK,yBAAsB,IAC/B,IACT,EACT,KAAC,sBAAsB,KAAG,IACtB,IACF,EAEN,eAAK,SAAS,EAAC,6DAA6D,aAC1E,cAAK,SAAS,EAAC,qCAAqC,YACjD,gBAAgB,CAAC,SAAS,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CACpD,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,CAC1C,eAAiB,SAAS,EAAC,WAAW,aACpC,KAAC,QAAQ,IAAC,SAAS,EAAC,UAAU,GAAG,EACjC,KAAC,QAAQ,IAAC,SAAS,EAAC,eAAe,GAAG,KAF9B,KAAK,CAGT,CACP,CAAC,CACH,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CACxB,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;gCACpB,MAAM,MAAM,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;gCACtC,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC;gCAC3C,MAAM,UAAU,GACd,qBAAqB,KAAK,kBAAkB,CAAC,IAAI,CAAC,CAAC;gCACrD,MAAM,UAAU,GACd,aAAa,KAAK,IAAI;oCACtB,kBAAkB,CAAC,aAAa,CAAC;wCAC/B,kBAAkB,CAAC,IAAI,CAAC,CAAC;gCAC7B,OAAO,CACL,eAEE,SAAS,EAAE,EAAE,CACX,uEAAuE,EACvE,UAAU,IAAI,cAAc,CAC7B,aAED,eAAK,SAAS,EAAC,SAAS,aACtB,kBACE,IAAI,EAAC,QAAQ,EACb,SAAS,EAAC,8JAA8J,kBAC1J,UAAU,EACxB,OAAO,EAAE,GAAG,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,aAErC,eAAK,SAAS,EAAC,iCAAiC,aAC9C,KAAC,SAAS,IAAC,IAAI,EAAE,MAAM,CAAC,IAAI,GAAI,EAChC,eAAM,SAAS,EAAC,8CAA8C,YAC3D,IAAI,CAAC,IAAI,GACL,IACH,EACN,cAAK,SAAS,EAAC,6CAA6C,YACzD,gBAAgB,CAAC,IAAI,CAAC,GACnB,EACN,eAAK,SAAS,EAAC,uEAAuE,aACpF,oCAAY,iBAAiB,CAAC,IAAI,CAAC,IAAQ,EAC3C,oCAAY,iBAAiB,CAAC,IAAI,CAAC,IAAQ,EAC3C,yBAAO,oBAAoB,CAAC,IAAI,CAAC,GAAQ,EACxC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAChB,uCAAe,mBAAmB,CAAC,IAAI,CAAC,IAAQ,CACjD,CAAC,CAAC,CAAC,IAAI,IACJ,IACC,EACR,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAChB,eAAK,SAAS,EAAC,6EAA6E,aAC1F,eAAM,SAAS,EAAC,qBAAqB,YAClC,IAAI,CAAC,SAAS,GACV,EACP,MAAC,IAAI,IACH,EAAE,EAAE,0BAA0B,CAAC,IAAI,CAAC,EACpC,SAAS,EAAC,wGAAwG,gBACtG,gBAAgB,IAAI,CAAC,IAAI,kBAAkB,aAEvD,KAAC,cAAc,IAAC,IAAI,EAAE,EAAE,iBAAc,MAAM,GAAG,oCAE1C,IACH,CACP,CAAC,CAAC,CAAC,IAAI,IACJ,EACN,eAAK,SAAS,EAAC,+BAA+B,aAC5C,KAAC,KAAK,IACJ,OAAO,EACL,MAAM,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,EAEtD,SAAS,EAAC,KAAK,YAEd,MAAM,CAAC,KAAK,GACP,EACR,KAAC,MAAM,IACL,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,OAAO,EACvB,QAAQ,EAAE,CAAC,SAAS,IAAI,UAAU,gBACtB,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,eAAe,IAAI,CAAC,IAAI,EAAE,EAC5E,eAAe,EAAE,CAAC,OAAO,EAAE,EAAE,CAC3B,gBAAgB,CAAC,MAAM,CAAC;wDACtB,KAAK,EAAE,IAAI,CAAC,KAAK;wDACjB,IAAI,EAAE,IAAI,CAAC,IAAI;wDACf,OAAO,EAAE,OAAO;qDACjB,CAAC,GAEJ,IACE,KApED,IAAI,CAAC,EAAE,CAqER,CACP,CAAC;4BACJ,CAAC,CAAC,CACH,CAAC,CAAC,CAAC,CACF,cAAK,SAAS,EAAC,sDAAsD,YAClE,KAAK;oCACJ,CAAC,CAAC,0BAA0B;oCAC5B,CAAC,CAAC,IAAI,KAAK,KAAK;wCACd,CAAC,CAAC,oGAAoG;wCACtG,CAAC,CAAC,mFAAmF,GACrF,CACP,GACG,EAEL,aAAa,CAAC,CAAC,CAAC,CACf,KAAC,sBAAsB,IACrB,UAAU,EAAE,aAAa,EACzB,UAAU,EACR,qBAAqB,KAAK,kBAAkB,CAAC,aAAa,CAAC,EAE7D,QAAQ,EAAE,GAAG,EAAE,CACb,gBAAgB,CAAC,MAAM,CAAC;gCACtB,KAAK,EAAE,aAAa,CAAC,KAAK;gCAC1B,IAAI,EAAE,aAAa,CAAC,IAAI;gCACxB,OAAO,EAAE,CAAC,aAAa,CAAC,OAAO;6BAChC,CAAC,GAEJ,CACH,CAAC,CAAC,CAAC,CACF,eAAK,SAAS,EAAC,iHAAiH,aAC9H,KAAC,eAAe,IACd,IAAI,EAAE,EAAE,EACR,WAAW,EAAE,GAAG,EAChB,SAAS,EAAC,uBAAuB,GACjC,EACF,YAAG,SAAS,EAAC,0CAA0C,qCAEnD,EACJ,YAAG,SAAS,EAAC,6CAA6C,kFAGtD,IACA,CACP,IACG,IACE,GACI,CACjB,CAAC;AACJ,CAAC","sourcesContent":["import { sendToAgentChat } from \"@agent-native/core/client/agent-chat\";\nimport { PromptComposer } from \"@agent-native/core/client/composer\";\nimport { useChangeVersions } from \"@agent-native/core/client/hooks\";\nimport {\n IconFileSearch,\n IconListDetails,\n IconPlus,\n IconSearch,\n IconSettingsAutomation,\n} from \"@tabler/icons-react\";\nimport { useMutation, useQuery, useQueryClient } from \"@tanstack/react-query\";\nimport { useEffect, useMemo, useState } from \"react\";\nimport { Link } from \"react-router\";\nimport { toast } from \"sonner\";\n\nimport { AutomationDetailsPanel } from \"../../components/automation-details-panel\";\nimport { DispatchShell } from \"../../components/dispatch-shell\";\nimport { Badge } from \"../../components/ui/badge\";\nimport { Button } from \"../../components/ui/button\";\nimport { Input } from \"../../components/ui/input\";\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from \"../../components/ui/popover\";\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"../../components/ui/select\";\nimport { Skeleton } from \"../../components/ui/skeleton\";\nimport { Switch } from \"../../components/ui/switch\";\nimport {\n automationIdentity,\n automationLastCheck,\n automationLastRun,\n automationNextRun,\n automationScopeLabel,\n automationStatus,\n automationTarget,\n automationTroubleshootPath,\n belongsToDispatch,\n sortAutomations,\n type AutomationStatusTone,\n} from \"../../lib/automation-display\";\nimport {\n listDispatchAutomations,\n setDispatchAutomationEnabled,\n type DispatchAutomationItem,\n type SetDispatchAutomationEnabledInput,\n} from \"../../lib/automations\";\nimport { cn } from \"../../lib/utils\";\n\nconst AUTOMATIONS_QUERY_KEY = [\"dispatch-automations\"] as const;\n\nexport function meta() {\n return [{ title: \"Automations — Dispatch\" }];\n}\n\nfunction StatusDot({ tone }: { tone: AutomationStatusTone }) {\n return (\n <span\n className={cn(\n \"size-2 shrink-0 rounded-full\",\n tone === \"success\" && \"bg-emerald-500\",\n tone === \"warning\" && \"bg-amber-500\",\n tone === \"danger\" && \"bg-destructive\",\n tone === \"muted\" && \"bg-muted-foreground/35\",\n tone === \"default\" && \"bg-primary\",\n )}\n />\n );\n}\n\nfunction useAutomations() {\n const version = useChangeVersions([\"action\", \"screen-refresh\"]);\n return useQuery<DispatchAutomationItem[]>({\n queryKey: [...AUTOMATIONS_QUERY_KEY, version],\n queryFn: listDispatchAutomations,\n placeholderData: (prev) => prev,\n staleTime: 5_000,\n });\n}\n\nfunction useToggleAutomation() {\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: setDispatchAutomationEnabled,\n onMutate: async (input: SetDispatchAutomationEnabledInput) => {\n await queryClient.cancelQueries({ queryKey: AUTOMATIONS_QUERY_KEY });\n const snapshots = queryClient.getQueriesData<DispatchAutomationItem[]>({\n queryKey: AUTOMATIONS_QUERY_KEY,\n });\n\n queryClient.setQueriesData<DispatchAutomationItem[]>(\n { queryKey: AUTOMATIONS_QUERY_KEY },\n (rows) =>\n rows?.map((item) =>\n automationIdentity(item) === automationIdentity(input)\n ? { ...item, enabled: input.enabled }\n : item,\n ),\n );\n\n return { snapshots };\n },\n onError: (err, _input, context) => {\n for (const [queryKey, data] of context?.snapshots ?? []) {\n queryClient.setQueryData(queryKey, data);\n }\n toast.error(\n `Could not update automation: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n },\n onSuccess: (updated) => {\n queryClient.setQueriesData<DispatchAutomationItem[]>(\n { queryKey: AUTOMATIONS_QUERY_KEY },\n (rows) =>\n rows?.map((item) =>\n automationIdentity(item) === automationIdentity(updated)\n ? updated\n : item,\n ),\n );\n },\n onSettled: () => {\n void queryClient.invalidateQueries({ queryKey: AUTOMATIONS_QUERY_KEY });\n },\n });\n}\n\nfunction CreateAutomationButton() {\n const [open, setOpen] = useState(false);\n const [scope, setScope] = useState<\"personal\" | \"organization\">(\"personal\");\n\n function handleSubmit(text: string) {\n const trimmed = text.trim();\n if (!trimmed) return;\n window.dispatchEvent(\n new CustomEvent(\"agent-panel:set-mode\", {\n detail: { mode: \"chat\" },\n }),\n );\n sendToAgentChat({\n message: trimmed,\n context: `The user wants to create a new automation. Scope: ${scope}. Use manage-automations with action=define to create it. Ask clarifying questions if needed about what event to trigger on, conditions, and what actions to take.`,\n submit: true,\n newTab: true,\n });\n setOpen(false);\n }\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n <Button size=\"sm\">\n <IconPlus size={14} />\n New automation\n </Button>\n </PopoverTrigger>\n <PopoverContent align=\"end\" className=\"w-[min(100vw-2rem,24rem)] p-3\">\n <p className=\"pb-2 text-sm font-semibold text-foreground\">\n New automation\n </p>\n <PromptComposer\n autoFocus\n placeholder=\"Describe what you want to automate...\"\n draftScope=\"dispatch-automations:create\"\n onSubmit={handleSubmit}\n />\n <select\n value={scope}\n onChange={(event) =>\n setScope(event.target.value as \"personal\" | \"organization\")\n }\n className=\"mt-2 w-full cursor-pointer rounded-md border border-input bg-background px-3 py-1.5 text-xs text-foreground\"\n >\n <option value=\"personal\">Personal</option>\n <option value=\"organization\">Organization</option>\n </select>\n </PopoverContent>\n </Popover>\n );\n}\n\nexport default function AutomationsRoute() {\n const [view, setView] = useState<\"dispatch\" | \"all\">(\"dispatch\");\n const [query, setQuery] = useState(\"\");\n const [detailsTarget, setDetailsTarget] =\n useState<DispatchAutomationItem | null>(null);\n const automationsQuery = useAutomations();\n const toggleAutomation = useToggleAutomation();\n const automations = automationsQuery.data ?? [];\n const visibleAutomations = useMemo(\n () =>\n view === \"all\"\n ? automations\n : automations.filter((item) => belongsToDispatch(item)),\n [automations, view],\n );\n const ordered = useMemo(\n () => sortAutomations(visibleAutomations),\n [visibleAutomations],\n );\n const filtered = useMemo(() => {\n const normalized = query.trim().toLowerCase();\n if (!normalized) return ordered;\n return ordered.filter((item) =>\n [\n item.name,\n item.event,\n item.schedule,\n item.scheduleDescription,\n item.body,\n item.model,\n item.domain,\n ]\n .filter(Boolean)\n .join(\" \")\n .toLowerCase()\n .includes(normalized),\n );\n }, [ordered, query]);\n const enabledCount = visibleAutomations.filter((item) => item.enabled).length;\n const errorCount = visibleAutomations.filter(\n (item) =>\n item.enabled &&\n (item.lastStatus === \"error\" || item.lastStatus === \"skipped\"),\n ).length;\n const pendingToggleIdentity = toggleAutomation.isPending\n ? toggleAutomation.variables\n ? automationIdentity(toggleAutomation.variables)\n : null\n : null;\n\n useEffect(() => {\n if (!detailsTarget) return;\n const current = filtered.find(\n (item) => automationIdentity(item) === automationIdentity(detailsTarget),\n );\n if (current) {\n if (current !== detailsTarget) setDetailsTarget(current);\n } else {\n setDetailsTarget(null);\n }\n }, [detailsTarget, filtered]);\n\n return (\n <DispatchShell\n title=\"Automations\"\n description=\"See scheduled and event-triggered jobs, inspect their checks and past runs, pause them, or ask the agent to create one.\"\n >\n <section className=\"flex flex-col gap-4\">\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <div className=\"flex min-w-0 flex-1 flex-wrap items-center gap-3\">\n <div className=\"relative w-full max-w-sm\">\n <IconSearch\n size={15}\n aria-hidden=\"true\"\n className=\"pointer-events-none absolute left-2.5 top-1/2 -translate-y-1/2 text-muted-foreground\"\n />\n <Input\n value={query}\n onChange={(event) => setQuery(event.target.value)}\n placeholder=\"Search automations\"\n aria-label=\"Search automations\"\n className=\"h-8 pl-8 text-xs\"\n />\n </div>\n <div className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n <IconSettingsAutomation size={16} className=\"shrink-0\" />\n <span>\n {enabledCount} enabled\n {errorCount > 0 ? ` · ${errorCount} errors` : \"\"}\n </span>\n </div>\n </div>\n <div className=\"flex flex-wrap items-center justify-end gap-2\">\n <Select\n value={view}\n onValueChange={(value) => {\n if (value === \"dispatch\" || value === \"all\") setView(value);\n }}\n >\n <SelectTrigger\n className=\"h-8 w-[10.5rem] text-xs\"\n aria-label=\"Automation view\"\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"dispatch\">Dispatch automations</SelectItem>\n <SelectItem value=\"all\">All apps</SelectItem>\n </SelectContent>\n </Select>\n <CreateAutomationButton />\n </div>\n </div>\n\n <div className=\"grid gap-4 lg:grid-cols-[minmax(0,1fr)_minmax(24rem,32rem)]\">\n <div className=\"min-w-0 divide-y rounded-lg bg-card\">\n {automationsQuery.isLoading && ordered.length === 0 ? (\n Array.from({ length: 4 }).map((_, index) => (\n <div key={index} className=\"px-4 py-3\">\n <Skeleton className=\"h-4 w-40\" />\n <Skeleton className=\"mt-2 h-3 w-28\" />\n </div>\n ))\n ) : filtered.length > 0 ? (\n filtered.map((item) => {\n const status = automationStatus(item);\n const canUpdate = item.canUpdate !== false;\n const isToggling =\n pendingToggleIdentity === automationIdentity(item);\n const isSelected =\n detailsTarget !== null &&\n automationIdentity(detailsTarget) ===\n automationIdentity(item);\n return (\n <div\n key={item.id}\n className={cn(\n \"grid grid-cols-[minmax(0,1fr)_auto] gap-3 px-4 py-3 transition-colors\",\n isSelected && \"bg-accent/40\",\n )}\n >\n <div className=\"min-w-0\">\n <button\n type=\"button\"\n className=\"w-full min-w-0 cursor-pointer text-left outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-card\"\n aria-pressed={isSelected}\n onClick={() => setDetailsTarget(item)}\n >\n <div className=\"flex min-w-0 items-center gap-2\">\n <StatusDot tone={status.tone} />\n <span className=\"truncate text-sm font-medium text-foreground\">\n {item.name}\n </span>\n </div>\n <div className=\"mt-1 truncate text-xs text-muted-foreground\">\n {automationTarget(item)}\n </div>\n <div className=\"mt-1 flex flex-wrap gap-x-3 gap-y-1 text-[11px] text-muted-foreground\">\n <span>Last {automationLastRun(item)}</span>\n <span>Next {automationNextRun(item)}</span>\n <span>{automationScopeLabel(item)}</span>\n {item.lastCheck ? (\n <span>Checked {automationLastCheck(item)}</span>\n ) : null}\n </div>\n </button>\n {item.lastError ? (\n <div className=\"mt-1 flex flex-wrap items-baseline gap-x-2 gap-y-1 text-xs text-destructive\">\n <span className=\"min-w-0 break-words\">\n {item.lastError}\n </span>\n <Link\n to={automationTroubleshootPath(item)}\n className=\"inline-flex shrink-0 items-center gap-1 font-medium text-foreground underline-offset-4 hover:underline\"\n aria-label={`Troubleshoot ${item.name} in Thread Debug`}\n >\n <IconFileSearch size={13} aria-hidden=\"true\" />\n Troubleshoot in Thread Debug\n </Link>\n </div>\n ) : null}\n </div>\n <div className=\"flex flex-col items-end gap-2\">\n <Badge\n variant={\n status.tone === \"danger\" ? \"destructive\" : \"outline\"\n }\n className=\"h-5\"\n >\n {status.label}\n </Badge>\n <Switch\n checked={!!item.enabled}\n disabled={!canUpdate || isToggling}\n aria-label={`${item.enabled ? \"Disable\" : \"Enable\"} automation ${item.name}`}\n onCheckedChange={(checked) =>\n toggleAutomation.mutate({\n owner: item.owner,\n path: item.path,\n enabled: checked,\n })\n }\n />\n </div>\n </div>\n );\n })\n ) : (\n <div className=\"px-4 py-10 text-center text-sm text-muted-foreground\">\n {query\n ? \"No matching automations.\"\n : view === \"all\"\n ? \"No automations yet. Create one here, or ask Dispatch to set up a scheduled or event-triggered job.\"\n : \"No Dispatch automations yet. Switch to All apps to inspect workspace automations.\"}\n </div>\n )}\n </div>\n\n {detailsTarget ? (\n <AutomationDetailsPanel\n automation={detailsTarget}\n isToggling={\n pendingToggleIdentity === automationIdentity(detailsTarget)\n }\n onToggle={() =>\n toggleAutomation.mutate({\n owner: detailsTarget.owner,\n path: detailsTarget.path,\n enabled: !detailsTarget.enabled,\n })\n }\n />\n ) : (\n <div className=\"flex min-h-[34rem] flex-col items-center justify-center rounded-lg border border-dashed bg-card p-6 text-center\">\n <IconListDetails\n size={24}\n strokeWidth={1.6}\n className=\"text-muted-foreground\"\n />\n <p className=\"mt-3 text-sm font-medium text-foreground\">\n Select an automation\n </p>\n <p className=\"mt-1 max-w-xs text-sm text-muted-foreground\">\n Inspect its prompt, configuration, capabilities, and run\n history.\n </p>\n </div>\n )}\n </div>\n </section>\n </DispatchShell>\n );\n}\n"]}
|