@truefoundry/trueforge-ui 0.2.4 → 0.3.0-rc.1

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.
Files changed (113) hide show
  1. package/CHANGELOG.md +85 -0
  2. package/README.md +55 -23
  3. package/dist/AgentCodeSnippets-NZOFJE57.js +74 -0
  4. package/dist/AgentCodeSnippets-NZOFJE57.js.map +1 -0
  5. package/dist/AgentMetricChart-JPU3MFQ2.js +15 -0
  6. package/dist/AgentOverview-EEBHJC32.js +167 -0
  7. package/dist/AgentOverview-EEBHJC32.js.map +1 -0
  8. package/dist/AgentSessionEventTimeline-26Z6IB3B.js +79 -0
  9. package/dist/AgentSessionEventTimeline-26Z6IB3B.js.map +1 -0
  10. package/dist/AgentSessionEventTimelineChart-CGCU7WBK.js +504 -0
  11. package/dist/AgentSessionEventTimelineChart-CGCU7WBK.js.map +1 -0
  12. package/dist/AgentSessions-3MPDZASK.js +275 -0
  13. package/dist/AgentSessions-3MPDZASK.js.map +1 -0
  14. package/dist/{ConnectorSettings-ODC3LVHN.js → ConnectorSettings-MXFRWWR6.js} +8 -6
  15. package/dist/{ConnectorSettings-ODC3LVHN.js.map → ConnectorSettings-MXFRWWR6.js.map} +1 -1
  16. package/dist/{DockLayout-TEOWEMSV.js → DockLayout-LM4AFMFY.js} +12 -11
  17. package/dist/{DockLayout-TEOWEMSV.js.map → DockLayout-LM4AFMFY.js.map} +1 -1
  18. package/dist/DrawerLayout-JR3O6RRB.js +184 -0
  19. package/dist/DrawerLayout-JR3O6RRB.js.map +1 -0
  20. package/dist/{ModelSettings-CKQ5CVAC.js → ModelSettings-G7IFNC2Y.js} +8 -6
  21. package/dist/{ModelSettings-CKQ5CVAC.js.map → ModelSettings-G7IFNC2Y.js.map} +1 -1
  22. package/dist/OpenUiFenceBlock-2TV5TQ6P.js +9 -0
  23. package/dist/OpenUiFenceBlock-2TV5TQ6P.js.map +1 -0
  24. package/dist/{SandboxSettings-HSCPYUOC.js → SandboxSettings-NXAANMAX.js} +8 -6
  25. package/dist/{SandboxSettings-HSCPYUOC.js.map → SandboxSettings-NXAANMAX.js.map} +1 -1
  26. package/dist/SchedulesPage-KKLUDO6B.js +15 -0
  27. package/dist/SchedulesPage-KKLUDO6B.js.map +1 -0
  28. package/dist/{SettingsBuilder-MYWRFXRI.js → SettingsBuilder-L34IQ56S.js} +26 -22
  29. package/dist/SettingsBuilder-L34IQ56S.js.map +1 -0
  30. package/dist/{ShellRouteSync-T4WLY57Q.js → ShellRouteSync-CEBETGEJ.js} +95 -22
  31. package/dist/ShellRouteSync-CEBETGEJ.js.map +1 -0
  32. package/dist/SidebarLayout-A2HJPPHS.js +308 -0
  33. package/dist/SidebarLayout-A2HJPPHS.js.map +1 -0
  34. package/dist/{SkillSettings-ECH6SHZ7.js → SkillSettings-FGGMSUNO.js} +8 -6
  35. package/dist/{SkillSettings-ECH6SHZ7.js.map → SkillSettings-FGGMSUNO.js.map} +1 -1
  36. package/dist/{TrueForgeUIWithRouter-KMHPOKLN.js → TrueForgeUIWithRouter-MZVJWJMB.js} +15 -11
  37. package/dist/{TrueForgeUIWithRouter-KMHPOKLN.js.map → TrueForgeUIWithRouter-MZVJWJMB.js.map} +1 -1
  38. package/dist/{WidgetLayout-4ZIG3RXV.js → WidgetLayout-6QM45OQQ.js} +19 -15
  39. package/dist/WidgetLayout-6QM45OQQ.js.map +1 -0
  40. package/dist/chunk-2XPPBEYF.js +74 -0
  41. package/dist/chunk-2XPPBEYF.js.map +1 -0
  42. package/dist/chunk-6O6FWUDW.js +1376 -0
  43. package/dist/chunk-6O6FWUDW.js.map +1 -0
  44. package/dist/chunk-7Z2C2K7F.js +578 -0
  45. package/dist/chunk-7Z2C2K7F.js.map +1 -0
  46. package/dist/{chunk-FXCYLEFB.js → chunk-BSIPI3KV.js} +18 -2
  47. package/dist/chunk-BSIPI3KV.js.map +1 -0
  48. package/dist/{chunk-255AE5NI.js → chunk-CYHJDKFB.js} +118 -33
  49. package/dist/chunk-CYHJDKFB.js.map +1 -0
  50. package/dist/chunk-DJUFS2BW.js +27 -0
  51. package/dist/chunk-DJUFS2BW.js.map +1 -0
  52. package/dist/chunk-DMMZSZTN.js +252 -0
  53. package/dist/chunk-DMMZSZTN.js.map +1 -0
  54. package/dist/chunk-FJW36PD7.js +180 -0
  55. package/dist/chunk-FJW36PD7.js.map +1 -0
  56. package/dist/chunk-M62XY4NC.js +555 -0
  57. package/dist/chunk-M62XY4NC.js.map +1 -0
  58. package/dist/{chunk-JRRCAKEP.js → chunk-MPNSO7E6.js} +2 -2
  59. package/dist/{chunk-CFLVPV5M.js → chunk-QC42MLAK.js} +7 -2
  60. package/dist/chunk-QC42MLAK.js.map +1 -0
  61. package/dist/chunk-RDXBMTP5.js +32 -0
  62. package/dist/chunk-RDXBMTP5.js.map +1 -0
  63. package/dist/chunk-SFZNIO6U.js +18 -0
  64. package/dist/chunk-SFZNIO6U.js.map +1 -0
  65. package/dist/chunk-U7KURWW3.js +840 -0
  66. package/dist/chunk-U7KURWW3.js.map +1 -0
  67. package/dist/chunk-WVE3F724.js +200 -0
  68. package/dist/chunk-WVE3F724.js.map +1 -0
  69. package/dist/{chunk-IFLE7OMM.js → chunk-XAU7QFG3.js} +180 -38
  70. package/dist/chunk-XAU7QFG3.js.map +1 -0
  71. package/dist/chunk-Y6S4OVVQ.js +16 -0
  72. package/dist/chunk-Y6S4OVVQ.js.map +1 -0
  73. package/dist/chunk-ZKNUMH2M.js +119 -0
  74. package/dist/chunk-ZKNUMH2M.js.map +1 -0
  75. package/dist/chunk-ZTMKFZQ3.js +13214 -0
  76. package/dist/chunk-ZTMKFZQ3.js.map +1 -0
  77. package/dist/{createTrueFoundryServer-B72bU-J7.d.ts → createTrueFoundryServer-CmZnBh_l.d.ts} +15 -4
  78. package/dist/index.d.ts +1199 -161
  79. package/dist/index.js +699 -69
  80. package/dist/index.js.map +1 -1
  81. package/dist/plugins/trueforge-agent-server-adapter/index.d.ts +552 -12
  82. package/dist/plugins/trueforge-agent-server-adapter/index.js +506 -188
  83. package/dist/plugins/trueforge-agent-server-adapter/index.js.map +1 -1
  84. package/dist/styles.css +1 -1
  85. package/package.json +11 -10
  86. package/dist/DrawerLayout-2JOQ4EDZ.js +0 -159
  87. package/dist/DrawerLayout-2JOQ4EDZ.js.map +0 -1
  88. package/dist/OpenUiFenceBlock-3EFPY4JW.js +0 -9
  89. package/dist/SettingsBuilder-MYWRFXRI.js.map +0 -1
  90. package/dist/ShellRouteSync-T4WLY57Q.js.map +0 -1
  91. package/dist/SidebarLayout-6MHQYW45.js +0 -239
  92. package/dist/SidebarLayout-6MHQYW45.js.map +0 -1
  93. package/dist/WidgetLayout-4ZIG3RXV.js.map +0 -1
  94. package/dist/chunk-255AE5NI.js.map +0 -1
  95. package/dist/chunk-72LC2M3R.js +0 -567
  96. package/dist/chunk-72LC2M3R.js.map +0 -1
  97. package/dist/chunk-CFLVPV5M.js.map +0 -1
  98. package/dist/chunk-FXCYLEFB.js.map +0 -1
  99. package/dist/chunk-IFLE7OMM.js.map +0 -1
  100. package/dist/chunk-IWPDSPVA.js +0 -2068
  101. package/dist/chunk-IWPDSPVA.js.map +0 -1
  102. package/dist/chunk-OZTNBS6V.js +0 -75
  103. package/dist/chunk-OZTNBS6V.js.map +0 -1
  104. package/dist/chunk-PUFYHFND.js +0 -91
  105. package/dist/chunk-PUFYHFND.js.map +0 -1
  106. package/dist/chunk-WQULF5AT.js +0 -460
  107. package/dist/chunk-WQULF5AT.js.map +0 -1
  108. package/dist/chunk-XRTTKGKN.js +0 -6825
  109. package/dist/chunk-XRTTKGKN.js.map +0 -1
  110. package/dist/chunk-YIWUG6MC.js +0 -101
  111. package/dist/chunk-YIWUG6MC.js.map +0 -1
  112. /package/dist/{OpenUiFenceBlock-3EFPY4JW.js.map → AgentMetricChart-JPU3MFQ2.js.map} +0 -0
  113. /package/dist/{chunk-JRRCAKEP.js.map → chunk-MPNSO7E6.js.map} +0 -0
@@ -0,0 +1,1376 @@
1
+ import {
2
+ Button,
3
+ ConnectorConnectButton,
4
+ DEFAULT_TABLE_PAGE_SIZE,
5
+ Dialog,
6
+ DialogContent,
7
+ DialogFooter,
8
+ DialogHeader,
9
+ DialogTitle,
10
+ DropdownMenu,
11
+ DropdownMenuItem,
12
+ EmptyScreen,
13
+ PopoverSelect,
14
+ SearchInput_default,
15
+ SideDrawer,
16
+ Skeleton,
17
+ Table,
18
+ TableBody,
19
+ TableCell,
20
+ TableHead,
21
+ TableHeader,
22
+ TableRow,
23
+ TableTokenPagination,
24
+ Tooltip,
25
+ auiInputClass,
26
+ mountName,
27
+ searchAllAgents,
28
+ useToasterOptional
29
+ } from "./chunk-ZTMKFZQ3.js";
30
+ import {
31
+ readScheduleShareSearch,
32
+ replaceScheduleShareSearch
33
+ } from "./chunk-2XPPBEYF.js";
34
+ import {
35
+ DraftCatalogProvider,
36
+ Icon,
37
+ PageHeader,
38
+ auiButtonClass,
39
+ useDraftCatalog
40
+ } from "./chunk-XAU7QFG3.js";
41
+ import {
42
+ libraryAgentId,
43
+ useOptionalCatalogServer,
44
+ useScheduleServer,
45
+ useServer
46
+ } from "./chunk-U7KURWW3.js";
47
+ import {
48
+ cn
49
+ } from "./chunk-7Z2C2K7F.js";
50
+
51
+ // src/atoms/schedules/SchedulesPage.tsx
52
+ import { useCallback, useEffect as useEffect3, useMemo as useMemo2, useRef, useState as useState3 } from "react";
53
+
54
+ // src/atoms/schedules/cadence.ts
55
+ var TIMEZONE_OPTIONS = [
56
+ { value: "America/New_York", label: "America/New_York (ET)" },
57
+ { value: "America/Chicago", label: "America/Chicago (CT)" },
58
+ { value: "America/Denver", label: "America/Denver (MT)" },
59
+ { value: "America/Los_Angeles", label: "America/Los_Angeles (PT)" },
60
+ { value: "UTC", label: "UTC" },
61
+ { value: "Europe/London", label: "Europe/London (GMT/BST)" },
62
+ { value: "Europe/Berlin", label: "Europe/Berlin (CET)" },
63
+ { value: "Asia/Kolkata", label: "Asia/Kolkata (IST)" },
64
+ { value: "Asia/Tokyo", label: "Asia/Tokyo (JST)" }
65
+ ];
66
+ function resolveLocalTimezone() {
67
+ try {
68
+ return Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
69
+ } catch {
70
+ return "UTC";
71
+ }
72
+ }
73
+ function getTimezoneOptions(selectedTimezone) {
74
+ const localTimezone = resolveLocalTimezone();
75
+ const knownTimezones = new Set(TIMEZONE_OPTIONS.map((option) => option.value));
76
+ const additionalTimezones = [localTimezone, selectedTimezone].flatMap((timezone) => {
77
+ if (timezone == null || timezone.length === 0 || knownTimezones.has(timezone)) return [];
78
+ knownTimezones.add(timezone);
79
+ return [{ value: timezone, label: timezone === localTimezone ? `${timezone} (Local)` : timezone }];
80
+ });
81
+ return [...additionalTimezones, ...TIMEZONE_OPTIONS];
82
+ }
83
+ var WEEKDAY_OPTIONS = [
84
+ { value: 1, label: "Mon" },
85
+ { value: 2, label: "Tue" },
86
+ { value: 3, label: "Wed" },
87
+ { value: 4, label: "Thu" },
88
+ { value: 5, label: "Fri" },
89
+ { value: 6, label: "Sat" },
90
+ { value: 0, label: "Sun" }
91
+ ];
92
+ var TZ_ABBR = {
93
+ "America/New_York": "ET",
94
+ "America/Chicago": "CT",
95
+ "America/Denver": "MT",
96
+ "America/Los_Angeles": "PT",
97
+ UTC: "UTC",
98
+ "Europe/London": "GMT",
99
+ "Europe/Berlin": "CET",
100
+ "Asia/Kolkata": "IST",
101
+ "Asia/Tokyo": "JST"
102
+ };
103
+ function defaultScheduleFormValues() {
104
+ return {
105
+ name: "",
106
+ task: "",
107
+ recurrence: "daily",
108
+ hour: 9,
109
+ minute: 0,
110
+ weekdays: [1],
111
+ customCron: "0 9 * * *",
112
+ timezone: resolveLocalTimezone()
113
+ };
114
+ }
115
+ function timezoneAbbrev(timezone) {
116
+ return TZ_ABBR[timezone] ?? timezone.split("/").pop() ?? timezone;
117
+ }
118
+ function pad2(n) {
119
+ return n.toString().padStart(2, "0");
120
+ }
121
+ function formatClock({ hour, minute }) {
122
+ const period = hour >= 12 ? "PM" : "AM";
123
+ const h12 = hour % 12 === 0 ? 12 : hour % 12;
124
+ return `${String(h12)}:${pad2(minute)} ${period}`;
125
+ }
126
+ function valuesToCron(values) {
127
+ const minute = values.minute;
128
+ const hour = values.hour;
129
+ switch (values.recurrence) {
130
+ case "hourly":
131
+ return `${String(minute)} * * * *`;
132
+ case "daily":
133
+ return `${String(minute)} ${String(hour)} * * *`;
134
+ case "weekly": {
135
+ const days = [...values.weekdays].sort((a, b) => a - b);
136
+ const dow = days.length > 0 ? days.join(",") : "*";
137
+ return `${String(minute)} ${String(hour)} * * ${dow}`;
138
+ }
139
+ case "custom":
140
+ return values.customCron.trim();
141
+ }
142
+ }
143
+ function formatCadenceSummary(input) {
144
+ const abbr = timezoneAbbrev(input.timezone);
145
+ const parts = input.cron.trim().split(/\s+/);
146
+ if (parts.length !== 5) return input.cron;
147
+ const [minuteRaw, hourRaw, dayOfMonthRaw, monthRaw, dowRaw] = parts;
148
+ if (dayOfMonthRaw !== "*" || monthRaw !== "*") return input.cron;
149
+ const minute = Number(minuteRaw);
150
+ const hour = Number(hourRaw);
151
+ if (hourRaw === "*" && dowRaw === "*" && minuteRaw !== void 0 && !Number.isNaN(minute)) {
152
+ return `Hourly at :${pad2(minute)} ${abbr}`;
153
+ }
154
+ if (dowRaw === "*" && hourRaw !== void 0 && minuteRaw !== void 0 && !Number.isNaN(hour) && !Number.isNaN(minute)) {
155
+ return `Daily ${formatClock({ hour, minute })} ${abbr}`;
156
+ }
157
+ if (dowRaw === "1-5" && hourRaw !== void 0 && minuteRaw !== void 0 && !Number.isNaN(hour) && !Number.isNaN(minute)) {
158
+ return `Weekdays ${formatClock({ hour, minute })} ${abbr}`;
159
+ }
160
+ if (dowRaw != null && /^\d(?:,\d)*$/.test(dowRaw) && hourRaw !== void 0 && minuteRaw !== void 0 && !Number.isNaN(hour) && !Number.isNaN(minute)) {
161
+ const labels = dowRaw.split(",").map((d) => WEEKDAY_OPTIONS.find((w) => w.value === Number(d))?.label ?? d);
162
+ if (labels.length === 1) {
163
+ const day = labels[0] === "Mon" ? "Monday" : labels[0];
164
+ return `Every ${day} ${formatClock({ hour, minute })} ${abbr}`;
165
+ }
166
+ return `${labels.join(", ")} ${formatClock({ hour, minute })} ${abbr}`;
167
+ }
168
+ return input.cron;
169
+ }
170
+ function cronToFormValues(input) {
171
+ const base = defaultScheduleFormValues();
172
+ const parts = input.cron.trim().split(/\s+/);
173
+ if (parts.length !== 5) {
174
+ return {
175
+ ...base,
176
+ name: input.name,
177
+ task: input.task,
178
+ timezone: input.timezone,
179
+ recurrence: "custom",
180
+ customCron: input.cron
181
+ };
182
+ }
183
+ const [minuteRaw, hourRaw, dom, month, dowRaw] = parts;
184
+ const minute = Number(minuteRaw);
185
+ const hour = Number(hourRaw);
186
+ if (dom !== "*" || month !== "*") {
187
+ return {
188
+ ...base,
189
+ name: input.name,
190
+ task: input.task,
191
+ timezone: input.timezone,
192
+ recurrence: "custom",
193
+ customCron: input.cron
194
+ };
195
+ }
196
+ if (hourRaw === "*" && dowRaw === "*" && !Number.isNaN(minute)) {
197
+ return {
198
+ ...base,
199
+ name: input.name,
200
+ task: input.task,
201
+ timezone: input.timezone,
202
+ recurrence: "hourly",
203
+ minute,
204
+ customCron: input.cron
205
+ };
206
+ }
207
+ if (dowRaw === "*" && !Number.isNaN(hour) && !Number.isNaN(minute)) {
208
+ return {
209
+ ...base,
210
+ name: input.name,
211
+ task: input.task,
212
+ timezone: input.timezone,
213
+ recurrence: "daily",
214
+ hour,
215
+ minute,
216
+ customCron: input.cron
217
+ };
218
+ }
219
+ if (dowRaw != null && /^\d(?:,\d)*$/.test(dowRaw) && !Number.isNaN(hour) && !Number.isNaN(minute)) {
220
+ return {
221
+ ...base,
222
+ name: input.name,
223
+ task: input.task,
224
+ timezone: input.timezone,
225
+ recurrence: "weekly",
226
+ hour,
227
+ minute,
228
+ weekdays: dowRaw.split(",").map(Number),
229
+ customCron: input.cron
230
+ };
231
+ }
232
+ return {
233
+ ...base,
234
+ name: input.name,
235
+ task: input.task,
236
+ timezone: input.timezone,
237
+ recurrence: "custom",
238
+ customCron: input.cron,
239
+ hour: Number.isNaN(hour) ? base.hour : hour,
240
+ minute: Number.isNaN(minute) ? base.minute : minute
241
+ };
242
+ }
243
+ function formatRelativeTime(iso, nowMs = Date.now()) {
244
+ if (iso == null) return "Never";
245
+ const then = Date.parse(iso);
246
+ if (Number.isNaN(then)) return "Never";
247
+ const deltaSec = Math.round((nowMs - then) / 1e3);
248
+ if (deltaSec < 60) return "just now";
249
+ const mins = Math.round(deltaSec / 60);
250
+ if (mins < 60) return `${String(mins)} min ago`;
251
+ const hours = Math.round(mins / 60);
252
+ if (hours < 48) return `${String(hours)} hour${hours === 1 ? "" : "s"} ago`;
253
+ const days = Math.round(hours / 24);
254
+ return `${String(days)} day${days === 1 ? "" : "s"} ago`;
255
+ }
256
+
257
+ // src/atoms/schedules/ScheduleFormDrawer.tsx
258
+ import { useEffect as useEffect2, useMemo, useState as useState2 } from "react";
259
+
260
+ // src/atoms/schedules/ScheduleFormFields.tsx
261
+ import { jsx, jsxs } from "react/jsx-runtime";
262
+ var RECURRENCE_OPTIONS = [
263
+ { value: "hourly", label: "Hourly" },
264
+ { value: "daily", label: "Daily" },
265
+ { value: "weekly", label: "Weekly" }
266
+ ];
267
+ var HOUR_OPTIONS = Array.from({ length: 24 }, (_, hour) => ({
268
+ value: String(hour),
269
+ label: hour.toString().padStart(2, "0")
270
+ }));
271
+ var MINUTE_OPTIONS = [0, 15, 30, 45].map((minute) => ({
272
+ value: String(minute),
273
+ label: `:${minute.toString().padStart(2, "0")}`
274
+ }));
275
+ function ScheduleFormFields({
276
+ values,
277
+ onChange,
278
+ agentId,
279
+ onAgentIdChange,
280
+ agentOptions,
281
+ agentPickerDisabled = false
282
+ }) {
283
+ const cron = valuesToCron(values);
284
+ const cadence = formatCadenceSummary({ cron, timezone: values.timezone });
285
+ const timezoneOptions = getTimezoneOptions(values.timezone);
286
+ const set = (key, value) => {
287
+ onChange({ ...values, [key]: value });
288
+ };
289
+ const toggleWeekday = (day) => {
290
+ const has = values.weekdays.includes(day);
291
+ const weekdays = has ? values.weekdays.filter((d) => d !== day) : [...values.weekdays, day];
292
+ onChange({ ...values, weekdays: weekdays.length > 0 ? weekdays : [day] });
293
+ };
294
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4 px-5 py-4", children: [
295
+ /* @__PURE__ */ jsxs("div", { className: "block", children: [
296
+ /* @__PURE__ */ jsx("span", { className: "mb-1.5 block text-sm font-medium", children: "Agent" }),
297
+ /* @__PURE__ */ jsx(
298
+ PopoverSelect,
299
+ {
300
+ "aria-label": "Agent",
301
+ placeholder: "Select an agent",
302
+ value: agentId,
303
+ options: agentOptions.map((agent) => ({ value: agent.agentId, label: agent.name })),
304
+ onValueChange: (value) => onAgentIdChange?.(value),
305
+ disabled: agentPickerDisabled || onAgentIdChange == null
306
+ }
307
+ )
308
+ ] }),
309
+ /* @__PURE__ */ jsxs("label", { className: "block", children: [
310
+ /* @__PURE__ */ jsx("span", { className: "mb-1.5 block text-sm font-medium", children: "Name" }),
311
+ /* @__PURE__ */ jsx(
312
+ "input",
313
+ {
314
+ value: values.name,
315
+ onChange: (e) => set("name", e.target.value),
316
+ placeholder: "harness-daily-digest",
317
+ className: auiInputClass("h-9"),
318
+ required: true
319
+ }
320
+ )
321
+ ] }),
322
+ /* @__PURE__ */ jsxs("label", { className: "block", children: [
323
+ /* @__PURE__ */ jsx("span", { className: "mb-1.5 block text-sm font-medium", children: "Task" }),
324
+ /* @__PURE__ */ jsx(
325
+ "textarea",
326
+ {
327
+ value: values.task,
328
+ onChange: (e) => set("task", e.target.value),
329
+ rows: 3,
330
+ placeholder: "Summarise yesterday's harness runs and post the digest\u2026",
331
+ className: auiInputClass("resize-y py-2"),
332
+ required: true
333
+ }
334
+ )
335
+ ] }),
336
+ /* @__PURE__ */ jsxs("fieldset", { children: [
337
+ /* @__PURE__ */ jsx("legend", { className: "mb-1.5 text-sm font-medium", children: "Recurrence" }),
338
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-3 gap-1 rounded-lg border border-border p-1", children: RECURRENCE_OPTIONS.map((opt) => {
339
+ const selected = values.recurrence === opt.value;
340
+ return /* @__PURE__ */ jsx(
341
+ "button",
342
+ {
343
+ type: "button",
344
+ className: cn(
345
+ "rounded-md px-2 py-1.5 text-xs font-medium transition-colors",
346
+ selected ? "border border-primary-button-bg/40 bg-primary-button-bg/10 text-primary-button-bg" : "text-text-secondary hover:bg-ghost-button-hover border border-transparent"
347
+ ),
348
+ "aria-pressed": selected,
349
+ onClick: () => set("recurrence", opt.value),
350
+ children: opt.label
351
+ },
352
+ opt.value
353
+ );
354
+ }) })
355
+ ] }),
356
+ values.recurrence === "hourly" ? /* @__PURE__ */ jsxs("div", { className: "block max-w-[8rem]", children: [
357
+ /* @__PURE__ */ jsx("span", { className: "mb-1.5 block text-sm font-medium", children: "Minute" }),
358
+ /* @__PURE__ */ jsx(
359
+ PopoverSelect,
360
+ {
361
+ "aria-label": "Minute",
362
+ value: String(values.minute),
363
+ options: MINUTE_OPTIONS,
364
+ onValueChange: (value) => set("minute", Number(value))
365
+ }
366
+ )
367
+ ] }) : null,
368
+ values.recurrence === "daily" || values.recurrence === "weekly" ? /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-3", children: [
369
+ /* @__PURE__ */ jsxs("div", { className: "block min-w-[7rem] flex-1", children: [
370
+ /* @__PURE__ */ jsx("span", { className: "mb-1.5 block text-sm font-medium", children: "Hour" }),
371
+ /* @__PURE__ */ jsx(
372
+ PopoverSelect,
373
+ {
374
+ "aria-label": "Hour",
375
+ value: String(values.hour),
376
+ options: HOUR_OPTIONS,
377
+ onValueChange: (value) => set("hour", Number(value))
378
+ }
379
+ )
380
+ ] }),
381
+ /* @__PURE__ */ jsxs("div", { className: "block min-w-[7rem] flex-1", children: [
382
+ /* @__PURE__ */ jsx("span", { className: "mb-1.5 block text-sm font-medium", children: "Minute" }),
383
+ /* @__PURE__ */ jsx(
384
+ PopoverSelect,
385
+ {
386
+ "aria-label": "Minute",
387
+ value: String(values.minute),
388
+ options: MINUTE_OPTIONS,
389
+ onValueChange: (value) => set("minute", Number(value))
390
+ }
391
+ )
392
+ ] })
393
+ ] }) : null,
394
+ values.recurrence === "weekly" ? /* @__PURE__ */ jsxs("fieldset", { children: [
395
+ /* @__PURE__ */ jsx("legend", { className: "mb-1.5 text-sm font-medium", children: "Days" }),
396
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: WEEKDAY_OPTIONS.map((day) => {
397
+ const selected = values.weekdays.includes(day.value);
398
+ return /* @__PURE__ */ jsx(
399
+ "button",
400
+ {
401
+ type: "button",
402
+ className: cn(
403
+ "rounded-md border px-2.5 py-1 text-xs font-medium",
404
+ selected ? "border-primary-button-bg/40 bg-primary-button-bg/10 text-primary-button-bg" : "border-border text-text-secondary hover:bg-ghost-button-hover"
405
+ ),
406
+ "aria-pressed": selected,
407
+ onClick: () => toggleWeekday(day.value),
408
+ children: day.label
409
+ },
410
+ day.value
411
+ );
412
+ }) })
413
+ ] }) : null,
414
+ values.recurrence === "custom" ? /* @__PURE__ */ jsxs("div", { className: "rounded-md border border-border bg-secondary-bg/40 px-3 py-2", children: [
415
+ /* @__PURE__ */ jsx("span", { className: "text-text-secondary block text-xs", children: "Existing custom schedule" }),
416
+ /* @__PURE__ */ jsx("code", { className: "text-text-primary mt-1 block font-mono text-sm", children: values.customCron }),
417
+ /* @__PURE__ */ jsx("span", { className: "text-text-secondary mt-1 block text-xs", children: "Select Hourly, Daily, or Weekly to replace this schedule." })
418
+ ] }) : null,
419
+ /* @__PURE__ */ jsxs("div", { className: "block", children: [
420
+ /* @__PURE__ */ jsx("span", { className: "mb-1.5 block text-sm font-medium", children: "Timezone" }),
421
+ /* @__PURE__ */ jsx(
422
+ PopoverSelect,
423
+ {
424
+ "aria-label": "Timezone",
425
+ value: values.timezone,
426
+ options: timezoneOptions,
427
+ onValueChange: (value) => set("timezone", value),
428
+ menuPlacement: "top"
429
+ }
430
+ )
431
+ ] }),
432
+ /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-border bg-secondary-bg/40 px-3 py-3", children: [
433
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3", children: [
434
+ /* @__PURE__ */ jsx("span", { className: "text-text-secondary text-[10px] font-semibold tracking-wide uppercase", children: "Frequency" }),
435
+ /* @__PURE__ */ jsx("code", { className: "text-text-secondary font-mono text-xs", children: cron || "\u2014" })
436
+ ] }),
437
+ /* @__PURE__ */ jsx("p", { className: "text-text-primary mt-1 text-sm font-semibold", children: cadence || "\u2014" })
438
+ ] })
439
+ ] });
440
+ }
441
+
442
+ // src/atoms/schedules/TestScheduleScreen.tsx
443
+ import { useEffect, useState } from "react";
444
+
445
+ // src/atoms/schedules/scheduleRuns.ts
446
+ function isManualRun(name) {
447
+ return name.startsWith("manual-");
448
+ }
449
+ function runChipKind(status) {
450
+ if (status === "failed") return "failed";
451
+ return "success";
452
+ }
453
+ function runStatusLabel(status) {
454
+ if (status === "failed") return "Failed";
455
+ if (status === "triggered") return "Triggered";
456
+ return "Scheduled";
457
+ }
458
+ function runTypeLabel(name) {
459
+ return isManualRun(name) ? "Manual test" : "Cron";
460
+ }
461
+ function lastHistoricalRuns(runs, limit = 5) {
462
+ const historical = runs.filter((run) => run.status !== "scheduled");
463
+ return historical.slice(0, limit).reverse();
464
+ }
465
+ function formatScheduleRunInstant(iso) {
466
+ if (iso == null) return "\u2014";
467
+ const date = new Date(iso);
468
+ if (Number.isNaN(date.getTime())) return "\u2014";
469
+ return new Intl.DateTimeFormat(void 0, {
470
+ month: "short",
471
+ day: "numeric",
472
+ year: "numeric",
473
+ hour: "numeric",
474
+ minute: "2-digit",
475
+ hour12: true
476
+ }).format(date);
477
+ }
478
+
479
+ // src/atoms/schedules/ScheduleRunTooltip.tsx
480
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
481
+ function ScheduleRunTooltip({ run }) {
482
+ return /* @__PURE__ */ jsxs2("div", { className: "flex flex-col gap-1 text-left text-xs", children: [
483
+ /* @__PURE__ */ jsx2("p", { className: "text-text-primary font-medium", children: runStatusLabel(run.status) }),
484
+ /* @__PURE__ */ jsx2("p", { className: "text-text-secondary", children: runTypeLabel(run.name) }),
485
+ /* @__PURE__ */ jsxs2("p", { className: "text-text-secondary", children: [
486
+ "Scheduled for: ",
487
+ formatScheduleRunInstant(run.scheduledFor)
488
+ ] }),
489
+ run.triggeredAt != null ? /* @__PURE__ */ jsxs2("p", { className: "text-text-secondary", children: [
490
+ "Triggered at: ",
491
+ formatScheduleRunInstant(run.triggeredAt)
492
+ ] }) : null,
493
+ /* @__PURE__ */ jsxs2("p", { className: "text-text-secondary", children: [
494
+ "Triggered by: ",
495
+ run.triggeredBy
496
+ ] })
497
+ ] });
498
+ }
499
+
500
+ // src/atoms/schedules/ScheduleRunChip.tsx
501
+ import { jsx as jsx3 } from "react/jsx-runtime";
502
+ var CHIP_STYLES = {
503
+ success: "border-emerald-600/30 bg-emerald-500/10 text-emerald-700 dark:border-emerald-400/35 dark:bg-emerald-500/15 dark:text-emerald-300",
504
+ failed: "border-red-600/30 bg-red-500/10 text-red-700 dark:border-red-400/35 dark:bg-red-500/15 dark:text-red-300"
505
+ };
506
+ function ScheduleRunChip({ run }) {
507
+ const kind = runChipKind(run.status);
508
+ const when = formatScheduleRunInstant(run.triggeredAt ?? run.scheduledFor);
509
+ return /* @__PURE__ */ jsx3(Tooltip, { content: /* @__PURE__ */ jsx3(ScheduleRunTooltip, { run }), side: "top", children: /* @__PURE__ */ jsx3(
510
+ "span",
511
+ {
512
+ className: cn("inline-flex size-6 shrink-0 items-center justify-center rounded-md border", CHIP_STYLES[kind]),
513
+ "aria-label": `${runStatusLabel(run.status)} run at ${when}`,
514
+ children: /* @__PURE__ */ jsx3(Icon, { name: kind === "failed" ? "triangle-exclamation" : "check", className: "size-3.5" })
515
+ }
516
+ ) });
517
+ }
518
+
519
+ // src/atoms/schedules/ScheduleStatusBadge.tsx
520
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
521
+ function ScheduleStatusBadge({ status }) {
522
+ const active = status === "active";
523
+ return /* @__PURE__ */ jsxs3(
524
+ "span",
525
+ {
526
+ className: cn(
527
+ "inline-flex items-center gap-1.5 rounded-full border px-2.5 py-0.5 text-xs font-medium",
528
+ active ? "border-emerald-600/30 bg-emerald-500/10 text-emerald-700 dark:border-emerald-400/35 dark:bg-emerald-500/15 dark:text-emerald-300" : "border-border bg-secondary-bg text-text-secondary"
529
+ ),
530
+ children: [
531
+ /* @__PURE__ */ jsx4(
532
+ "span",
533
+ {
534
+ className: cn(
535
+ "size-1.5 shrink-0 rounded-full",
536
+ active ? "bg-emerald-600 dark:bg-emerald-400" : "bg-text-secondary"
537
+ ),
538
+ "aria-hidden": true
539
+ }
540
+ ),
541
+ active ? "Active" : "Paused"
542
+ ]
543
+ }
544
+ );
545
+ }
546
+
547
+ // src/atoms/schedules/TestScheduleScreen.tsx
548
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
549
+ function mountConnector(connectors, name) {
550
+ return connectors.find((connector) => connector.name === name || connector.id === name) ?? null;
551
+ }
552
+ function ScheduleMcpConnect({
553
+ connector,
554
+ onConnected
555
+ }) {
556
+ const catalog = useOptionalCatalogServer();
557
+ if (catalog == null) return null;
558
+ return /* @__PURE__ */ jsx5(ConnectorConnectButton, { connector, onConnected });
559
+ }
560
+ function DetailRow({ label, children }) {
561
+ return /* @__PURE__ */ jsxs4("div", { className: "flex items-start justify-between gap-3 border-b border-border px-3 py-2.5 last:border-b-0", children: [
562
+ /* @__PURE__ */ jsx5("span", { className: "text-text-secondary shrink-0 text-[10px] font-semibold tracking-wide uppercase", children: label }),
563
+ /* @__PURE__ */ jsx5("div", { className: "min-w-0 text-right text-sm text-text-primary", children })
564
+ ] });
565
+ }
566
+ function TestScheduleScreen({ schedule, agentName, mcpMounts, onEditConfiguration }) {
567
+ const scheduleServer = useScheduleServer();
568
+ const toaster = useToasterOptional();
569
+ const { connectors, ensureLoaded, refreshConnectors, loading, error } = useDraftCatalog();
570
+ const [running, setRunning] = useState(false);
571
+ const [lastTestRun, setLastTestRun] = useState(null);
572
+ const cadence = formatCadenceSummary({ cron: schedule.cron, timezone: schedule.timezone });
573
+ const handleRunTest = async () => {
574
+ setRunning(true);
575
+ try {
576
+ const run = await scheduleServer.createScheduleRun({ scheduleId: schedule.id });
577
+ setLastTestRun(run);
578
+ toaster?.showSuccess({ title: "Test run started" });
579
+ } catch (caught) {
580
+ toaster?.showError(caught);
581
+ } finally {
582
+ setRunning(false);
583
+ }
584
+ };
585
+ useEffect(() => {
586
+ ensureLoaded();
587
+ }, [ensureLoaded]);
588
+ useEffect(() => {
589
+ if (error != null) toaster?.showError(error);
590
+ }, [error, toaster]);
591
+ return /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-4 px-5 py-4", children: [
592
+ /* @__PURE__ */ jsxs4("section", { className: "overflow-hidden rounded-lg border border-border bg-card-bg", children: [
593
+ /* @__PURE__ */ jsx5(DetailRow, { label: "Agent", children: agentName }),
594
+ /* @__PURE__ */ jsx5(DetailRow, { label: "Name", children: schedule.name }),
595
+ /* @__PURE__ */ jsx5(DetailRow, { label: "Task", children: /* @__PURE__ */ jsx5("span", { className: "whitespace-pre-wrap break-words", children: schedule.task }) }),
596
+ /* @__PURE__ */ jsx5(DetailRow, { label: "Cadence", children: /* @__PURE__ */ jsxs4("div", { className: "flex flex-wrap items-center justify-end gap-1.5", children: [
597
+ /* @__PURE__ */ jsx5("span", { children: cadence }),
598
+ /* @__PURE__ */ jsx5("code", { className: "text-text-secondary font-mono text-xs", children: schedule.cron })
599
+ ] }) }),
600
+ /* @__PURE__ */ jsx5(DetailRow, { label: "Status", children: /* @__PURE__ */ jsx5(ScheduleStatusBadge, { status: schedule.status }) }),
601
+ /* @__PURE__ */ jsx5("div", { className: "flex justify-end px-3 py-2.5", children: /* @__PURE__ */ jsxs4(Button, { type: "button", variant: "outline", size: "sm", onClick: onEditConfiguration, children: [
602
+ /* @__PURE__ */ jsx5(Icon, { name: "pencil", className: "size-3.5" }),
603
+ "Edit Configuration"
604
+ ] }) })
605
+ ] }),
606
+ /* @__PURE__ */ jsxs4("section", { className: "overflow-hidden rounded-lg border border-border bg-card-bg", children: [
607
+ /* @__PURE__ */ jsx5("div", { className: "border-b border-border px-3 py-2", children: /* @__PURE__ */ jsx5("h3", { className: "text-text-secondary text-[10px] font-semibold tracking-wide uppercase", children: "Tool Authorization" }) }),
608
+ mcpMounts.length === 0 ? /* @__PURE__ */ jsx5("p", { className: "text-text-secondary px-3 py-3 text-sm", children: "No MCP connectors configured for this agent." }) : /* @__PURE__ */ jsx5("ul", { className: "divide-y divide-border", children: mcpMounts.map((mount) => {
609
+ const connector = mountConnector(connectors, mount.name);
610
+ const authenticated = connector?.authenticated === true;
611
+ const unavailable = connector == null && !loading;
612
+ return /* @__PURE__ */ jsxs4("li", { className: "flex items-center gap-2 px-3 py-2.5", children: [
613
+ /* @__PURE__ */ jsx5(
614
+ "span",
615
+ {
616
+ className: cn(
617
+ "inline-flex size-4 shrink-0 items-center justify-center rounded-full border",
618
+ authenticated ? "border-emerald-600/40 bg-emerald-500/15 text-emerald-700 dark:text-emerald-300" : "border-border text-transparent"
619
+ ),
620
+ "aria-hidden": true,
621
+ children: authenticated ? /* @__PURE__ */ jsx5(Icon, { name: "check", className: "size-3" }) : null
622
+ }
623
+ ),
624
+ /* @__PURE__ */ jsx5("span", { className: "text-text-primary min-w-0 flex-1 truncate text-sm", children: mount.name }),
625
+ authenticated ? /* @__PURE__ */ jsx5("span", { className: "text-success-bg text-xs font-medium", children: "Connected" }) : unavailable ? /* @__PURE__ */ jsx5("span", { className: "text-text-secondary text-xs", children: "Unavailable" }) : connector != null && connector.authenticated === false ? /* @__PURE__ */ jsx5(ScheduleMcpConnect, { connector, onConnected: refreshConnectors }) : loading ? /* @__PURE__ */ jsx5("span", { className: "text-text-secondary text-xs", children: "Loading\u2026" }) : null
626
+ ] }, mount.name);
627
+ }) })
628
+ ] }),
629
+ /* @__PURE__ */ jsxs4("section", { className: "overflow-hidden rounded-lg border border-border bg-card-bg", children: [
630
+ /* @__PURE__ */ jsxs4("div", { className: "flex items-center justify-between gap-2 border-b border-border px-3 py-2", children: [
631
+ /* @__PURE__ */ jsx5("h3", { className: "text-text-secondary text-[10px] font-semibold tracking-wide uppercase", children: "Test Run" }),
632
+ /* @__PURE__ */ jsxs4(
633
+ "button",
634
+ {
635
+ type: "button",
636
+ disabled: running,
637
+ onClick: () => void handleRunTest(),
638
+ className: auiButtonClass({ variant: "default", size: "sm" }),
639
+ children: [
640
+ /* @__PURE__ */ jsx5(Icon, { name: running ? "loader" : "play", className: cn("size-3.5", running && "animate-spin") }),
641
+ "Run Test"
642
+ ]
643
+ }
644
+ )
645
+ ] }),
646
+ lastTestRun == null ? /* @__PURE__ */ jsx5("p", { className: "text-text-secondary px-3 py-3 text-sm", children: "Run a one-off test using the schedule task. This does not change the cron pending run." }) : /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-3 px-3 py-3", children: [
647
+ /* @__PURE__ */ jsx5(ScheduleRunChip, { run: lastTestRun }),
648
+ /* @__PURE__ */ jsxs4("div", { className: "min-w-0 text-sm", children: [
649
+ /* @__PURE__ */ jsx5("p", { className: "text-text-primary font-medium", children: runTypeLabel(lastTestRun.name) }),
650
+ /* @__PURE__ */ jsx5("p", { className: "text-text-secondary", children: formatRelativeTime(lastTestRun.triggeredAt ?? lastTestRun.scheduledFor) })
651
+ ] })
652
+ ] })
653
+ ] })
654
+ ] });
655
+ }
656
+
657
+ // src/atoms/schedules/ScheduleFormDrawer.tsx
658
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
659
+ function ScheduleFormDrawerBody({
660
+ open,
661
+ onOpenChange,
662
+ mode,
663
+ schedule,
664
+ initialAgentId = "",
665
+ onSaved
666
+ }) {
667
+ const scheduleServer = useScheduleServer();
668
+ const server = useServer();
669
+ const toaster = useToasterOptional();
670
+ const [form, setForm] = useState2(defaultScheduleFormValues);
671
+ const [agentId, setAgentId] = useState2(initialAgentId);
672
+ const [agents, setAgents] = useState2([]);
673
+ const [view, setView] = useState2({ kind: "form" });
674
+ const [saving, setSaving] = useState2(false);
675
+ const [activating, setActivating] = useState2(false);
676
+ const [error, setError] = useState2(null);
677
+ const savedFromCreate = view.kind === "form" ? view.saved : view.schedule;
678
+ const isExternalEdit = mode === "edit" && view.kind === "form" && view.saved == null && schedule != null;
679
+ const isCreatedEdit = view.kind === "form" && view.saved != null;
680
+ useEffect2(() => {
681
+ if (!open) return;
682
+ let cancelled = false;
683
+ void searchAllAgents(server).then((rows) => {
684
+ if (cancelled) return;
685
+ setAgents(rows);
686
+ }).catch(() => void 0);
687
+ return () => {
688
+ cancelled = true;
689
+ };
690
+ }, [open, server]);
691
+ useEffect2(() => {
692
+ if (!open) {
693
+ setForm(defaultScheduleFormValues());
694
+ setAgentId(initialAgentId);
695
+ setError(null);
696
+ setView({ kind: "form" });
697
+ setActivating(false);
698
+ return;
699
+ }
700
+ if (mode === "edit" && schedule != null) {
701
+ setForm(
702
+ cronToFormValues({
703
+ name: schedule.name,
704
+ task: schedule.task,
705
+ cron: schedule.cron,
706
+ timezone: schedule.timezone
707
+ })
708
+ );
709
+ setAgentId(schedule.agentId);
710
+ setView({ kind: "form" });
711
+ return;
712
+ }
713
+ setForm(defaultScheduleFormValues());
714
+ setAgentId(initialAgentId);
715
+ setView({ kind: "form" });
716
+ }, [open, mode, schedule, initialAgentId]);
717
+ const agentOptions = useMemo(
718
+ () => agents.map((agent) => ({ agentId: libraryAgentId(agent), name: agent.name })),
719
+ [agents]
720
+ );
721
+ const selectedAgent = useMemo(
722
+ () => agents.find((agent) => libraryAgentId(agent) === agentId) ?? null,
723
+ [agents, agentId]
724
+ );
725
+ const agentLabel = selectedAgent?.name ?? savedFromCreate?.agentName ?? schedule?.agentName ?? agentOptions.find((option) => option.agentId === agentId)?.name ?? agentId;
726
+ const mcpMounts = useMemo(() => {
727
+ const mounts = selectedAgent?.agentSpec?.mcpServers ?? [];
728
+ return mounts.map(mountName).filter((name) => name != null).map((name) => ({ name }));
729
+ }, [selectedAgent]);
730
+ const canSubmit = useMemo(() => {
731
+ const cron = valuesToCron(form);
732
+ return form.name.trim().length > 0 && form.task.trim().length > 0 && cron.length > 0 && agentId.length > 0;
733
+ }, [form, agentId]);
734
+ const enterTestView = (saved) => {
735
+ setView({ kind: "test", schedule: saved });
736
+ setForm(
737
+ cronToFormValues({
738
+ name: saved.name,
739
+ task: saved.task,
740
+ cron: saved.cron,
741
+ timezone: saved.timezone
742
+ })
743
+ );
744
+ setAgentId(saved.agentId);
745
+ };
746
+ const handleSave = async (event) => {
747
+ event.preventDefault();
748
+ if (!canSubmit) return;
749
+ const cron = valuesToCron(form);
750
+ setSaving(true);
751
+ setError(null);
752
+ try {
753
+ if (isExternalEdit && schedule != null) {
754
+ await scheduleServer.updateSchedule({
755
+ id: schedule.id,
756
+ name: form.name.trim(),
757
+ task: form.task.trim(),
758
+ cron,
759
+ timezone: form.timezone,
760
+ status: schedule.status
761
+ });
762
+ onSaved?.();
763
+ onOpenChange(false);
764
+ return;
765
+ }
766
+ if (isCreatedEdit && view.kind === "form" && view.saved != null) {
767
+ const saved2 = await scheduleServer.updateSchedule({
768
+ id: view.saved.id,
769
+ name: form.name.trim(),
770
+ task: form.task.trim(),
771
+ cron,
772
+ timezone: form.timezone,
773
+ status: "paused"
774
+ });
775
+ onSaved?.();
776
+ enterTestView(saved2);
777
+ toaster?.showSuccess({
778
+ title: "Schedule saved as paused",
779
+ description: formatCadenceSummary({ cron: saved2.cron, timezone: saved2.timezone })
780
+ });
781
+ return;
782
+ }
783
+ const saved = await scheduleServer.createSchedule({
784
+ agentId,
785
+ name: form.name.trim(),
786
+ task: form.task.trim(),
787
+ cron,
788
+ timezone: form.timezone,
789
+ status: "paused"
790
+ });
791
+ onSaved?.();
792
+ enterTestView(saved);
793
+ toaster?.showSuccess({
794
+ title: "Schedule saved as paused",
795
+ description: formatCadenceSummary({ cron: saved.cron, timezone: saved.timezone })
796
+ });
797
+ } catch (caught) {
798
+ const message = caught instanceof Error ? caught.message : "Failed to save schedule";
799
+ setError(message);
800
+ } finally {
801
+ setSaving(false);
802
+ }
803
+ };
804
+ const handleActivate = async () => {
805
+ if (view.kind !== "test") return;
806
+ setActivating(true);
807
+ setError(null);
808
+ try {
809
+ await scheduleServer.updateSchedule({
810
+ id: view.schedule.id,
811
+ name: view.schedule.name,
812
+ task: view.schedule.task,
813
+ cron: view.schedule.cron,
814
+ timezone: view.schedule.timezone,
815
+ status: "active"
816
+ });
817
+ onSaved?.();
818
+ toaster?.showSuccess({ title: "Schedule activated" });
819
+ onOpenChange(false);
820
+ } catch (caught) {
821
+ const message = caught instanceof Error ? caught.message : "Failed to activate schedule";
822
+ setError(message);
823
+ toaster?.showError(caught);
824
+ } finally {
825
+ setActivating(false);
826
+ }
827
+ };
828
+ const title = view.kind === "test" ? "Test Schedule" : isExternalEdit || isCreatedEdit ? "Edit Schedule" : "New Schedule";
829
+ const description = view.kind === "test" ? `Recurring unattended runs of ${agentLabel}.` : isExternalEdit || isCreatedEdit ? "Update cadence and task for this schedule." : "Create a recurring unattended run for an agent.";
830
+ let footer;
831
+ if (view.kind === "test") {
832
+ footer = /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-2", children: [
833
+ error != null ? /* @__PURE__ */ jsx6("p", { className: "text-failure-bg text-sm", children: error }) : null,
834
+ /* @__PURE__ */ jsx6(
835
+ Button,
836
+ {
837
+ type: "button",
838
+ variant: "secondary",
839
+ className: "w-full",
840
+ disabled: activating,
841
+ onClick: () => void handleActivate(),
842
+ children: "Activate Anyway"
843
+ }
844
+ )
845
+ ] });
846
+ } else {
847
+ footer = /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-2", children: [
848
+ error != null ? /* @__PURE__ */ jsx6("p", { className: "text-failure-bg text-sm", children: error }) : null,
849
+ /* @__PURE__ */ jsxs5("div", { className: "flex justify-end gap-2", children: [
850
+ /* @__PURE__ */ jsx6(Button, { type: "button", variant: "outline", onClick: () => onOpenChange(false), disabled: saving, children: "Cancel" }),
851
+ /* @__PURE__ */ jsx6(Button, { type: "submit", form: "schedule-form", disabled: !canSubmit || saving, children: "Save" })
852
+ ] })
853
+ ] });
854
+ }
855
+ return /* @__PURE__ */ jsx6(
856
+ SideDrawer,
857
+ {
858
+ open,
859
+ onOpenChange,
860
+ title,
861
+ description,
862
+ anchor: "right",
863
+ size: "lg",
864
+ headerIcon: /* @__PURE__ */ jsx6("span", { className: "text-primary-button-bg inline-flex size-8 items-center justify-center", children: /* @__PURE__ */ jsx6(
865
+ Icon,
866
+ {
867
+ name: view.kind === "test" || isExternalEdit || isCreatedEdit ? "calendar" : "calendar-plus",
868
+ className: "size-5"
869
+ }
870
+ ) }),
871
+ footer,
872
+ children: view.kind === "test" ? /* @__PURE__ */ jsx6(
873
+ TestScheduleScreen,
874
+ {
875
+ schedule: view.schedule,
876
+ agentName: agentLabel,
877
+ mcpMounts,
878
+ onEditConfiguration: () => {
879
+ setError(null);
880
+ setView({ kind: "form", saved: view.schedule });
881
+ }
882
+ }
883
+ ) : /* @__PURE__ */ jsx6("form", { id: "schedule-form", onSubmit: (event) => void handleSave(event), children: /* @__PURE__ */ jsx6(
884
+ ScheduleFormFields,
885
+ {
886
+ values: form,
887
+ onChange: setForm,
888
+ agentId,
889
+ onAgentIdChange: isExternalEdit || isCreatedEdit ? void 0 : setAgentId,
890
+ agentOptions,
891
+ agentPickerDisabled: isExternalEdit || isCreatedEdit
892
+ }
893
+ ) })
894
+ }
895
+ );
896
+ }
897
+ function ScheduleFormDrawer(props) {
898
+ return /* @__PURE__ */ jsx6(DraftCatalogProvider, { children: /* @__PURE__ */ jsx6(ScheduleFormDrawerBody, { ...props }) });
899
+ }
900
+
901
+ // src/atoms/schedules/ScheduleLastRunsCell.tsx
902
+ import { jsx as jsx7 } from "react/jsx-runtime";
903
+ function ScheduleLastRunsCell({ runs }) {
904
+ const history = lastHistoricalRuns(runs);
905
+ if (history.length === 0) {
906
+ return /* @__PURE__ */ jsx7("span", { className: "text-text-secondary text-sm", children: "\u2014" });
907
+ }
908
+ return /* @__PURE__ */ jsx7("div", { className: "flex items-center gap-1", children: history.map((run) => /* @__PURE__ */ jsx7(ScheduleRunChip, { run }, run.id)) });
909
+ }
910
+
911
+ // src/atoms/schedules/SchedulesPage.tsx
912
+ import { Fragment, jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
913
+ var STATUS_FILTER_OPTIONS = [
914
+ { value: "all", label: "All statuses" },
915
+ { value: "active", label: "Active" },
916
+ { value: "paused", label: "Paused" }
917
+ ];
918
+ var SCHEDULES_PAGE_SIZE_OPTIONS = [10, 25];
919
+ function clampPageSize(size) {
920
+ return Math.min(Math.max(size, 1), 25);
921
+ }
922
+ function filtersFromSearch(search) {
923
+ const share = readScheduleShareSearch(search);
924
+ return {
925
+ nameQuery: share.q ?? "",
926
+ statusFilter: share.status ?? "all",
927
+ agentFilter: share.agent ?? "all"
928
+ };
929
+ }
930
+ function ScheduleRowActions({
931
+ schedule,
932
+ running,
933
+ onRunNow,
934
+ onEdit,
935
+ onTogglePause,
936
+ onDelete
937
+ }) {
938
+ return /* @__PURE__ */ jsxs6("div", { className: "inline-flex items-center justify-end gap-1.5", children: [
939
+ /* @__PURE__ */ jsxs6(
940
+ "button",
941
+ {
942
+ type: "button",
943
+ disabled: running,
944
+ "aria-label": `Run now ${schedule.name}`,
945
+ className: auiButtonClass({ variant: "outline", size: "sm" }),
946
+ onClick: onRunNow,
947
+ children: [
948
+ /* @__PURE__ */ jsx8(Icon, { name: running ? "loader" : "play", className: cn("size-3.5", running && "animate-spin") }),
949
+ "Run now"
950
+ ]
951
+ }
952
+ ),
953
+ /* @__PURE__ */ jsxs6(
954
+ DropdownMenu,
955
+ {
956
+ align: "end",
957
+ trigger: /* @__PURE__ */ jsx8(
958
+ "button",
959
+ {
960
+ type: "button",
961
+ className: auiButtonClass({ variant: "ghost", size: "icon" }),
962
+ "aria-label": `Actions for ${schedule.name}`,
963
+ children: /* @__PURE__ */ jsx8(Icon, { name: "ellipsis", className: "size-4" })
964
+ }
965
+ ),
966
+ children: [
967
+ /* @__PURE__ */ jsxs6(DropdownMenuItem, { onClick: onEdit, children: [
968
+ /* @__PURE__ */ jsx8(Icon, { name: "pencil", className: "size-3.5" }),
969
+ "Edit"
970
+ ] }),
971
+ /* @__PURE__ */ jsxs6(DropdownMenuItem, { onClick: onTogglePause, children: [
972
+ /* @__PURE__ */ jsx8(Icon, { name: schedule.status === "active" ? "pause" : "play", className: "size-3.5" }),
973
+ schedule.status === "active" ? "Pause" : "Resume"
974
+ ] }),
975
+ /* @__PURE__ */ jsxs6(DropdownMenuItem, { className: "text-failure-bg focus:text-failure-bg", onClick: onDelete, children: [
976
+ /* @__PURE__ */ jsx8(Icon, { name: "trash", className: "size-3.5" }),
977
+ "Delete"
978
+ ] })
979
+ ]
980
+ }
981
+ )
982
+ ] });
983
+ }
984
+ function SchedulesPage() {
985
+ const scheduleServer = useScheduleServer();
986
+ const server = useServer();
987
+ const toaster = useToasterOptional();
988
+ const [schedules, setSchedules] = useState3([]);
989
+ const [runsByScheduleId, setRunsByScheduleId] = useState3({});
990
+ const [runsLoading, setRunsLoading] = useState3(false);
991
+ const [runningScheduleIds, setRunningScheduleIds] = useState3(() => /* @__PURE__ */ new Set());
992
+ const [agentOptions, setAgentOptions] = useState3([]);
993
+ const [loading, setLoading] = useState3(true);
994
+ const [error, setError] = useState3(null);
995
+ const [nameQuery, setNameQuery] = useState3(() => filtersFromSearch(window.location.search).nameQuery);
996
+ const [statusFilter, setStatusFilter] = useState3(
997
+ () => filtersFromSearch(window.location.search).statusFilter
998
+ );
999
+ const [agentFilter, setAgentFilter] = useState3(() => filtersFromSearch(window.location.search).agentFilter);
1000
+ const [drawer, setDrawer] = useState3({ kind: "closed" });
1001
+ const [pendingDelete, setPendingDelete] = useState3(null);
1002
+ const [pageSize, setPageSize] = useState3(() => clampPageSize(DEFAULT_TABLE_PAGE_SIZE));
1003
+ const [pageToken, setPageToken] = useState3(void 0);
1004
+ const [nextPageToken, setNextPageToken] = useState3(void 0);
1005
+ const [prevTokenStack, setPrevTokenStack] = useState3([]);
1006
+ const loadGenRef = useRef(0);
1007
+ const didConsumeIsNewRef = useRef(false);
1008
+ const loadRunsForSchedules = useCallback(
1009
+ async ({ rows, gen }) => {
1010
+ if (rows.length === 0) {
1011
+ if (gen === loadGenRef.current) setRunsByScheduleId({});
1012
+ return;
1013
+ }
1014
+ setRunsLoading(true);
1015
+ try {
1016
+ const results = await Promise.allSettled(
1017
+ rows.map((schedule) => scheduleServer.listScheduleRuns({ scheduleId: schedule.id }))
1018
+ );
1019
+ if (gen !== loadGenRef.current) return;
1020
+ setRunsByScheduleId(
1021
+ Object.fromEntries(
1022
+ rows.map((schedule, index) => {
1023
+ const result = results[index];
1024
+ const runs = result?.status === "fulfilled" ? result.value : [];
1025
+ return [schedule.id, runs];
1026
+ })
1027
+ )
1028
+ );
1029
+ } finally {
1030
+ if (gen === loadGenRef.current) setRunsLoading(false);
1031
+ }
1032
+ },
1033
+ [scheduleServer]
1034
+ );
1035
+ const loadSchedules = useCallback(
1036
+ async ({ token, size, agentId }) => {
1037
+ const gen = ++loadGenRef.current;
1038
+ setLoading(true);
1039
+ setError(null);
1040
+ try {
1041
+ const page = await scheduleServer.listSchedules({
1042
+ limit: clampPageSize(size),
1043
+ ...token === void 0 || token === "" ? {} : { pageToken: token },
1044
+ ...agentId === "all" ? {} : { agentIds: [agentId] }
1045
+ });
1046
+ if (gen !== loadGenRef.current) return;
1047
+ setSchedules(page.data);
1048
+ setNextPageToken(page.nextPageToken);
1049
+ void loadRunsForSchedules({ rows: page.data, gen });
1050
+ } catch (caught) {
1051
+ if (gen !== loadGenRef.current) return;
1052
+ const message = caught instanceof Error ? caught.message : "Failed to load schedules";
1053
+ setError(message);
1054
+ setSchedules([]);
1055
+ setRunsByScheduleId({});
1056
+ setNextPageToken(void 0);
1057
+ } finally {
1058
+ if (gen === loadGenRef.current) setLoading(false);
1059
+ }
1060
+ },
1061
+ [scheduleServer, loadRunsForSchedules]
1062
+ );
1063
+ const resetToFirstPage = useCallback(
1064
+ (next) => {
1065
+ const size = next?.size ?? pageSize;
1066
+ const agentId = next?.agentId ?? agentFilter;
1067
+ setPageToken(void 0);
1068
+ setPrevTokenStack([]);
1069
+ void loadSchedules({ token: void 0, size, agentId });
1070
+ },
1071
+ [agentFilter, loadSchedules, pageSize]
1072
+ );
1073
+ useEffect3(() => {
1074
+ replaceScheduleShareSearch({
1075
+ agent: agentFilter === "all" ? null : agentFilter,
1076
+ status: statusFilter === "all" ? null : statusFilter,
1077
+ q: nameQuery.trim().length === 0 ? null : nameQuery
1078
+ });
1079
+ }, [agentFilter, statusFilter, nameQuery]);
1080
+ useEffect3(() => {
1081
+ if (didConsumeIsNewRef.current) return;
1082
+ const share = readScheduleShareSearch(window.location.search);
1083
+ if (!share.isNew) return;
1084
+ didConsumeIsNewRef.current = true;
1085
+ setDrawer({
1086
+ kind: "create",
1087
+ ...share.agent != null ? { agentId: share.agent } : {}
1088
+ });
1089
+ replaceScheduleShareSearch({ isNew: null });
1090
+ }, []);
1091
+ useEffect3(() => {
1092
+ const syncFromUrl = () => {
1093
+ const next = filtersFromSearch(window.location.search);
1094
+ setNameQuery(next.nameQuery);
1095
+ setStatusFilter(next.statusFilter);
1096
+ setAgentFilter((current) => {
1097
+ if (current === next.agentFilter) return current;
1098
+ setPageToken(void 0);
1099
+ setPrevTokenStack([]);
1100
+ return next.agentFilter;
1101
+ });
1102
+ };
1103
+ window.addEventListener("popstate", syncFromUrl);
1104
+ return () => {
1105
+ window.removeEventListener("popstate", syncFromUrl);
1106
+ };
1107
+ }, []);
1108
+ useEffect3(() => {
1109
+ void loadSchedules({ token: pageToken, size: pageSize, agentId: agentFilter });
1110
+ }, [agentFilter, pageSize, pageToken, loadSchedules]);
1111
+ useEffect3(() => {
1112
+ let cancelled = false;
1113
+ void searchAllAgents(server).then((rows) => {
1114
+ if (cancelled) return;
1115
+ setAgentOptions(rows.map((agent) => ({ agentId: libraryAgentId(agent), name: agent.name })));
1116
+ }).catch(() => void 0);
1117
+ return () => {
1118
+ cancelled = true;
1119
+ };
1120
+ }, [server]);
1121
+ const agentNameById = useMemo2(() => {
1122
+ const map = /* @__PURE__ */ new Map();
1123
+ for (const agent of agentOptions) {
1124
+ map.set(agent.agentId, agent.name);
1125
+ }
1126
+ return map;
1127
+ }, [agentOptions]);
1128
+ const filtered = useMemo2(() => {
1129
+ const q = nameQuery.trim().toLowerCase();
1130
+ return schedules.filter((schedule) => {
1131
+ if (statusFilter !== "all" && schedule.status !== statusFilter) return false;
1132
+ if (q.length > 0 && !schedule.name.toLowerCase().includes(q)) return false;
1133
+ return true;
1134
+ });
1135
+ }, [schedules, nameQuery, statusFilter]);
1136
+ const hasPageNav = prevTokenStack.length > 0 || nextPageToken != null;
1137
+ const handleTogglePause = async (schedule) => {
1138
+ const nextStatus = schedule.status === "active" ? "paused" : "active";
1139
+ try {
1140
+ await scheduleServer.updateSchedule({ ...schedule, status: nextStatus });
1141
+ await loadSchedules({ token: pageToken, size: pageSize, agentId: agentFilter });
1142
+ } catch (caught) {
1143
+ toaster?.showError(caught);
1144
+ }
1145
+ };
1146
+ const handleRunNow = async (schedule) => {
1147
+ setRunningScheduleIds((prev) => new Set(prev).add(schedule.id));
1148
+ try {
1149
+ await scheduleServer.createScheduleRun({ scheduleId: schedule.id });
1150
+ toaster?.showSuccess({ title: "Run started" });
1151
+ const runs = await scheduleServer.listScheduleRuns({ scheduleId: schedule.id });
1152
+ setRunsByScheduleId((prev) => ({ ...prev, [schedule.id]: runs }));
1153
+ } catch (caught) {
1154
+ toaster?.showError(caught);
1155
+ } finally {
1156
+ setRunningScheduleIds((prev) => {
1157
+ const next = new Set(prev);
1158
+ next.delete(schedule.id);
1159
+ return next;
1160
+ });
1161
+ }
1162
+ };
1163
+ const handleDelete = async (schedule) => {
1164
+ setPendingDelete(null);
1165
+ try {
1166
+ await scheduleServer.deleteSchedule({ id: schedule.id });
1167
+ resetToFirstPage();
1168
+ } catch (caught) {
1169
+ toaster?.showError(caught);
1170
+ }
1171
+ };
1172
+ const goNext = () => {
1173
+ if (nextPageToken == null) return;
1174
+ setPrevTokenStack((stack) => [...stack, pageToken ?? ""]);
1175
+ setPageToken(nextPageToken);
1176
+ };
1177
+ const goPrev = () => {
1178
+ if (prevTokenStack.length === 0) return;
1179
+ const stack = [...prevTokenStack];
1180
+ const prev = stack.pop();
1181
+ setPrevTokenStack(stack);
1182
+ setPageToken(prev === "" ? void 0 : prev);
1183
+ };
1184
+ return /* @__PURE__ */ jsxs6("div", { className: "flex h-full min-h-0 flex-col bg-primary-bg", children: [
1185
+ /* @__PURE__ */ jsx8(
1186
+ PageHeader,
1187
+ {
1188
+ title: "Scheduled Agents",
1189
+ end: /* @__PURE__ */ jsxs6(Fragment, { children: [
1190
+ /* @__PURE__ */ jsx8("div", { className: "w-full sm:w-56", children: /* @__PURE__ */ jsx8(SearchInput_default, { query: nameQuery, setQuery: setNameQuery, placeholder: "Search schedules by name" }) }),
1191
+ /* @__PURE__ */ jsx8(
1192
+ PopoverSelect,
1193
+ {
1194
+ value: statusFilter,
1195
+ onValueChange: setStatusFilter,
1196
+ options: STATUS_FILTER_OPTIONS,
1197
+ className: "sm:w-40",
1198
+ "aria-label": "Filter by status"
1199
+ }
1200
+ ),
1201
+ /* @__PURE__ */ jsx8(
1202
+ PopoverSelect,
1203
+ {
1204
+ value: agentFilter,
1205
+ onValueChange: (value) => {
1206
+ setAgentFilter(value);
1207
+ setPageToken(void 0);
1208
+ setPrevTokenStack([]);
1209
+ },
1210
+ options: [
1211
+ { value: "all", label: "All agents" },
1212
+ ...agentOptions.map((agent) => ({ value: agent.agentId, label: agent.name }))
1213
+ ],
1214
+ className: "sm:w-40",
1215
+ "aria-label": "Filter by agent"
1216
+ }
1217
+ ),
1218
+ /* @__PURE__ */ jsxs6(
1219
+ Button,
1220
+ {
1221
+ type: "button",
1222
+ onClick: () => setDrawer({
1223
+ kind: "create",
1224
+ agentId: agentFilter !== "all" ? agentFilter : void 0
1225
+ }),
1226
+ children: [
1227
+ /* @__PURE__ */ jsx8(Icon, { name: "plus", className: "size-3.5" }),
1228
+ "Create Schedule"
1229
+ ]
1230
+ }
1231
+ )
1232
+ ] })
1233
+ }
1234
+ ),
1235
+ /* @__PURE__ */ jsx8("div", { className: "min-h-0 flex-1 overflow-auto px-4 py-4 md:px-6", children: loading ? /* @__PURE__ */ jsx8("div", { className: "flex flex-col gap-2", role: "status", "aria-label": "Loading schedules", children: Array.from({ length: 5 }, (_, i) => /* @__PURE__ */ jsx8(Skeleton, { className: "h-12 w-full rounded-md" }, i)) }) : error != null ? /* @__PURE__ */ jsx8("p", { className: "text-failure-bg px-3 py-8 text-center text-sm", children: error }) : schedules.length === 0 ? /* @__PURE__ */ jsx8(EmptyScreen, { title: "No Schedules Found", description: "Create one to get started.", className: "min-h-full" }) : filtered.length === 0 ? /* @__PURE__ */ jsxs6("div", { className: "flex min-h-full flex-col", children: [
1236
+ /* @__PURE__ */ jsx8(EmptyScreen, { title: "No Schedules Found", description: "No schedules match your filters.", className: "flex-1" }),
1237
+ hasPageNav ? /* @__PURE__ */ jsx8(
1238
+ TableTokenPagination,
1239
+ {
1240
+ pageSize,
1241
+ rowCount: 0,
1242
+ canPrev: prevTokenStack.length > 0,
1243
+ canNext: nextPageToken != null,
1244
+ onPrev: goPrev,
1245
+ onNext: goNext,
1246
+ pageSizeOptions: SCHEDULES_PAGE_SIZE_OPTIONS,
1247
+ onPageSizeChange: (size) => {
1248
+ const next = clampPageSize(size);
1249
+ setPageSize(next);
1250
+ setPageToken(void 0);
1251
+ setPrevTokenStack([]);
1252
+ }
1253
+ }
1254
+ ) : null
1255
+ ] }) : /* @__PURE__ */ jsxs6("div", { className: "rounded-lg border border-border", children: [
1256
+ /* @__PURE__ */ jsxs6(Table, { className: "min-w-[48rem]", children: [
1257
+ /* @__PURE__ */ jsx8(TableHeader, { children: /* @__PURE__ */ jsxs6(TableRow, { className: "hover:bg-transparent", children: [
1258
+ /* @__PURE__ */ jsx8(TableHead, { children: "Name" }),
1259
+ /* @__PURE__ */ jsx8(TableHead, { children: "Agent" }),
1260
+ /* @__PURE__ */ jsx8(TableHead, { children: "Cadence" }),
1261
+ /* @__PURE__ */ jsx8(TableHead, { children: "Status" }),
1262
+ /* @__PURE__ */ jsx8(TableHead, { children: "Last 5 runs" }),
1263
+ /* @__PURE__ */ jsx8(TableHead, { children: /* @__PURE__ */ jsx8("span", { className: "sr-only", children: "Actions" }) })
1264
+ ] }) }),
1265
+ /* @__PURE__ */ jsx8(TableBody, { children: filtered.map((schedule) => {
1266
+ const cadence = formatCadenceSummary({ cron: schedule.cron, timezone: schedule.timezone });
1267
+ const agentLabel = schedule.agentName ?? agentNameById.get(schedule.agentId) ?? schedule.agentId;
1268
+ return /* @__PURE__ */ jsxs6(TableRow, { children: [
1269
+ /* @__PURE__ */ jsx8(TableCell, { className: "text-text-primary font-medium", children: /* @__PURE__ */ jsx8(
1270
+ "button",
1271
+ {
1272
+ type: "button",
1273
+ className: "text-primary-button-bg hover:underline text-left",
1274
+ onClick: () => setDrawer({ kind: "edit", schedule }),
1275
+ children: schedule.name
1276
+ }
1277
+ ) }),
1278
+ /* @__PURE__ */ jsx8(TableCell, { children: agentLabel }),
1279
+ /* @__PURE__ */ jsx8(TableCell, { children: cadence }),
1280
+ /* @__PURE__ */ jsx8(TableCell, { children: /* @__PURE__ */ jsx8(ScheduleStatusBadge, { status: schedule.status }) }),
1281
+ /* @__PURE__ */ jsx8(TableCell, { children: runsLoading ? /* @__PURE__ */ jsx8("span", { className: "text-text-secondary text-sm", children: "\u2026" }) : /* @__PURE__ */ jsx8(ScheduleLastRunsCell, { runs: runsByScheduleId[schedule.id] ?? [] }) }),
1282
+ /* @__PURE__ */ jsx8(TableCell, { className: "text-right", children: /* @__PURE__ */ jsx8(
1283
+ ScheduleRowActions,
1284
+ {
1285
+ schedule,
1286
+ running: runningScheduleIds.has(schedule.id),
1287
+ onRunNow: () => void handleRunNow(schedule),
1288
+ onEdit: () => setDrawer({ kind: "edit", schedule }),
1289
+ onTogglePause: () => void handleTogglePause(schedule),
1290
+ onDelete: () => setPendingDelete(schedule)
1291
+ }
1292
+ ) })
1293
+ ] }, schedule.id);
1294
+ }) })
1295
+ ] }),
1296
+ (filtered.length > 0 || hasPageNav) && /* @__PURE__ */ jsx8(
1297
+ TableTokenPagination,
1298
+ {
1299
+ pageSize,
1300
+ rowCount: filtered.length,
1301
+ canPrev: prevTokenStack.length > 0,
1302
+ canNext: nextPageToken != null,
1303
+ onPrev: goPrev,
1304
+ onNext: goNext,
1305
+ pageSizeOptions: SCHEDULES_PAGE_SIZE_OPTIONS,
1306
+ onPageSizeChange: (size) => {
1307
+ const next = clampPageSize(size);
1308
+ setPageSize(next);
1309
+ setPageToken(void 0);
1310
+ setPrevTokenStack([]);
1311
+ }
1312
+ }
1313
+ )
1314
+ ] }) }),
1315
+ drawer.kind === "create" ? /* @__PURE__ */ jsx8(
1316
+ ScheduleFormDrawer,
1317
+ {
1318
+ open: true,
1319
+ mode: "create",
1320
+ initialAgentId: drawer.agentId ?? "",
1321
+ onOpenChange: (open) => {
1322
+ if (!open) setDrawer({ kind: "closed" });
1323
+ },
1324
+ onSaved: () => resetToFirstPage()
1325
+ }
1326
+ ) : null,
1327
+ drawer.kind === "edit" ? /* @__PURE__ */ jsx8(
1328
+ ScheduleFormDrawer,
1329
+ {
1330
+ open: true,
1331
+ mode: "edit",
1332
+ schedule: drawer.schedule,
1333
+ onOpenChange: (open) => {
1334
+ if (!open) setDrawer({ kind: "closed" });
1335
+ },
1336
+ onSaved: () => void loadSchedules({ token: pageToken, size: pageSize, agentId: agentFilter })
1337
+ }
1338
+ ) : null,
1339
+ pendingDelete != null ? /* @__PURE__ */ jsxs6(
1340
+ Dialog,
1341
+ {
1342
+ open: true,
1343
+ onOpenChange: (open) => {
1344
+ if (!open) setPendingDelete(null);
1345
+ },
1346
+ "aria-label": "Delete schedule",
1347
+ className: "max-w-md",
1348
+ children: [
1349
+ /* @__PURE__ */ jsx8(DialogContent, { children: /* @__PURE__ */ jsxs6(DialogHeader, { children: [
1350
+ /* @__PURE__ */ jsx8(DialogTitle, { children: "Delete schedule" }),
1351
+ /* @__PURE__ */ jsxs6("p", { className: "text-text-secondary text-sm", children: [
1352
+ "\u201C",
1353
+ pendingDelete.name,
1354
+ "\u201D will stop running. This cannot be undone."
1355
+ ] })
1356
+ ] }) }),
1357
+ /* @__PURE__ */ jsxs6(DialogFooter, { children: [
1358
+ /* @__PURE__ */ jsx8(Button, { type: "button", variant: "secondary", onClick: () => setPendingDelete(null), children: "Cancel" }),
1359
+ /* @__PURE__ */ jsx8(Button, { type: "button", variant: "destructive", onClick: () => void handleDelete(pendingDelete), children: "Delete" })
1360
+ ] })
1361
+ ]
1362
+ }
1363
+ ) : null
1364
+ ] });
1365
+ }
1366
+
1367
+ export {
1368
+ ScheduleFormFields,
1369
+ ScheduleRunChip,
1370
+ ScheduleStatusBadge,
1371
+ TestScheduleScreen,
1372
+ ScheduleFormDrawer,
1373
+ ScheduleLastRunsCell,
1374
+ SchedulesPage
1375
+ };
1376
+ //# sourceMappingURL=chunk-6O6FWUDW.js.map