opencami 1.4.0 → 1.5.0

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 (77) hide show
  1. package/README.md +60 -0
  2. package/dist/client/assets/CSPContext-EgWK8bIJ.js +1 -0
  3. package/dist/client/assets/DirectionContext-DXtY05YF.js +1 -0
  4. package/dist/client/assets/_sessionKey-B89e7G3y.js +100 -0
  5. package/dist/client/assets/agents-screen-1BiEZ9od.js +1 -0
  6. package/dist/client/assets/agents-x54ocA9z.js +2 -0
  7. package/dist/client/assets/bots-screen-BNQciUeJ.js +1 -0
  8. package/dist/client/assets/bots-x86ZHG4b.js +2 -0
  9. package/dist/client/assets/button-nDcsaNPl.js +1 -0
  10. package/dist/client/assets/{connect-D3baVDFL.js → connect-w4lLOqiJ.js} +1 -1
  11. package/dist/client/assets/file-explorer-screen-CAsjd3w8.js +1 -0
  12. package/dist/client/assets/files-Bype5Mnb.js +2 -0
  13. package/dist/client/assets/{index-ByIsZcHh.js → index-36G0WCxU.js} +1 -1
  14. package/dist/client/assets/{index-CVV4XiZo.js → index-BXkRE220.js} +2 -2
  15. package/dist/client/assets/keyboard-shortcuts-dialog-BdCeXRjD.js +1 -0
  16. package/dist/client/assets/{main-CkIF0soY.js → main-B3N0eQFg.js} +129 -17
  17. package/dist/client/assets/{opencami-logo-CIxSO1oo.js → opencami-logo-DD0DPFRQ.js} +1 -1
  18. package/dist/client/assets/{react-BhVdgA5r.js → react-B16OrBeM.js} +1 -1
  19. package/dist/client/assets/search-dialog-BjTPceEl.js +1 -0
  20. package/dist/client/assets/session-export-dialog-DtHKG2zW.js +1 -0
  21. package/dist/client/assets/settings-dialog-hiqdk_UD.js +1 -0
  22. package/dist/client/assets/skills-DhwyFq3y.js +2 -0
  23. package/dist/client/assets/skills-panel-BLUjzfjJ.js +5 -0
  24. package/dist/client/assets/styles-CHP4l6vZ.css +1 -0
  25. package/dist/client/assets/switch-J6wLIVu2.js +1 -0
  26. package/dist/client/assets/tabs-DvPgTz5I.js +1 -0
  27. package/dist/client/assets/tooltip-C14vdXHK.js +1 -0
  28. package/dist/client/assets/use-file-explorer-state-BnaJEqRP.js +12 -0
  29. package/dist/client/assets/useButton-Bnnac1eR.js +1 -0
  30. package/dist/client/assets/useCompositeItem-BgiEMKAt.js +1 -0
  31. package/dist/client/assets/{useControlled-Y306krcC.js → useControlled-BhUuiHAm.js} +1 -1
  32. package/dist/client/assets/{useMutation-0WgW4xQJ.js → useMutation-CFmVaBag.js} +1 -1
  33. package/dist/client/assets/visuallyHidden-DCCICp6T.js +9 -0
  34. package/dist/server/assets/{_sessionKey-BhFH4uWY.js → _sessionKey-tRze5NLR.js} +178 -46
  35. package/dist/server/assets/_tanstack-start-manifest_v-CyfoMvUa.js +4 -0
  36. package/dist/server/assets/{agents-Dz_i76VW.js → agents-CmQ4vvXm.js} +1 -1
  37. package/dist/server/assets/{agents-screen-CqQPJndp.js → agents-screen-bmrIyFbk.js} +43 -35
  38. package/dist/server/assets/{bots-6ryCIgKh.js → bots-Byt6jv0a.js} +1 -1
  39. package/dist/server/assets/bots-screen-C2TGFv42.js +474 -0
  40. package/dist/server/assets/{button-DtQ3rV1m.js → button-CwY2OHFj.js} +2 -2
  41. package/dist/server/assets/{connect-B8jpGQGK.js → connect-d3AqjAqe.js} +2 -2
  42. package/dist/server/assets/{file-explorer-screen-DCfS_Ajx.js → file-explorer-screen-CVlFiAFu.js} +36 -36
  43. package/dist/server/assets/{files-D2GIrPF4.js → files-BIEcSPGp.js} +1 -1
  44. package/dist/server/assets/{index-BNSsDaLb.js → index-CNIATlJ9.js} +22 -3
  45. package/dist/server/assets/{index-B2JHn34C.js → index-CRfLKh30.js} +2 -1
  46. package/dist/server/assets/{keyboard-shortcuts-dialog-CqIm8aYF.js → keyboard-shortcuts-dialog-CsNP85q8.js} +2 -2
  47. package/dist/server/assets/{router-Dme7USeO.js → router-rn7pJO_D.js} +356 -64
  48. package/dist/server/assets/{search-dialog-DG0D9KRN.js → search-dialog-Bz4Cu0KW.js} +23 -6
  49. package/dist/server/assets/{session-export-dialog-DLPZVlQV.js → session-export-dialog-CwclV0Aj.js} +2 -2
  50. package/dist/server/assets/{settings-dialog-BaGT4e5l.js → settings-dialog-BBM7jCjE.js} +386 -95
  51. package/dist/server/assets/skills-Cy8xclXY.js +11 -0
  52. package/dist/server/assets/skills-panel-BnRNb7u9.js +762 -0
  53. package/dist/server/assets/{switch-DnX0MjGS.js → switch-BbkUeVDV.js} +1 -1
  54. package/dist/server/assets/tabs-DDFZob0m.js +67 -0
  55. package/dist/server/assets/{tooltip-gbV6rEVv.js → tooltip-DgsSPocE.js} +1 -1
  56. package/dist/server/assets/{use-file-explorer-state-DfAKF2gZ.js → use-file-explorer-state-Il1LlBAe.js} +1 -1
  57. package/dist/server/server.js +2 -2
  58. package/package.json +6 -2
  59. package/dist/client/assets/_sessionKey-DB95zj1L.js +0 -97
  60. package/dist/client/assets/agents-LFrWe-HX.js +0 -2
  61. package/dist/client/assets/agents-screen-CEBBk1yO.js +0 -1
  62. package/dist/client/assets/bots-CxDwf_WK.js +0 -2
  63. package/dist/client/assets/bots-screen-D6qma1wK.js +0 -1
  64. package/dist/client/assets/button-Il3CHIzX.js +0 -1
  65. package/dist/client/assets/file-explorer-screen-rtV6n-5_.js +0 -1
  66. package/dist/client/assets/files-DMemuq9D.js +0 -2
  67. package/dist/client/assets/keyboard-shortcuts-dialog-DXC0YHoy.js +0 -1
  68. package/dist/client/assets/search-dialog-I1jJplIh.js +0 -1
  69. package/dist/client/assets/session-export-dialog-CH5unryw.js +0 -1
  70. package/dist/client/assets/settings-dialog-B8v-GVJ8.js +0 -1
  71. package/dist/client/assets/styles-BaTVzdPa.css +0 -1
  72. package/dist/client/assets/switch-sQnv1YsK.js +0 -1
  73. package/dist/client/assets/tooltip-j_viC_EE.js +0 -1
  74. package/dist/client/assets/use-file-explorer-state-BUH-u7Jv.js +0 -12
  75. package/dist/client/assets/useButton-9VAzplAB.js +0 -9
  76. package/dist/server/assets/_tanstack-start-manifest_v-BaIrL1VQ.js +0 -4
  77. package/dist/server/assets/bots-screen-DS_ZF9Ec.js +0 -417
@@ -4,10 +4,10 @@ import { useQueryClient, useQuery } from "@tanstack/react-query";
4
4
  import { HugeiconsIcon } from "@hugeicons/react";
5
5
  import { SidebarLeft01Icon, ArrowLeft01Icon, Add01Icon, Loading02Icon, PencilEdit01Icon, Delete01Icon, Cancel01Icon, Alert01Icon } from "@hugeicons/core-free-icons";
6
6
  import { motion, AnimatePresence } from "motion/react";
7
- import { c as cn, b as buttonVariants, B as Button } from "./button-DtQ3rV1m.js";
8
- import { S as Switch } from "./switch-DnX0MjGS.js";
7
+ import { c as cn, b as buttonVariants, B as Button } from "./button-CwY2OHFj.js";
8
+ import { S as Switch } from "./switch-BbkUeVDV.js";
9
9
  import { Link } from "@tanstack/react-router";
10
- import { d as chatUiQueryKey, g as getChatUiState, s as setChatUiState, T as TooltipProvider, a as TooltipRoot, b as TooltipTrigger, c as TooltipContent } from "./tooltip-gbV6rEVv.js";
10
+ import { d as chatUiQueryKey, g as getChatUiState, s as setChatUiState, T as TooltipProvider, a as TooltipRoot, b as TooltipTrigger, c as TooltipContent } from "./tooltip-DgsSPocE.js";
11
11
  import { O as OpenCamiLogo, a as OpenCamiText } from "./opencami-logo-C-43FL3R.js";
12
12
  import "@base-ui/react/merge-props";
13
13
  import "@base-ui/react/use-render";
@@ -38,45 +38,49 @@ function AgentCard({
38
38
  const isMain = agent.isDefault || agent.id === "main";
39
39
  const displayName = agent.name || agent.id.charAt(0).toUpperCase() + agent.id.slice(1);
40
40
  const shortModel = agent.model ? agent.model.split("/").pop() : void 0;
41
- return /* @__PURE__ */ jsx("div", { className: "rounded-xl border border-primary-200 bg-surface p-4 hover:border-primary-300 transition-colors", children: /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-4", children: [
42
- /* @__PURE__ */ jsxs("div", { className: "relative flex size-12 shrink-0 items-center justify-center rounded-xl bg-primary-100 text-xl", children: [
41
+ return /* @__PURE__ */ jsx("div", { className: "group rounded-lg border border-primary-100 bg-surface p-4 transition-all duration-150 ease-out hover:border-primary-200 hover:shadow-sm", children: /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-4", children: [
42
+ /* @__PURE__ */ jsxs("div", { className: "relative flex size-12 shrink-0 items-center justify-center rounded-lg bg-primary-50 text-xl", children: [
43
43
  agent.emoji || "🤖",
44
- isMain && /* @__PURE__ */ jsx("span", { className: "absolute -top-1 -right-1 size-3 rounded-full bg-green-500 ring-2 ring-surface" })
44
+ isMain && /* @__PURE__ */ jsx("span", { className: "absolute -top-1 -right-1 size-3 rounded-full bg-emerald-500 ring-2 ring-surface" })
45
45
  ] }),
46
46
  /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
47
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mb-1", children: [
48
- /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-primary-900 truncate", children: displayName }),
49
- displayName !== agent.id && /* @__PURE__ */ jsx("span", { className: "text-xs text-primary-400 font-mono", children: agent.id }),
50
- isMain && /* @__PURE__ */ jsx("span", { className: "rounded bg-green-500/15 px-1.5 py-0.5 text-[10px] font-medium text-green-600 dark:text-green-400", children: "PRIMARY" })
47
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3 mb-2", children: [
48
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 min-w-0", children: [
49
+ /* @__PURE__ */ jsx("h4", { className: "text-[13px] font-semibold text-primary-900 leading-tight truncate", children: displayName }),
50
+ displayName !== agent.id && /* @__PURE__ */ jsx("span", { className: "text-[10px] font-mono text-primary-400 shrink-0", children: agent.id })
51
+ ] }),
52
+ isMain && /* @__PURE__ */ jsx("span", { className: "inline-flex items-center px-2 py-0.5 text-[11px] font-medium rounded-full bg-emerald-50 text-emerald-600 border border-emerald-100 shrink-0", children: "Primary" })
51
53
  ] }),
52
- /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2 text-xs text-primary-500 mb-2", children: [
53
- shortModel && /* @__PURE__ */ jsx("span", { className: "inline-flex items-center rounded-md bg-primary-100 px-2 py-0.5 font-mono text-[11px] text-primary-600 truncate max-w-[250px]", children: shortModel }),
54
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-1.5 mb-3", children: [
55
+ shortModel && /* @__PURE__ */ jsx("span", { className: "inline-flex items-center px-2 py-0.5 text-[10px] font-mono rounded-full bg-primary-50 text-primary-600 border border-primary-100 truncate max-w-[200px]", children: shortModel }),
54
56
  agent.sandbox !== void 0 && /* @__PURE__ */ jsx("span", { className: cn(
55
- "inline-flex items-center rounded-md px-2 py-0.5 text-[11px] font-medium",
56
- agent.sandbox ? "bg-amber-500/15 text-amber-600 dark:text-amber-400" : "bg-primary-100 text-primary-500"
57
- ), children: agent.sandbox ? "🔒 Sandbox" : "🔓 No sandbox" }),
58
- agent.sessionScope && /* @__PURE__ */ jsx("span", { className: "inline-flex items-center rounded-md bg-primary-100 px-2 py-0.5 text-[11px] text-primary-500", children: agent.sessionScope === "per-sender" ? "👤 Per-sender" : "🌐 Global" })
57
+ "inline-flex items-center px-2 py-0.5 text-[10px] font-medium rounded-full border",
58
+ agent.sandbox ? "bg-amber-50 text-amber-600 border-amber-100" : "bg-primary-50 text-primary-500 border-primary-100"
59
+ ), children: agent.sandbox ? "🔒 Sandbox" : "🔓 Open" }),
60
+ agent.sessionScope && /* @__PURE__ */ jsx("span", { className: "inline-flex items-center px-2 py-0.5 text-[10px] font-medium rounded-full bg-primary-50 text-primary-500 border border-primary-100", children: agent.sessionScope === "per-sender" ? "👤 Per-sender" : "🌐 Global" })
59
61
  ] }),
60
62
  agent.tools && agent.tools.length > 0 && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-1 mb-2", children: [
61
- agent.tools.slice(0, 8).map((tool) => /* @__PURE__ */ jsx("span", { className: "inline-flex items-center rounded bg-blue-500/10 px-1.5 py-0.5 text-[10px] font-mono text-blue-600 dark:text-blue-400", children: tool }, tool)),
62
- agent.tools.length > 8 && /* @__PURE__ */ jsxs("span", { className: "text-[10px] text-primary-400", children: [
63
+ agent.tools.slice(0, 6).map((tool) => /* @__PURE__ */ jsx("span", { className: "inline-flex items-center px-1.5 py-0.5 text-[10px] font-mono rounded bg-sky-50 text-sky-600 border border-sky-100", children: tool }, tool)),
64
+ agent.tools.length > 6 && /* @__PURE__ */ jsxs("span", { className: "text-[10px] text-primary-400", children: [
63
65
  "+",
64
- agent.tools.length - 8,
66
+ agent.tools.length - 6,
65
67
  " more"
66
68
  ] })
67
69
  ] }),
68
- agent.channelBindings && agent.channelBindings.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1 mb-2", children: agent.channelBindings.map((ch) => /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center rounded bg-purple-500/10 px-1.5 py-0.5 text-[10px] text-purple-600 dark:text-purple-400", children: [
70
+ agent.channelBindings && agent.channelBindings.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1 mb-2", children: agent.channelBindings.map((ch) => /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center px-1.5 py-0.5 text-[10px] rounded bg-violet-50 text-violet-600 border border-violet-100", children: [
69
71
  "#",
70
72
  ch
71
73
  ] }, ch)) }),
72
- agent.workspace && /* @__PURE__ */ jsx("div", { className: "text-[11px] font-mono text-primary-400 truncate", title: agent.workspace, children: agent.workspace }),
73
- agent.activeSessions !== void 0 && agent.activeSessions > 0 && /* @__PURE__ */ jsxs("div", { className: "text-[11px] text-primary-400 mt-1", children: [
74
- agent.activeSessions,
75
- " active session",
76
- agent.activeSessions !== 1 ? "s" : ""
74
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 pt-2 border-t border-primary-50", children: [
75
+ agent.workspace && /* @__PURE__ */ jsx("span", { className: "text-[11px] font-mono text-primary-400 truncate", title: agent.workspace, children: agent.workspace }),
76
+ agent.activeSessions !== void 0 && agent.activeSessions > 0 && /* @__PURE__ */ jsxs("span", { className: "text-[11px] text-primary-400", children: [
77
+ agent.activeSessions,
78
+ " active session",
79
+ agent.activeSessions !== 1 ? "s" : ""
80
+ ] })
77
81
  ] })
78
82
  ] }),
79
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 shrink-0", children: [
83
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 shrink-0 opacity-0 group-hover:opacity-100 transition-opacity duration-150", children: [
80
84
  /* @__PURE__ */ jsx(Button, { size: "icon-sm", variant: "ghost", onClick: onEdit, "aria-label": "Edit agent", children: /* @__PURE__ */ jsx(HugeiconsIcon, { icon: PencilEdit01Icon, size: 16, strokeWidth: 1.5 }) }),
81
85
  !isMain && /* @__PURE__ */ jsx(Button, { size: "icon-sm", variant: "ghost", onClick: onDelete, "aria-label": "Delete agent", className: "text-red-500 hover:text-red-600", children: /* @__PURE__ */ jsx(HugeiconsIcon, { icon: Delete01Icon, size: 16, strokeWidth: 1.5 }) })
82
86
  ] })
@@ -144,8 +148,8 @@ function AgentForm({
144
148
  /* @__PURE__ */ jsx(Button, { size: "icon-sm", variant: "ghost", onClick: onCancel, children: /* @__PURE__ */ jsx(HugeiconsIcon, { icon: Cancel01Icon, size: 18, strokeWidth: 1.5 }) })
145
149
  ] }),
146
150
  /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
147
- /* @__PURE__ */ jsxs("div", { className: "rounded-xl border border-primary-200 bg-surface p-4", children: [
148
- /* @__PURE__ */ jsx("h3", { className: "text-xs font-medium text-primary-500 uppercase tracking-wider mb-3", children: "Basic Info" }),
151
+ /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-primary-100 bg-surface p-4", children: [
152
+ /* @__PURE__ */ jsx("h3", { className: "text-xs font-medium text-primary-400 uppercase tracking-wider mb-3", children: "Basic Info" }),
149
153
  /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-3", children: [
150
154
  /* @__PURE__ */ jsxs("div", { className: "col-span-2 sm:col-span-1", children: [
151
155
  /* @__PURE__ */ jsx("label", { className: "block text-xs text-primary-600 mb-1", children: "Name" }),
@@ -185,8 +189,8 @@ function AgentForm({
185
189
  ] })
186
190
  ] })
187
191
  ] }),
188
- /* @__PURE__ */ jsxs("div", { className: "rounded-xl border border-primary-200 bg-surface p-4", children: [
189
- /* @__PURE__ */ jsx("h3", { className: "text-xs font-medium text-primary-500 uppercase tracking-wider mb-3", children: "Model & Config" }),
192
+ /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-primary-100 bg-surface p-4", children: [
193
+ /* @__PURE__ */ jsx("h3", { className: "text-xs font-medium text-primary-400 uppercase tracking-wider mb-3", children: "Model & Config" }),
190
194
  /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
191
195
  /* @__PURE__ */ jsxs("div", { children: [
192
196
  /* @__PURE__ */ jsx("label", { className: "block text-xs text-primary-600 mb-1", children: "Model" }),
@@ -236,8 +240,8 @@ function AgentForm({
236
240
  ] })
237
241
  ] })
238
242
  ] }),
239
- /* @__PURE__ */ jsxs("div", { className: "rounded-xl border border-primary-200 bg-surface p-4", children: [
240
- /* @__PURE__ */ jsx("h3", { className: "text-xs font-medium text-primary-500 uppercase tracking-wider mb-3", children: "Tools" }),
243
+ /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-primary-100 bg-surface p-4", children: [
244
+ /* @__PURE__ */ jsx("h3", { className: "text-xs font-medium text-primary-400 uppercase tracking-wider mb-3", children: "Tools" }),
241
245
  /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
242
246
  /* @__PURE__ */ jsx(
243
247
  TagInput,
@@ -259,8 +263,8 @@ function AgentForm({
259
263
  )
260
264
  ] })
261
265
  ] }),
262
- /* @__PURE__ */ jsxs("div", { className: "rounded-xl border border-primary-200 bg-surface p-4", children: [
263
- /* @__PURE__ */ jsx("h3", { className: "text-xs font-medium text-primary-500 uppercase tracking-wider mb-3", children: "Channel Bindings" }),
266
+ /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-primary-100 bg-surface p-4", children: [
267
+ /* @__PURE__ */ jsx("h3", { className: "text-xs font-medium text-primary-400 uppercase tracking-wider mb-3", children: "Channel Bindings" }),
264
268
  /* @__PURE__ */ jsx(
265
269
  TagInput,
266
270
  {
@@ -549,7 +553,11 @@ function AgentsScreen() {
549
553
  },
550
554
  agent.id
551
555
  )),
552
- agents.length === 0 && /* @__PURE__ */ jsx("div", { className: "text-center py-16 text-sm text-primary-400", children: "No agents found. Create one to get started." })
556
+ agents.length === 0 && /* @__PURE__ */ jsxs("div", { className: "py-12 text-center", children: [
557
+ /* @__PURE__ */ jsx("div", { className: "inline-flex items-center justify-center w-12 h-12 rounded-lg bg-primary-50 mb-3", children: /* @__PURE__ */ jsx("span", { className: "text-2xl", children: "🤖" }) }),
558
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-primary-500", children: "No agents found" }),
559
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-primary-400 mt-1", children: "Create one to get started" })
560
+ ] })
553
561
  ] }) : view === "create" ? /* @__PURE__ */ jsx(
554
562
  AgentForm,
555
563
  {
@@ -1,6 +1,6 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
2
  import { Suspense, lazy } from "react";
3
- const BotsScreen = lazy(() => import("./bots-screen-DS_ZF9Ec.js").then((m) => ({
3
+ const BotsScreen = lazy(() => import("./bots-screen-C2TGFv42.js").then((m) => ({
4
4
  default: m.BotsScreen
5
5
  })));
6
6
  function BotsRoute() {
@@ -0,0 +1,474 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { useState, useCallback, useMemo } from "react";
3
+ import { HugeiconsIcon } from "@hugeicons/react";
4
+ import { Loading02Icon, Clock01Icon, Calendar01Icon, PlayIcon, SmartPhone01Icon, ArrowLeft01Icon } from "@hugeicons/core-free-icons";
5
+ import { Link } from "@tanstack/react-router";
6
+ import { motion, AnimatePresence } from "motion/react";
7
+ import { c as cn, B as Button } from "./button-CwY2OHFj.js";
8
+ import { S as Switch } from "./switch-BbkUeVDV.js";
9
+ import { useQuery, useQueryClient, useMutation } from "@tanstack/react-query";
10
+ import "@base-ui/react/merge-props";
11
+ import "@base-ui/react/use-render";
12
+ import "class-variance-authority";
13
+ import "clsx";
14
+ import "tailwind-merge";
15
+ import "@base-ui/react/switch";
16
+ const CRON_JOBS_KEY = ["cron-jobs"];
17
+ function useCronJobs() {
18
+ return useQuery({
19
+ queryKey: CRON_JOBS_KEY,
20
+ queryFn: async ({ signal }) => {
21
+ const controller = new AbortController();
22
+ const onAbort = () => controller.abort();
23
+ signal.addEventListener("abort", onAbort);
24
+ try {
25
+ const res = await fetch("/api/cron", { signal: controller.signal });
26
+ if (!res.ok) throw new Error("Failed to fetch cron jobs");
27
+ const data = await res.json();
28
+ return Array.isArray(data.jobs) ? data.jobs : [];
29
+ } finally {
30
+ signal.removeEventListener("abort", onAbort);
31
+ }
32
+ },
33
+ refetchInterval: 3e4
34
+ });
35
+ }
36
+ function useCronJobRuns(jobId) {
37
+ return useQuery({
38
+ queryKey: ["cron-job-runs", jobId],
39
+ queryFn: async ({ signal }) => {
40
+ if (!jobId) return [];
41
+ const controller = new AbortController();
42
+ const onAbort = () => controller.abort();
43
+ signal.addEventListener("abort", onAbort);
44
+ try {
45
+ const res = await fetch(`/api/cron?jobId=${encodeURIComponent(jobId)}`, {
46
+ signal: controller.signal
47
+ });
48
+ if (!res.ok) throw new Error("Failed to fetch cron job runs");
49
+ const data = await res.json();
50
+ return Array.isArray(data.runs) ? data.runs : [];
51
+ } finally {
52
+ signal.removeEventListener("abort", onAbort);
53
+ }
54
+ },
55
+ enabled: !!jobId
56
+ });
57
+ }
58
+ function useRunCronJob() {
59
+ const queryClient = useQueryClient();
60
+ return useMutation({
61
+ mutationFn: async (jobId) => {
62
+ const controller = new AbortController();
63
+ const res = await fetch("/api/cron", {
64
+ method: "POST",
65
+ headers: { "Content-Type": "application/json" },
66
+ body: JSON.stringify({ jobId }),
67
+ signal: controller.signal
68
+ });
69
+ if (!res.ok) throw new Error("Failed to run cron job");
70
+ return res.json();
71
+ },
72
+ onSuccess: () => {
73
+ void queryClient.invalidateQueries({ queryKey: CRON_JOBS_KEY });
74
+ }
75
+ });
76
+ }
77
+ function useToggleCronJob() {
78
+ const queryClient = useQueryClient();
79
+ return useMutation({
80
+ mutationFn: async ({ jobId, enabled }) => {
81
+ const controller = new AbortController();
82
+ const res = await fetch("/api/cron", {
83
+ method: "PATCH",
84
+ headers: { "Content-Type": "application/json" },
85
+ body: JSON.stringify({ jobId, patch: { enabled } }),
86
+ signal: controller.signal
87
+ });
88
+ if (!res.ok) throw new Error("Failed to update cron job");
89
+ return res.json();
90
+ },
91
+ onSuccess: () => {
92
+ void queryClient.invalidateQueries({ queryKey: CRON_JOBS_KEY });
93
+ }
94
+ });
95
+ }
96
+ function formatDuration(ms) {
97
+ if (!ms || ms <= 0) return "—";
98
+ if (ms < 1e3) return `${ms}ms`;
99
+ if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
100
+ return `${(ms / 6e4).toFixed(1)}m`;
101
+ }
102
+ function formatTime(ts) {
103
+ if (!ts) return "—";
104
+ return new Date(ts).toLocaleString();
105
+ }
106
+ function CronJobDetail({ job }) {
107
+ const runsQuery = useCronJobRuns(job.id);
108
+ const runs = runsQuery.data ?? [];
109
+ return /* @__PURE__ */ jsx(
110
+ motion.div,
111
+ {
112
+ initial: { height: 0, opacity: 0 },
113
+ animate: { height: "auto", opacity: 1 },
114
+ exit: { height: 0, opacity: 0 },
115
+ transition: { duration: 0.15 },
116
+ className: "overflow-hidden",
117
+ children: /* @__PURE__ */ jsxs("div", { className: "space-y-4 border-t border-primary-100 bg-primary-50/50 px-4 py-4", children: [
118
+ (job.payload.message || job.payload.prompt) && /* @__PURE__ */ jsxs("div", { children: [
119
+ /* @__PURE__ */ jsx("h5", { className: "text-xs font-medium text-primary-400 uppercase tracking-wider mb-2", children: "Prompt / Message" }),
120
+ /* @__PURE__ */ jsx("p", { className: "whitespace-pre-wrap break-words rounded-lg bg-surface border border-primary-100 p-3 text-sm text-primary-700 leading-relaxed", children: job.payload.message ?? job.payload.prompt })
121
+ ] }),
122
+ job.delivery && /* @__PURE__ */ jsxs("div", { children: [
123
+ /* @__PURE__ */ jsx("h5", { className: "text-xs font-medium text-primary-400 uppercase tracking-wider mb-2", children: "Delivery" }),
124
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2", children: [
125
+ job.delivery.mode && /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center px-2 py-0.5 text-[11px] font-medium rounded-full bg-surface text-primary-600 border border-primary-100", children: [
126
+ "Mode: ",
127
+ job.delivery.mode
128
+ ] }),
129
+ job.delivery.channel && /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center px-2 py-0.5 text-[11px] font-medium rounded-full bg-surface text-primary-600 border border-primary-100", children: [
130
+ "Channel: ",
131
+ job.delivery.channel
132
+ ] }),
133
+ job.delivery.to && /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center px-2 py-0.5 text-[11px] font-medium rounded-full bg-surface text-primary-600 border border-primary-100", children: [
134
+ "To: ",
135
+ job.delivery.to
136
+ ] })
137
+ ] })
138
+ ] }),
139
+ job.payload.model && /* @__PURE__ */ jsxs("div", { children: [
140
+ /* @__PURE__ */ jsx("h5", { className: "text-xs font-medium text-primary-400 uppercase tracking-wider mb-2", children: "Model" }),
141
+ /* @__PURE__ */ jsx("span", { className: "inline-flex items-center px-2 py-0.5 text-[10px] font-mono rounded-full bg-surface text-primary-600 border border-primary-100", children: job.payload.model })
142
+ ] }),
143
+ job.state?.lastError && /* @__PURE__ */ jsxs("div", { children: [
144
+ /* @__PURE__ */ jsx("h5", { className: "text-xs font-medium text-red-500 uppercase tracking-wider mb-2", children: "Last Error" }),
145
+ /* @__PURE__ */ jsx("p", { className: "rounded-lg bg-red-50 border border-red-100 p-3 font-mono text-xs text-red-600 leading-relaxed", children: job.state.lastError })
146
+ ] }),
147
+ /* @__PURE__ */ jsxs("div", { children: [
148
+ /* @__PURE__ */ jsx("h5", { className: "text-xs font-medium text-primary-400 uppercase tracking-wider mb-2", children: "Recent Runs" }),
149
+ runsQuery.isLoading ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-xs text-primary-500", children: [
150
+ /* @__PURE__ */ jsx(HugeiconsIcon, { icon: Loading02Icon, size: 12, className: "animate-spin" }),
151
+ "Loading..."
152
+ ] }) : runs.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-xs text-primary-400", children: "No run history available" }) : /* @__PURE__ */ jsx("div", { className: "space-y-1.5", children: runs.slice(0, 5).map((run) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 py-1.5 px-2 rounded-md bg-surface border border-primary-50", children: [
153
+ /* @__PURE__ */ jsx(
154
+ "span",
155
+ {
156
+ className: cn(
157
+ "inline-block h-2 w-2 shrink-0 rounded-full",
158
+ run.status === "ok" ? "bg-emerald-500" : "bg-red-500"
159
+ )
160
+ }
161
+ ),
162
+ /* @__PURE__ */ jsx(HugeiconsIcon, { icon: Clock01Icon, size: 11, strokeWidth: 1.5, className: "text-primary-400" }),
163
+ /* @__PURE__ */ jsx("span", { className: "tabular-nums text-[11px] text-primary-600", children: formatTime(run.ranAt) }),
164
+ /* @__PURE__ */ jsx("span", { className: "tabular-nums text-[11px] text-primary-400", children: formatDuration(run.durationMs) }),
165
+ run.error && /* @__PURE__ */ jsx("span", { className: "truncate text-[11px] text-red-500 ml-auto", children: run.error })
166
+ ] }, run.id)) })
167
+ ] })
168
+ ] })
169
+ }
170
+ );
171
+ }
172
+ function parseCronExpr(expr) {
173
+ const parts = expr.trim().split(/\s+/);
174
+ if (parts.length < 5) return expr;
175
+ const [min, hour, dom, mon, dow] = parts;
176
+ const timeStr = `${hour.padStart(2, "0")}:${min.padStart(2, "0")}`;
177
+ if (dom === "*" && mon === "*" && dow === "*") return `Daily at ${timeStr}`;
178
+ if (dom === "*" && mon === "*" && dow !== "*") {
179
+ const days = {
180
+ "0": "Sun",
181
+ "1": "Mon",
182
+ "2": "Tue",
183
+ "3": "Wed",
184
+ "4": "Thu",
185
+ "5": "Fri",
186
+ "6": "Sat",
187
+ "7": "Sun"
188
+ };
189
+ const dayNames = dow.split(",").map((d) => days[d] ?? d).join(", ");
190
+ return `${dayNames} at ${timeStr}`;
191
+ }
192
+ return expr;
193
+ }
194
+ function humanSchedule(job) {
195
+ const schedule = job.schedule;
196
+ if (schedule.kind === "every" && schedule.expr) return `Every ${schedule.expr}`;
197
+ if (schedule.kind === "at" && schedule.expr) return `Once at ${schedule.expr}`;
198
+ if (schedule.kind === "cron" && schedule.expr) return parseCronExpr(schedule.expr);
199
+ return schedule.expr ?? "Unknown";
200
+ }
201
+ function formatRelativeMs$1(ms) {
202
+ if (!ms) return "—";
203
+ const diff = Date.now() - ms;
204
+ if (diff < 6e4) return "just now";
205
+ if (diff < 36e5) return `${Math.floor(diff / 6e4)}m ago`;
206
+ if (diff < 864e5) return `${Math.floor(diff / 36e5)}h ago`;
207
+ return new Date(ms).toLocaleDateString();
208
+ }
209
+ function formatFutureMs(ms) {
210
+ if (!ms) return "—";
211
+ const diff = ms - Date.now();
212
+ if (diff < 0) return "overdue";
213
+ if (diff < 6e4) return "in <1m";
214
+ if (diff < 36e5) return `in ${Math.floor(diff / 6e4)}m`;
215
+ if (diff < 864e5) return `in ${Math.floor(diff / 36e5)}h`;
216
+ return new Date(ms).toLocaleDateString();
217
+ }
218
+ function StatusBadge({ status }) {
219
+ if (status === "ok") {
220
+ return /* @__PURE__ */ jsx("span", { className: "inline-flex items-center px-2 py-0.5 text-[11px] font-medium rounded-full bg-emerald-50 text-emerald-600 border border-emerald-100", children: "ok" });
221
+ }
222
+ if (status === "error") {
223
+ return /* @__PURE__ */ jsx("span", { className: "inline-flex items-center px-2 py-0.5 text-[11px] font-medium rounded-full bg-red-50 text-red-600 border border-red-100", children: "error" });
224
+ }
225
+ return /* @__PURE__ */ jsx("span", { className: "inline-flex items-center px-2 py-0.5 text-[11px] font-medium rounded-full bg-primary-50 text-primary-400 border border-primary-100", children: "—" });
226
+ }
227
+ function CronJobTable({ jobs }) {
228
+ const [expandedId, setExpandedId] = useState(null);
229
+ const runMutation = useRunCronJob();
230
+ const toggleMutation = useToggleCronJob();
231
+ const handleToggle = useCallback(
232
+ (job) => {
233
+ toggleMutation.mutate({ jobId: job.id, enabled: !job.enabled });
234
+ },
235
+ [toggleMutation]
236
+ );
237
+ const handleRun = useCallback(
238
+ (jobId) => {
239
+ runMutation.mutate(jobId);
240
+ },
241
+ [runMutation]
242
+ );
243
+ if (jobs.length === 0) {
244
+ return null;
245
+ }
246
+ return /* @__PURE__ */ jsx("div", { className: "space-y-2", children: jobs.map((job) => {
247
+ const isExpanded = expandedId === job.id;
248
+ const isRunning = runMutation.isPending && runMutation.variables === job.id;
249
+ return /* @__PURE__ */ jsxs(
250
+ "div",
251
+ {
252
+ className: cn(
253
+ "group rounded-lg border border-primary-100 bg-surface transition-all duration-150 ease-out",
254
+ isExpanded ? "border-primary-200 shadow-sm" : "hover:border-primary-200 hover:shadow-sm"
255
+ ),
256
+ children: [
257
+ /* @__PURE__ */ jsxs(
258
+ "button",
259
+ {
260
+ type: "button",
261
+ onClick: () => setExpandedId(isExpanded ? null : job.id),
262
+ className: cn(
263
+ "w-full p-4 text-left",
264
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-300 focus-visible:rounded-lg"
265
+ ),
266
+ children: [
267
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3 mb-2", children: [
268
+ /* @__PURE__ */ jsx("h4", { className: cn(
269
+ "text-[13px] font-semibold leading-tight truncate",
270
+ job.enabled ? "text-primary-900" : "text-primary-400"
271
+ ), children: job.name ?? job.id }),
272
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 shrink-0", children: [
273
+ !job.enabled && /* @__PURE__ */ jsx("span", { className: "inline-flex items-center px-2 py-0.5 text-[11px] font-medium rounded-full bg-primary-50 text-primary-400 border border-primary-100", children: "Disabled" }),
274
+ /* @__PURE__ */ jsx(StatusBadge, { status: job.state?.lastStatus })
275
+ ] })
276
+ ] }),
277
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-primary-500 leading-relaxed mb-3", children: humanSchedule(job) }),
278
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between pt-2 border-t border-primary-50", children: [
279
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4", children: [
280
+ /* @__PURE__ */ jsxs("span", { className: "text-[11px] text-primary-400 flex items-center gap-1", children: [
281
+ /* @__PURE__ */ jsx(HugeiconsIcon, { icon: Clock01Icon, size: 11, strokeWidth: 1.5 }),
282
+ "Last: ",
283
+ formatRelativeMs$1(job.state?.lastRunAtMs)
284
+ ] }),
285
+ /* @__PURE__ */ jsxs("span", { className: "text-[11px] text-primary-400 flex items-center gap-1", children: [
286
+ /* @__PURE__ */ jsx(HugeiconsIcon, { icon: Calendar01Icon, size: 11, strokeWidth: 1.5 }),
287
+ "Next: ",
288
+ formatFutureMs(job.state?.nextRunAtMs)
289
+ ] })
290
+ ] }),
291
+ /* @__PURE__ */ jsxs(
292
+ "div",
293
+ {
294
+ className: "flex items-center gap-2",
295
+ onClick: (event) => event.stopPropagation(),
296
+ children: [
297
+ /* @__PURE__ */ jsx(
298
+ Switch,
299
+ {
300
+ checked: job.enabled,
301
+ onCheckedChange: () => handleToggle(job),
302
+ "aria-label": `${job.enabled ? "Disable" : "Enable"} ${job.name ?? job.id}`,
303
+ disabled: toggleMutation.isPending
304
+ }
305
+ ),
306
+ /* @__PURE__ */ jsx(
307
+ Button,
308
+ {
309
+ variant: "ghost",
310
+ size: "icon-sm",
311
+ onClick: () => handleRun(job.id),
312
+ disabled: isRunning,
313
+ "aria-label": `Run ${job.name ?? job.id} now`,
314
+ className: "text-primary-500 hover:text-primary-700",
315
+ children: /* @__PURE__ */ jsx(
316
+ HugeiconsIcon,
317
+ {
318
+ icon: isRunning ? Loading02Icon : PlayIcon,
319
+ size: 16,
320
+ strokeWidth: 1.5,
321
+ className: cn(isRunning && "animate-spin")
322
+ }
323
+ )
324
+ }
325
+ )
326
+ ]
327
+ }
328
+ )
329
+ ] })
330
+ ]
331
+ }
332
+ ),
333
+ /* @__PURE__ */ jsx(AnimatePresence, { children: isExpanded && /* @__PURE__ */ jsx(CronJobDetail, { job }) })
334
+ ]
335
+ },
336
+ job.id
337
+ );
338
+ }) });
339
+ }
340
+ function formatRelativeMs(ms) {
341
+ if (!ms) return "never";
342
+ const diff = Date.now() - ms;
343
+ if (diff < 6e4) return "just now";
344
+ if (diff < 36e5) return `${Math.floor(diff / 6e4)}m ago`;
345
+ if (diff < 864e5) return `${Math.floor(diff / 36e5)}h ago`;
346
+ return new Date(ms).toLocaleDateString();
347
+ }
348
+ function extractBotName(jobName) {
349
+ for (const separator of [" - ", " | ", ": ", " / "]) {
350
+ const index = jobName.indexOf(separator);
351
+ if (index > 0) return jobName.substring(0, index).trim();
352
+ }
353
+ return jobName;
354
+ }
355
+ function groupJobsIntoBots(jobs) {
356
+ const grouped = /* @__PURE__ */ new Map();
357
+ for (const job of jobs) {
358
+ const name = extractBotName(job.name ?? job.id);
359
+ const existing = grouped.get(name);
360
+ if (existing) {
361
+ existing.push(job);
362
+ } else {
363
+ grouped.set(name, [job]);
364
+ }
365
+ }
366
+ return Array.from(grouped.entries()).map(([name, groupedJobs]) => ({ name, jobs: groupedJobs })).sort((a, b) => a.name.localeCompare(b.name));
367
+ }
368
+ function BotCard({ bot }) {
369
+ const lastActivity = bot.jobs.reduce((max, job) => Math.max(max, job.state?.lastRunAtMs ?? 0), 0);
370
+ const hasErrors = bot.jobs.some((job) => job.state?.lastStatus === "error");
371
+ const enabledCount = bot.jobs.filter((job) => job.enabled).length;
372
+ return /* @__PURE__ */ jsxs("div", { className: "group rounded-lg border border-primary-100 bg-surface p-4 transition-all duration-150 ease-out hover:border-primary-200 hover:shadow-sm", children: [
373
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3 mb-2", children: [
374
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
375
+ /* @__PURE__ */ jsx("div", { className: cn(
376
+ "flex items-center justify-center w-10 h-10 rounded-lg",
377
+ hasErrors ? "bg-red-50" : "bg-primary-50"
378
+ ), children: /* @__PURE__ */ jsx(
379
+ HugeiconsIcon,
380
+ {
381
+ icon: SmartPhone01Icon,
382
+ size: 20,
383
+ strokeWidth: 1.5,
384
+ className: hasErrors ? "text-red-500" : "text-primary-500"
385
+ }
386
+ ) }),
387
+ /* @__PURE__ */ jsx("h4", { className: "text-[13px] font-semibold text-primary-900 leading-tight truncate", children: bot.name })
388
+ ] }),
389
+ hasErrors && /* @__PURE__ */ jsx("span", { className: "inline-flex items-center px-2 py-0.5 text-[11px] font-medium rounded-full bg-red-50 text-red-600 border border-red-100 shrink-0", children: "Error" })
390
+ ] }),
391
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between pt-2 border-t border-primary-50", children: [
392
+ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-3", children: /* @__PURE__ */ jsxs("span", { className: "text-[11px] text-primary-400 flex items-center gap-1", children: [
393
+ /* @__PURE__ */ jsx(HugeiconsIcon, { icon: Clock01Icon, size: 11, strokeWidth: 1.5 }),
394
+ formatRelativeMs(lastActivity || void 0)
395
+ ] }) }),
396
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
397
+ /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center px-2 py-0.5 text-[10px] font-medium rounded-full bg-primary-50 text-primary-500 border border-primary-100", children: [
398
+ bot.jobs.length,
399
+ " job",
400
+ bot.jobs.length !== 1 ? "s" : ""
401
+ ] }),
402
+ enabledCount < bot.jobs.length && /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center px-2 py-0.5 text-[10px] font-medium rounded-full bg-amber-50 text-amber-600 border border-amber-100", children: [
403
+ bot.jobs.length - enabledCount,
404
+ " disabled"
405
+ ] })
406
+ ] })
407
+ ] })
408
+ ] });
409
+ }
410
+ function BotsScreen() {
411
+ const cronJobsQuery = useCronJobs();
412
+ const jobs = cronJobsQuery.data ?? [];
413
+ const bots = useMemo(() => groupJobsIntoBots(jobs), [jobs]);
414
+ return /* @__PURE__ */ jsxs("div", { className: "flex h-screen flex-col bg-surface text-primary-900", children: [
415
+ /* @__PURE__ */ jsxs("div", { className: "px-4 pt-4 pb-3 border-b border-primary-100", children: [
416
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 mb-2", children: [
417
+ /* @__PURE__ */ jsx(
418
+ Link,
419
+ {
420
+ to: "/chat/$sessionKey",
421
+ params: { sessionKey: "main" },
422
+ className: "p-1.5 -ml-1.5 rounded-md text-primary-500 hover:text-primary-700 hover:bg-primary-50 transition-colors duration-150",
423
+ "aria-label": "Back to Chat",
424
+ children: /* @__PURE__ */ jsx(HugeiconsIcon, { icon: ArrowLeft01Icon, size: 18, strokeWidth: 2 })
425
+ }
426
+ ),
427
+ /* @__PURE__ */ jsx("h2", { className: "text-base font-semibold text-primary-900", children: "Cron Jobs" })
428
+ ] }),
429
+ /* @__PURE__ */ jsxs("p", { className: "text-xs text-primary-500", children: [
430
+ jobs.length,
431
+ " job",
432
+ jobs.length !== 1 ? "s" : "",
433
+ " configured"
434
+ ] })
435
+ ] }),
436
+ /* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 overflow-auto px-4 pb-4 pt-4", children: cronJobsQuery.isLoading ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center py-12", children: /* @__PURE__ */ jsx(HugeiconsIcon, { icon: Loading02Icon, size: 18, className: "animate-spin text-primary-300" }) }) : cronJobsQuery.isError ? /* @__PURE__ */ jsxs("div", { className: "py-12 text-center", children: [
437
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-red-500", children: cronJobsQuery.error instanceof Error ? cronJobsQuery.error.message : "Failed to load cron jobs" }),
438
+ /* @__PURE__ */ jsx(
439
+ "button",
440
+ {
441
+ onClick: () => void cronJobsQuery.refetch(),
442
+ className: "mt-2 text-xs font-medium text-primary-500 hover:text-primary-700 transition-colors duration-150",
443
+ children: "Retry"
444
+ }
445
+ )
446
+ ] }) : jobs.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "py-12 text-center", children: [
447
+ /* @__PURE__ */ jsx("div", { className: "inline-flex items-center justify-center w-12 h-12 rounded-lg bg-primary-50 mb-3", children: /* @__PURE__ */ jsx(HugeiconsIcon, { icon: Clock01Icon, size: 24, className: "text-primary-400" }) }),
448
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-primary-500", children: "No cron jobs configured" }),
449
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-primary-400 mt-1", children: "Jobs will appear here once created" })
450
+ ] }) : /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
451
+ bots.length > 0 && /* @__PURE__ */ jsxs("section", { children: [
452
+ /* @__PURE__ */ jsxs("h3", { className: "text-xs font-medium text-primary-400 uppercase tracking-wider mb-3 flex items-center gap-2", children: [
453
+ /* @__PURE__ */ jsx(HugeiconsIcon, { icon: SmartPhone01Icon, size: 14, strokeWidth: 1.5 }),
454
+ "Bots (",
455
+ bots.length,
456
+ ")"
457
+ ] }),
458
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3", children: bots.map((bot) => /* @__PURE__ */ jsx(BotCard, { bot }, bot.name)) })
459
+ ] }),
460
+ /* @__PURE__ */ jsxs("section", { children: [
461
+ /* @__PURE__ */ jsxs("h3", { className: "text-xs font-medium text-primary-400 uppercase tracking-wider mb-3 flex items-center gap-2", children: [
462
+ /* @__PURE__ */ jsx(HugeiconsIcon, { icon: Clock01Icon, size: 14, strokeWidth: 1.5 }),
463
+ "All Jobs (",
464
+ jobs.length,
465
+ ")"
466
+ ] }),
467
+ /* @__PURE__ */ jsx("div", { className: "space-y-2", children: /* @__PURE__ */ jsx(CronJobTable, { jobs }) })
468
+ ] })
469
+ ] }) })
470
+ ] });
471
+ }
472
+ export {
473
+ BotsScreen
474
+ };