loopable-cli 0.2.1 → 0.3.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 (134) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/_connectorId-CDCuSgpN.js +1 -0
  3. package/.output/public/assets/_loopId-DOnIsVTW.js +1 -0
  4. package/.output/public/assets/_taskId-BO5MZnlZ.js +1 -0
  5. package/.output/public/assets/agents-CBozi7oi.js +1 -0
  6. package/.output/public/assets/alert-Bn_wjgTX.js +1 -0
  7. package/.output/public/assets/button-K3ypIhAZ.js +1 -0
  8. package/.output/public/assets/card-DiVnaTIE.js +1 -0
  9. package/.output/public/assets/chevron-right-BrctLtFK.js +1 -0
  10. package/.output/public/assets/connection-status-DKWelvLO.js +1 -0
  11. package/.output/public/assets/connector-icon-CEbdE71G.js +1 -0
  12. package/.output/public/assets/connectors-JyX3IzJj.js +1 -0
  13. package/.output/public/assets/gaps-PjmcbaWo.js +1 -0
  14. package/.output/public/assets/inbox-CujRR0Cz.js +1 -0
  15. package/.output/public/assets/index-C9SQ_Baq.js +16 -0
  16. package/.output/public/assets/loop-form-Dw3cuTzw.js +1 -0
  17. package/.output/public/assets/loops-DcEfZa2_.js +1 -0
  18. package/.output/public/assets/manifests-yJpevyye.js +1 -0
  19. package/.output/public/assets/new-CwsnNuRW.js +1 -0
  20. package/.output/public/assets/page-bvnfRMum.js +1 -0
  21. package/.output/public/assets/play-yHLs4DjK.js +1 -0
  22. package/.output/public/assets/plus-Ber25lrU.js +1 -0
  23. package/.output/public/assets/preload-helper-BoU_7EQC.js +3 -0
  24. package/.output/public/assets/{refresh-cw-C3xBnKfy.js → refresh-cw-BzSY7M8I.js} +1 -1
  25. package/.output/public/assets/rotate-ccw-DLv9EfCX.js +1 -0
  26. package/.output/public/assets/runners-DzX-RU3i.js +2 -0
  27. package/.output/public/assets/select-C9grIFwG.js +1 -0
  28. package/.output/public/assets/setting-fields-DUc3npgh.js +3 -0
  29. package/.output/public/assets/styles-B9B98zLk.css +2 -0
  30. package/.output/public/assets/switch-DwABVI6d.js +1 -0
  31. package/.output/public/assets/task-list-Ddk8-qt7.js +1 -0
  32. package/.output/public/assets/textarea-Ds-gIYUQ.js +1 -0
  33. package/.output/public/assets/time-goBWd-5J.js +1 -0
  34. package/.output/public/assets/use-live-tasks-DZNNuECy.js +1 -0
  35. package/.output/public/assets/useCompositeListItem-P4Nmmn7j.js +1 -0
  36. package/.output/public/assets/usePositioner-B7i9Ejzh.js +1 -0
  37. package/.output/server/_chunks/ssr-renderer.mjs +1 -1
  38. package/.output/server/{_connectorId-C9yydJrC.mjs → _connectorId-COuQN02a.mjs} +198 -188
  39. package/.output/server/_libs/@base-ui/react+[...].mjs +1410 -804
  40. package/.output/server/_libs/@tanstack/react-router+[...].mjs +1 -1
  41. package/.output/server/_libs/lucide-react.mjs +93 -84
  42. package/.output/server/_libs/next-themes.mjs +1 -1
  43. package/.output/server/_libs/sonner.mjs +1 -1
  44. package/.output/server/_loopId-CZh6_Yhr.mjs +310 -0
  45. package/.output/server/_ssr/{agents-DG643yoJ.mjs → agents-B_qo1gpL.mjs} +195 -6
  46. package/.output/server/_ssr/{agents-C7B6RJ6-.mjs → agents-Bz10gz_m.mjs} +8 -8
  47. package/.output/server/_ssr/agents-CjbOUp3Z.mjs +206 -0
  48. package/.output/server/_ssr/{alert-DQvzE7bu.mjs → alert-C2WBWfzo.mjs} +2 -2
  49. package/.output/server/_ssr/card-r1cV-PCN.mjs +36 -0
  50. package/.output/server/_ssr/{client-BtVRVhQ2.mjs → client-Dk3SD8Il.mjs} +5 -1
  51. package/.output/server/_ssr/connection-status-BogAL-Fj.mjs +32 -0
  52. package/.output/server/_ssr/{connections-Df-aDpfJ.mjs → connections-Cqvb6Gw2.mjs} +372 -325
  53. package/.output/server/_ssr/connector-icon-DrhVbhs6.mjs +190 -0
  54. package/.output/server/_ssr/connectors-BfS5tQKr.mjs +81 -0
  55. package/.output/server/_ssr/{connectors-e0mf2pMs.mjs → connectors-DJI-rzPU.mjs} +10 -10
  56. package/.output/server/_ssr/{createServerRpc-DPDrnFdH.mjs → createServerRpc-DDrp2nLT.mjs} +2 -2
  57. package/.output/server/_ssr/{gaps-Cz76ZicM.mjs → gaps-CcvHiLcj.mjs} +15 -7
  58. package/.output/server/_ssr/inbox-D1ef761v.mjs +178 -0
  59. package/.output/server/_ssr/loop-form-DWwOR05R.mjs +346 -0
  60. package/.output/server/_ssr/{loops-D8WuK7Ke.mjs → loops-6WhUe_lc.mjs} +37 -27
  61. package/.output/server/_ssr/loops-DHPjzG-t.mjs +217 -0
  62. package/.output/server/_ssr/new-ChKBdcXJ.mjs +245 -0
  63. package/.output/server/_ssr/page-DZVxKvdn.mjs +120 -0
  64. package/.output/server/_ssr/{router-54IDWTVz.mjs → router-DdwREiOf.mjs} +162 -489
  65. package/.output/server/_ssr/runners-CK9Cx9fS.mjs +217 -0
  66. package/.output/server/_ssr/runners-CrznFPHt.mjs +140 -0
  67. package/.output/server/_ssr/{runners-Cj6z9Mqh.mjs → runners-W-pgo5yo.mjs} +5 -5
  68. package/.output/server/_ssr/{secrets-CRnpwGBh.mjs → secrets-CV5scFlt.mjs} +2 -2
  69. package/.output/server/_ssr/select-BtndHhHb.mjs +76 -0
  70. package/.output/server/_ssr/{setting-fields-DzBlslan.mjs → setting-fields-LNgywAW7.mjs} +25 -20
  71. package/.output/server/_ssr/ssr.mjs +43 -42
  72. package/.output/server/_ssr/{start-Vq3Oan3v.mjs → start-C_j-e4vF.mjs} +9 -4
  73. package/.output/server/_ssr/{switch-1pnd_bPJ.mjs → switch-odxNO_S4.mjs} +3 -3
  74. package/.output/server/_ssr/task-list-Dnmq_WeE.mjs +150 -0
  75. package/.output/server/_ssr/{tasks-Qtml5TlK.mjs → tasks-D-kXLgVP.mjs} +13 -13
  76. package/.output/server/_ssr/{tasks-BDqznm57.mjs → tasks-D7aUvA33.mjs} +44 -17
  77. package/.output/server/_ssr/{textarea-CHJTKtsJ.mjs → textarea-D2c_vTIC.mjs} +2 -2
  78. package/.output/server/_ssr/time-Cy8TZP0Z.mjs +13 -0
  79. package/.output/server/_ssr/use-live-tasks-BzhTkEOL.mjs +40 -0
  80. package/.output/server/_tanstack-start-manifest_v-2HUdn-2E.mjs +166 -0
  81. package/.output/server/_taskId-D3KkNJW1.mjs +263 -0
  82. package/.output/server/index.mjs +199 -164
  83. package/README.md +36 -6
  84. package/dist/cli.mjs +161 -8
  85. package/dist/dispatcher.mjs +671 -285
  86. package/dist/runner.mjs +333 -46
  87. package/drizzle/0025_prompt_tasks_done.sql +2 -0
  88. package/drizzle/0026_guidance_into_prompt.sql +3 -0
  89. package/drizzle/0027_relative_folders.sql +5 -0
  90. package/drizzle/0028_runner_version.sql +5 -0
  91. package/drizzle/meta/_journal.json +29 -1
  92. package/package.json +4 -2
  93. package/.output/public/assets/_connectorId-CkygNLny.js +0 -1
  94. package/.output/public/assets/_loopId-Cshlc5Ja.js +0 -1
  95. package/.output/public/assets/_taskId-BKDIJ_Fg.js +0 -1
  96. package/.output/public/assets/alert-BT2EVMJP.js +0 -1
  97. package/.output/public/assets/arrow-left-CWUZt6k2.js +0 -1
  98. package/.output/public/assets/arrow-right-dy_oOg_c.js +0 -1
  99. package/.output/public/assets/connection-status-DCqqmY_6.js +0 -1
  100. package/.output/public/assets/connector-icon-BUoK6Cru.js +0 -1
  101. package/.output/public/assets/connectors-CeLBtEtg.js +0 -1
  102. package/.output/public/assets/external-link-DC11orFA.js +0 -1
  103. package/.output/public/assets/gaps-BtFoD7mF.js +0 -1
  104. package/.output/public/assets/inbox-BYNjkNDJ.js +0 -1
  105. package/.output/public/assets/index-0yP3Jn_G.js +0 -18
  106. package/.output/public/assets/loader-B4Epchm_.js +0 -1
  107. package/.output/public/assets/loop-form-DnSJNc1k.js +0 -1
  108. package/.output/public/assets/loops-1CvbqO6m.js +0 -1
  109. package/.output/public/assets/new-m5NlqYQT.js +0 -1
  110. package/.output/public/assets/play-DD4xQgvi.js +0 -1
  111. package/.output/public/assets/preload-helper-CfdAYKD7.js +0 -3
  112. package/.output/public/assets/rolldown-runtime-CbXtAM7H.js +0 -1
  113. package/.output/public/assets/runners-D8QVl-Y6.js +0 -1
  114. package/.output/public/assets/select-Cppu-hti.js +0 -1
  115. package/.output/public/assets/setting-fields-bJ_gTG7Q.js +0 -3
  116. package/.output/public/assets/styles-ClTGaXU0.css +0 -2
  117. package/.output/public/assets/switch-hRIbNGOj.js +0 -1
  118. package/.output/public/assets/task-list-BAxjcVgr.js +0 -1
  119. package/.output/public/assets/textarea-Fyuzi6Vp.js +0 -1
  120. package/.output/public/assets/trash-2-BXZzBsoV.js +0 -1
  121. package/.output/public/assets/useValueChanged-VVyq4hb9.js +0 -1
  122. package/.output/server/_loopId-B0VG-eBC.mjs +0 -301
  123. package/.output/server/_ssr/connection-status-BZr3Y-IC.mjs +0 -31
  124. package/.output/server/_ssr/connector-icon-WvxwZ5TF.mjs +0 -56
  125. package/.output/server/_ssr/connectors-BU0IiRge.mjs +0 -107
  126. package/.output/server/_ssr/inbox-CAUBa1I3.mjs +0 -114
  127. package/.output/server/_ssr/loop-form-FqSaXiHl.mjs +0 -413
  128. package/.output/server/_ssr/loops-Dqoebe8q.mjs +0 -222
  129. package/.output/server/_ssr/new-bBsVaWD6.mjs +0 -169
  130. package/.output/server/_ssr/runners-D2UAdLyj.mjs +0 -106
  131. package/.output/server/_ssr/runners-DGWaVvWi.mjs +0 -116
  132. package/.output/server/_ssr/task-list-_qgBS9LU.mjs +0 -187
  133. package/.output/server/_tanstack-start-manifest_v-CvheE7ZF.mjs +0 -131
  134. package/.output/server/_taskId-V4QLBO2N.mjs +0 -256
@@ -1,67 +1,27 @@
1
1
  import { i as __toESM } from "../_runtime.mjs";
2
- import { A as DialogRoot, B as mergeProps, C as TooltipArrow, D as TooltipTrigger$1, E as TooltipPortal, F as DialogBackdrop, I as Separator, L as Input, M as DialogPopup, N as DialogDescription, O as TooltipRoot, P as DialogClose, R as useRender, S as SelectRoot, T as TooltipPositioner, U as require_jsx_runtime, W as require_react, _ as SelectPositioner, b as SelectValue$1, d as SelectScrollDownArrow, f as SelectItemText, g as SelectPopup, h as SelectList, j as DialogPortal, k as DialogTitle, m as SelectItem$1, p as SelectItemIndicator, u as SelectScrollUpArrow, v as SelectPortal, w as TooltipPopup, x as SelectTrigger$1, y as SelectIcon, z as Button } from "../_libs/@base-ui/react+[...].mjs";
3
- import { a as useRouterState, c as Outlet, d as createRootRoute, f as Link, g as notFound, h as redirect, i as HeadContent, l as lazyRouteComponent, m as useRouter, r as Scripts, s as createRouter, u as createFileRoute } from "../_libs/@tanstack/react-router+[...].mjs";
4
- import { i as getServerFnById, n as createServerFn, r as TSS_SERVER_FUNCTION } from "./ssr.mjs";
2
+ import { A as TooltipTrigger$1, B as Input, D as TooltipPopup, E as TooltipArrow, F as DialogPortal, H as Button, I as DialogPopup, K as require_jsx_runtime, L as DialogDescription, N as DialogTitle, O as TooltipPositioner, P as DialogRoot, R as DialogClose, U as mergeProps, V as useRender, j as TooltipRoot, k as TooltipPortal, q as require_react, z as DialogBackdrop } from "../_libs/@base-ui/react+[...].mjs";
3
+ import { a as useRouterState, c as Outlet, d as createRootRoute, f as Link, g as notFound, h as redirect, i as HeadContent, l as lazyRouteComponent, r as Scripts, s as createRouter, u as createFileRoute } from "../_libs/@tanstack/react-router+[...].mjs";
4
+ import { a as getServerFnById, i as TSS_SERVER_FUNCTION, r as createServerFn } from "./ssr.mjs";
5
5
  import { n as clsx, t as cva } from "../_libs/class-variance-authority+clsx.mjs";
6
6
  import { t as twMerge } from "../_libs/tailwind-merge.mjs";
7
7
  import { _ as or, d as and, f as eq, g as lte, m as isNull } from "../_libs/drizzle-orm.mjs";
8
- import { d as runDir, g as tasks, s as db, t as __exportAll, u as rowsChanged } from "./client-BtVRVhQ2.mjs";
9
- import { n as isHosted } from "./secrets-CRnpwGBh.mjs";
10
- import { h as takeAuthAttempt, m as startAuthAttempt, o as connectorManifest, p as saveAuthorizedConnection, s as connectorRuntime } from "./connections-Df-aDpfJ.mjs";
8
+ import { d as runDir, g as tasks, s as db, t as __exportAll, u as rowsChanged } from "./client-Dk3SD8Il.mjs";
9
+ import { n as isHosted } from "./secrets-CV5scFlt.mjs";
10
+ import { _ as startAuthAttempt, c as connectorRuntime, g as saveAuthorizedConnection, s as connectorManifest, v as takeAuthAttempt } from "./connections-Cqvb6Gw2.mjs";
11
11
  import { a as callbackUrl, i as bearer, n as appOrigin } from "./access-DTttr563.mjs";
12
- import { t as AGENT_MANIFESTS } from "./agents-DG643yoJ.mjs";
13
- import { a as heartbeatRunner, d as runnerIdForToken, i as getRunner, o as isRunnerOnline, s as joinRunner } from "./runners-D2UAdLyj.mjs";
14
- import { c as jobRequiresHost, m as updateTask, p as taskRow, s as jobForTask, t as absorbAgentOutput } from "./tasks-BDqznm57.mjs";
15
- import { F as Check, I as Bot, M as CircleAlert, N as ChevronUp, O as CirclePlay, P as ChevronDown, _ as LoaderCircle, a as Terminal, d as Plug, h as MousePointerClick, j as CircleCheck, k as CirclePause, m as OctagonX, n as Workflow, o as Settings, p as PanelLeft, r as TriangleAlert, s as Server, t as X, v as Info, y as Inbox } from "../_libs/lucide-react.mjs";
12
+ import { a as getRunner, c as joinRunner, d as protocolOf, o as heartbeatRunner, p as runnerIdForToken, s as isRunnerOnline } from "./runners-CK9Cx9fS.mjs";
13
+ import { _ as updateTask, c as jobForTask, g as taskRow, l as jobMinProtocol, t as absorbAgentOutput, u as jobRequiresHost } from "./tasks-D7aUvA33.mjs";
14
+ import { A as CirclePause, M as CircleCheck, N as CircleAlert, _ as LoaderCircle, a as Settings, k as CirclePlay, m as OctagonX, n as Workflow, o as Server, p as PanelLeft, r as TriangleAlert, t as X, u as Plug, v as Info, y as Inbox, z as Bot } from "../_libs/lucide-react.mjs";
16
15
  import { n as toast, t as Toaster } from "../_libs/sonner.mjs";
17
16
  import { t as z } from "../_libs/next-themes.mjs";
18
17
  import { randomBytes } from "node:crypto";
19
18
  import { appendFileSync, existsSync } from "node:fs";
20
19
  import { join } from "node:path";
21
20
  import { hostname } from "node:os";
22
- //#region node_modules/.nitro/vite/services/ssr/assets/card-vAtU-sOo.js
23
- var import_react = /* @__PURE__ */ __toESM(require_react());
24
- var import_jsx_runtime = require_jsx_runtime();
21
+ //#region node_modules/.nitro/vite/services/ssr/assets/createSsrRpc-Cukki1qM.js
25
22
  function cn(...inputs) {
26
23
  return twMerge(clsx(inputs));
27
24
  }
28
- var buttonVariants = cva("group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", {
29
- variants: {
30
- variant: {
31
- default: "bg-primary text-primary-foreground hover:bg-primary/80",
32
- outline: "border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
33
- secondary: "bg-secondary text-secondary-foreground hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)] aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
34
- ghost: "hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50",
35
- destructive: "bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40",
36
- link: "text-primary underline-offset-4 hover:underline"
37
- },
38
- size: {
39
- default: "h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
40
- xs: "h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
41
- sm: "h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5",
42
- lg: "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
43
- icon: "size-8",
44
- "icon-xs": "size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3",
45
- "icon-sm": "size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg",
46
- "icon-lg": "size-9"
47
- }
48
- },
49
- defaultVariants: {
50
- variant: "default",
51
- size: "default"
52
- }
53
- });
54
- function Button$1({ className, variant = "default", size = "default", ...props }) {
55
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
56
- "data-slot": "button",
57
- className: cn(buttonVariants({
58
- variant,
59
- size,
60
- className
61
- })),
62
- ...props
63
- });
64
- }
65
25
  var createSsrRpc = (functionId) => {
66
26
  const url = "/_serverFn/" + functionId;
67
27
  const serverFnMeta = { id: functionId };
@@ -74,59 +34,8 @@ var createSsrRpc = (functionId) => {
74
34
  [TSS_SERVER_FUNCTION]: true
75
35
  });
76
36
  };
77
- var badgeVariants = cva("group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!", {
78
- variants: { variant: {
79
- default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
80
- secondary: "bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80",
81
- destructive: "bg-destructive/10 text-destructive focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:focus-visible:ring-destructive/40 [a]:hover:bg-destructive/20",
82
- outline: "border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground",
83
- ghost: "hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50",
84
- link: "text-primary underline-offset-4 hover:underline"
85
- } },
86
- defaultVariants: { variant: "default" }
87
- });
88
- function Badge({ className, variant = "default", render, ...props }) {
89
- return useRender({
90
- defaultTagName: "span",
91
- props: mergeProps({ className: cn(badgeVariants({ variant }), className) }, props),
92
- render,
93
- state: {
94
- slot: "badge",
95
- variant
96
- }
97
- });
98
- }
99
- function Card({ className, size = "default", ...props }) {
100
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
101
- "data-slot": "card",
102
- "data-size": size,
103
- className: cn("group/card flex flex-col gap-(--card-spacing) overflow-hidden rounded-xl bg-card py-(--card-spacing) text-sm text-card-foreground ring-1 ring-foreground/10 [--card-spacing:--spacing(4)] has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:[--card-spacing:--spacing(3)] data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl", className),
104
- ...props
105
- });
106
- }
107
- function CardHeader({ className, ...props }) {
108
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
109
- "data-slot": "card-header",
110
- className: cn("group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-t-xl px-(--card-spacing) has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] [.border-b]:pb-(--card-spacing)", className),
111
- ...props
112
- });
113
- }
114
- function CardTitle({ className, ...props }) {
115
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
116
- "data-slot": "card-title",
117
- className: cn("font-heading text-base leading-snug font-medium group-data-[size=sm]/card:text-sm", className),
118
- ...props
119
- });
120
- }
121
- function CardContent({ className, ...props }) {
122
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
123
- "data-slot": "card-content",
124
- className: cn("px-(--card-spacing)", className),
125
- ...props
126
- });
127
- }
128
37
  //#endregion
129
- //#region node_modules/.nitro/vite/services/ssr/assets/connectors-D95Rzky0.js
38
+ //#region node_modules/.nitro/vite/services/ssr/assets/connectors-pwWSovhz.js
130
39
  /**
131
40
  * Readiness and connections for every connector in the registry. Manifests are
132
41
  * imported directly by the client, so they are deliberately not sent again.
@@ -137,34 +46,34 @@ var getConnectorOverview = createServerFn({ method: "GET" }).handler(createSsrRp
137
46
  * than in the browser so the page needs no QR library and never learns how the
138
47
  * provider's login works.
139
48
  */
140
- var beginQrLogin = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(createSsrRpc("794964d675b97edaa881ab5cb209225ef0a4507e3d2771d6577658a058e7621f"));
49
+ var beginQrLogin = createServerFn({ method: "POST" }).validator((data) => data).handler(createSsrRpc("794964d675b97edaa881ab5cb209225ef0a4507e3d2771d6577658a058e7621f"));
141
50
  /**
142
51
  * One look at a pending scan. The credential never reaches the browser: a
143
52
  * confirmed login is saved here and the page is told only that it worked.
144
53
  */
145
- var continueQrLogin = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(createSsrRpc("43b97e3644ad73286fe5de3e99134bff099e1a8704f1ae91fe643d419eceba73"));
54
+ var continueQrLogin = createServerFn({ method: "POST" }).validator((data) => data).handler(createSsrRpc("43b97e3644ad73286fe5de3e99134bff099e1a8704f1ae91fe643d419eceba73"));
146
55
  /**
147
56
  * A secret pasted in the UI. Stored here; the page is told only the account
148
57
  * label, never the token back.
149
58
  */
150
- var connectWithToken = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(createSsrRpc("fbf19a0d12b4046a23a8a41e106a79443b174cf1cad922b029e23fb4679b20db"));
151
- var disconnectConnection = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(createSsrRpc("f94f0787ce6704ea24f18add66832e0367a0feb9a690d8ad6454abec5e5df193"));
152
- var verifyConnection = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(createSsrRpc("f9ade3084971078ee9f2bc16fcdcdfb5a8d66a0f9b634803bb611287c14e21d7"));
153
- var saveConnectionSettings = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(createSsrRpc("18bae5deb2a19571893f0067e374e537f16d6774f9b4e04f9b7349360f6f8fdc"));
59
+ var connectWithToken = createServerFn({ method: "POST" }).validator((data) => data).handler(createSsrRpc("fbf19a0d12b4046a23a8a41e106a79443b174cf1cad922b029e23fb4679b20db"));
60
+ var disconnectConnection = createServerFn({ method: "POST" }).validator((data) => data).handler(createSsrRpc("f94f0787ce6704ea24f18add66832e0367a0feb9a690d8ad6454abec5e5df193"));
61
+ var verifyConnection = createServerFn({ method: "POST" }).validator((data) => data).handler(createSsrRpc("f9ade3084971078ee9f2bc16fcdcdfb5a8d66a0f9b634803bb611287c14e21d7"));
62
+ var saveConnectionSettings = createServerFn({ method: "POST" }).validator((data) => data).handler(createSsrRpc("18bae5deb2a19571893f0067e374e537f16d6774f9b4e04f9b7349360f6f8fdc"));
154
63
  //#endregion
155
- //#region node_modules/.nitro/vite/services/ssr/assets/tasks-DXjXqmnW.js
64
+ //#region node_modules/.nitro/vite/services/ssr/assets/tasks-CbDJxQNY.js
156
65
  var getInbox = createServerFn({ method: "GET" }).handler(createSsrRpc("48bc32df9d6f69f45df8dfbc6e798e1f99152dbd4b4ed65b149fa2612c65e687"));
157
- var getLoopTasks = createServerFn({ method: "GET" }).inputValidator((data) => data).handler(createSsrRpc("3f938fac3e5ec1631e700a2fdd4790e78abbefeb732ecfbbe6864cc19f0d5aac"));
158
- var getTaskById = createServerFn({ method: "GET" }).inputValidator((data) => data).handler(createSsrRpc("0033ef7c619c027b1428a722dff188d315ed335645afcb1033528630b7c95e88"));
159
- var getTaskLog = createServerFn({ method: "GET" }).inputValidator((data) => data).handler(createSsrRpc("81f567724371f0d6f24e636906e2e9ea5b7384422ac583c86a1050fb838664ce"));
160
- var runPrompt = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(createSsrRpc("bef559b165754b06d6d5cf7c6dccde3d5a6a326462430c621eb131f78665d6e1"));
161
- var runLoopNow = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(createSsrRpc("7258ce5fc6f23acf3aab039ecd4acdb006d42b2ddbf3ab8629b4000e101b51dc"));
162
- var stopTask = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(createSsrRpc("a57f49c322cc5afa2996e4a5777fa533fd963755d614d4a14eb32e51fb198d9d"));
66
+ var getLoopTasks = createServerFn({ method: "GET" }).validator((data) => data).handler(createSsrRpc("3f938fac3e5ec1631e700a2fdd4790e78abbefeb732ecfbbe6864cc19f0d5aac"));
67
+ var getTaskById = createServerFn({ method: "GET" }).validator((data) => data).handler(createSsrRpc("0033ef7c619c027b1428a722dff188d315ed335645afcb1033528630b7c95e88"));
68
+ var getTaskLog = createServerFn({ method: "GET" }).validator((data) => data).handler(createSsrRpc("81f567724371f0d6f24e636906e2e9ea5b7384422ac583c86a1050fb838664ce"));
69
+ var runPrompt = createServerFn({ method: "POST" }).validator((data) => data).handler(createSsrRpc("bef559b165754b06d6d5cf7c6dccde3d5a6a326462430c621eb131f78665d6e1"));
70
+ var runLoopNow = createServerFn({ method: "POST" }).validator((data) => data).handler(createSsrRpc("7258ce5fc6f23acf3aab039ecd4acdb006d42b2ddbf3ab8629b4000e101b51dc"));
71
+ var stopTask = createServerFn({ method: "POST" }).validator((data) => data).handler(createSsrRpc("a57f49c322cc5afa2996e4a5777fa533fd963755d614d4a14eb32e51fb198d9d"));
163
72
  /** Whether the dispatcher is up, and how hard it is allowed to pull. */
164
73
  var getDispatcherState = createServerFn({ method: "GET" }).handler(createSsrRpc("921b049ebafe3c6fb5206fbe3e2100608d3333b944e18385e57da62a28233bc6"));
165
- var pauseDispatcher = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(createSsrRpc("0fa144f67263a4a51c51e8baf134a15b89c0afdf9b92b64660520a5bce247eec"));
74
+ var pauseDispatcher = createServerFn({ method: "POST" }).validator((data) => data).handler(createSsrRpc("0fa144f67263a4a51c51e8baf134a15b89c0afdf9b92b64660520a5bce247eec"));
166
75
  //#endregion
167
- //#region node_modules/.nitro/vite/services/ssr/assets/loops-Cds9PIF2.js
76
+ //#region node_modules/.nitro/vite/services/ssr/assets/loops-Dn50QW1N.js
168
77
  /**
169
78
  * Saving is both making and changing, told apart by whether there is an id.
170
79
  * A loop chosen and not yet saved has none, and nothing on the server knew
@@ -173,16 +82,18 @@ var pauseDispatcher = createServerFn({ method: "POST" }).inputValidator((data) =
173
82
  var edit = (data) => data;
174
83
  var getLoopsPage = createServerFn({ method: "GET" }).handler(createSsrRpc("6d5a90e9ec4ebf70d8e2ac4aff960533a5e68d21b289033cc7e0751d00554c93"));
175
84
  var getLoopReadiness = createServerFn({ method: "GET" }).handler(createSsrRpc("997d2dd097598c44811fe97def167b79d33e4349c51e783058da648e860c7c1f"));
176
- var getLoopById = createServerFn({ method: "GET" }).inputValidator((data) => data).handler(createSsrRpc("81a1af559b2a373901872aa9f9a6546f2f3bda8157d81c21a36ba0d1d337613b"));
177
- var saveLoop = createServerFn({ method: "POST" }).inputValidator(edit).handler(createSsrRpc("e91fc2fd51633c7ca9184fea4beb66fe775ed0b4e6323111a4690da916a78b00"));
178
- var toggleLoop = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(createSsrRpc("3fc8a7de98be979f9820a4f5a0ca2e234ed8a7df8e692abb8c4437765474eca0"));
179
- var removeLoop = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(createSsrRpc("b5a89ca2d07f4ccf2827b5132ca52e7c7a42d584e702b0ab3963b2d5763c35b0"));
180
- var reorderLoop = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(createSsrRpc("7d7d5f80a4f0d2b3bb659d7dc2a91ea5983880648b9d1aa24356e7340505930f"));
181
- var getLoopPollState = createServerFn({ method: "GET" }).inputValidator((data) => data).handler(createSsrRpc("4fd3855e3e9ed954e102071a55b98d91651c9b1e353161f7223768ed6f2b8c28"));
182
- var runLoopBacklog = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(createSsrRpc("65494cbfd06143dbd9a012808fbba21163d9902e387869503fb8b68b0674902b"));
183
- var lookLoopNow = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(createSsrRpc("965ff21ee2f1e1f5a1cb78d50570cbad6f58ccdbdcc7955f02f816a9c0684f8e"));
85
+ var getLoopById = createServerFn({ method: "GET" }).validator((data) => data).handler(createSsrRpc("81a1af559b2a373901872aa9f9a6546f2f3bda8157d81c21a36ba0d1d337613b"));
86
+ var saveLoop = createServerFn({ method: "POST" }).validator(edit).handler(createSsrRpc("e91fc2fd51633c7ca9184fea4beb66fe775ed0b4e6323111a4690da916a78b00"));
87
+ var toggleLoop = createServerFn({ method: "POST" }).validator((data) => data).handler(createSsrRpc("3fc8a7de98be979f9820a4f5a0ca2e234ed8a7df8e692abb8c4437765474eca0"));
88
+ var removeLoop = createServerFn({ method: "POST" }).validator((data) => data).handler(createSsrRpc("b5a89ca2d07f4ccf2827b5132ca52e7c7a42d584e702b0ab3963b2d5763c35b0"));
89
+ var reorderLoop = createServerFn({ method: "POST" }).validator((data) => data).handler(createSsrRpc("7d7d5f80a4f0d2b3bb659d7dc2a91ea5983880648b9d1aa24356e7340505930f"));
90
+ var getLoopPollState = createServerFn({ method: "GET" }).validator((data) => data).handler(createSsrRpc("4fd3855e3e9ed954e102071a55b98d91651c9b1e353161f7223768ed6f2b8c28"));
91
+ var runLoopBacklog = createServerFn({ method: "POST" }).validator((data) => data).handler(createSsrRpc("65494cbfd06143dbd9a012808fbba21163d9902e387869503fb8b68b0674902b"));
92
+ var lookLoopNow = createServerFn({ method: "POST" }).validator((data) => data).handler(createSsrRpc("965ff21ee2f1e1f5a1cb78d50570cbad6f58ccdbdcc7955f02f816a9c0684f8e"));
184
93
  //#endregion
185
- //#region node_modules/.nitro/vite/services/ssr/assets/router-54IDWTVz.js
94
+ //#region node_modules/.nitro/vite/services/ssr/assets/router-DdwREiOf.js
95
+ var import_react = /* @__PURE__ */ __toESM(require_react());
96
+ var import_jsx_runtime = require_jsx_runtime();
186
97
  /**
187
98
  * Loopable's mark: something going round, and the thing it goes round for.
188
99
  *
@@ -225,6 +136,43 @@ function LoopableMark({ className }) {
225
136
  ]
226
137
  });
227
138
  }
139
+ var buttonVariants = cva("group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", {
140
+ variants: {
141
+ variant: {
142
+ default: "bg-primary text-primary-foreground hover:bg-primary/80",
143
+ outline: "border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
144
+ secondary: "bg-secondary text-secondary-foreground hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)] aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
145
+ ghost: "hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50",
146
+ destructive: "bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40",
147
+ link: "text-primary underline-offset-4 hover:underline"
148
+ },
149
+ size: {
150
+ default: "h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
151
+ xs: "h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
152
+ sm: "h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5",
153
+ lg: "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
154
+ icon: "size-8",
155
+ "icon-xs": "size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3",
156
+ "icon-sm": "size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg",
157
+ "icon-lg": "size-9"
158
+ }
159
+ },
160
+ defaultVariants: {
161
+ variant: "default",
162
+ size: "default"
163
+ }
164
+ });
165
+ function Button$1({ className, variant = "default", size = "default", ...props }) {
166
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
167
+ "data-slot": "button",
168
+ className: cn(buttonVariants({
169
+ variant,
170
+ size,
171
+ className
172
+ })),
173
+ ...props
174
+ });
175
+ }
228
176
  /**
229
177
  * The dispatcher can stop between two page loads and nothing on a page would
230
178
  * change, so this asks rather than waiting to be told.
@@ -300,6 +248,28 @@ function DispatcherStatus() {
300
248
  })]
301
249
  });
302
250
  }
251
+ var badgeVariants = cva("group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!", {
252
+ variants: { variant: {
253
+ default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
254
+ secondary: "bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80",
255
+ destructive: "bg-destructive/10 text-destructive focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:focus-visible:ring-destructive/40 [a]:hover:bg-destructive/20",
256
+ outline: "border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground",
257
+ ghost: "hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50",
258
+ link: "text-primary underline-offset-4 hover:underline"
259
+ } },
260
+ defaultVariants: { variant: "default" }
261
+ });
262
+ function Badge({ className, variant = "default", render, ...props }) {
263
+ return useRender({
264
+ defaultTagName: "span",
265
+ props: mergeProps({ className: cn(badgeVariants({ variant }), className) }, props),
266
+ render,
267
+ state: {
268
+ slot: "badge",
269
+ variant
270
+ }
271
+ });
272
+ }
303
273
  var MOBILE_BREAKPOINT = 768;
304
274
  function useIsMobile() {
305
275
  const [isMobile, setIsMobile] = import_react.useState(void 0);
@@ -322,14 +292,6 @@ function Input$1({ className, type, ...props }) {
322
292
  ...props
323
293
  });
324
294
  }
325
- function Separator$1({ className, orientation = "horizontal", ...props }) {
326
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Separator, {
327
- "data-slot": "separator",
328
- orientation,
329
- className: cn("shrink-0 bg-border data-horizontal:h-px data-horizontal:w-full data-vertical:w-px data-vertical:self-stretch", className),
330
- ...props
331
- });
332
- }
333
295
  function Sheet({ ...props }) {
334
296
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogRoot, {
335
297
  "data-slot": "sheet",
@@ -782,7 +744,7 @@ var Toaster$1 = ({ ...props }) => {
782
744
  ...props
783
745
  });
784
746
  };
785
- var styles_default = "/assets/styles-ClTGaXU0.css";
747
+ var styles_default = "/assets/styles-B9B98zLk.css";
786
748
  var Route$18 = createRootRoute({
787
749
  head: () => ({
788
750
  meta: [
@@ -814,7 +776,7 @@ var Route$18 = createRootRoute({
814
776
  });
815
777
  function AppLayout() {
816
778
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SidebarProvider, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(AppSidebar, {}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SidebarInset, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
817
- className: "flex-1 p-6",
779
+ className: "flex-1 px-6 py-8 md:px-10",
818
780
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
819
781
  className: "mx-auto w-full max-w-4xl",
820
782
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Outlet, {})
@@ -834,326 +796,16 @@ function RootDocument({ children }) {
834
796
  var Route$17 = createFileRoute("/")({ beforeLoad: () => {
835
797
  throw redirect({ to: "/connectors" });
836
798
  } });
837
- function Label({ className, ...props }) {
838
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", {
839
- "data-slot": "label",
840
- className: cn("flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50", className),
841
- ...props
842
- });
843
- }
844
- var Select$1 = SelectRoot;
845
- function SelectValue({ className, ...props }) {
846
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectValue$1, {
847
- "data-slot": "select-value",
848
- className: cn("flex flex-1 text-left", className),
849
- ...props
850
- });
851
- }
852
- function SelectTrigger({ className, size = "default", children, ...props }) {
853
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectTrigger$1, {
854
- "data-slot": "select-trigger",
855
- "data-size": size,
856
- className: cn("flex w-fit items-center justify-between gap-1.5 rounded-lg border border-input bg-transparent py-2 pr-2 pl-2.5 text-sm whitespace-nowrap transition-colors outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-placeholder:text-muted-foreground data-[size=default]:h-8 data-[size=sm]:h-7 data-[size=sm]:rounded-[min(var(--radius-md),10px)] *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-1.5 dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className),
857
- ...props,
858
- children: [children, /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectIcon, { render: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronDown, { className: "pointer-events-none size-4 text-muted-foreground" }) })]
859
- });
860
- }
861
- function SelectContent({ className, children, side = "bottom", sideOffset = 4, align = "center", alignOffset = 0, alignItemWithTrigger = true, ...props }) {
862
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectPortal, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectPositioner, {
863
- side,
864
- sideOffset,
865
- align,
866
- alignOffset,
867
- alignItemWithTrigger,
868
- className: "isolate z-50",
869
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectPopup, {
870
- "data-slot": "select-content",
871
- "data-align-trigger": alignItemWithTrigger,
872
- className: cn("relative isolate z-50 max-h-(--available-height) w-(--anchor-width) min-w-36 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[align-trigger=true]:animate-none data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className),
873
- ...props,
874
- children: [
875
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectScrollUpButton, {}),
876
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectList, { children }),
877
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectScrollDownButton, {})
878
- ]
879
- })
880
- }) });
881
- }
882
- function SelectItem({ className, children, ...props }) {
883
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectItem$1, {
884
- "data-slot": "select-item",
885
- className: cn("relative flex w-full cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2", className),
886
- ...props,
887
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItemText, {
888
- className: "flex flex-1 shrink-0 gap-2 whitespace-nowrap",
889
- children
890
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItemIndicator, {
891
- render: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "pointer-events-none absolute right-2 flex size-4 items-center justify-center" }),
892
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Check, { className: "pointer-events-none" })
893
- })]
894
- });
895
- }
896
- function SelectScrollUpButton({ className, ...props }) {
897
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectScrollUpArrow, {
898
- "data-slot": "select-scroll-up-button",
899
- className: cn("top-0 z-10 flex w-full cursor-default items-center justify-center bg-popover py-1 [&_svg:not([class*='size-'])]:size-4", className),
900
- ...props,
901
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronUp, {})
902
- });
903
- }
904
- function SelectScrollDownButton({ className, ...props }) {
905
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectScrollDownArrow, {
906
- "data-slot": "select-scroll-down-button",
907
- className: cn("bottom-0 z-10 flex w-full cursor-default items-center justify-center bg-popover py-1 [&_svg:not([class*='size-'])]:size-4", className),
908
- ...props,
909
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronDown, {})
910
- });
911
- }
912
- /**
913
- * States still in flight. Used by the pages to know when
914
- * to keep looking, and by the reaper to find runs a dead process abandoned.
915
- */
916
- var TASK_ACTIVE_STATES = [
917
- "queued",
918
- "preparing",
919
- "awaiting_agent",
920
- "applying"
921
- ];
922
- function isTaskActive(state) {
923
- return TASK_ACTIVE_STATES.includes(state);
924
- }
925
- /**
926
- * The work happens in the dispatcher, so the page has to go and look. Polling only
927
- * while something is actually running keeps an idle inbox quiet.
928
- */
929
- function useLiveTasks(states, everyMs = 2e3) {
930
- useLiveRefresh(everyMs, states.some((task) => isTaskActive(task.state)));
931
- }
932
- /** Runner inventory changes without this page doing anything, so it looks again. */
933
- function useLiveRefresh(everyMs = 1e4, enabled = true) {
934
- const router = useRouter();
935
- (0, import_react.useEffect)(() => {
936
- if (!enabled) return;
937
- const timer = setInterval(() => void router.invalidate(), everyMs);
938
- return () => clearInterval(timer);
939
- }, [
940
- enabled,
941
- everyMs,
942
- router
943
- ]);
944
- }
945
799
  var getAgentsPage = createServerFn({ method: "GET" }).handler(createSsrRpc("bde588e9b0b81a4ee619a63872d6b8b76e22db006c7a3c4351bcd56e5f76bbed"));
946
- var saveAgent = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(createSsrRpc("80883b912a5a0aba2aa583b505e4e289d765a3f7fbb793a97692f439e7f960ba"));
947
- var chooseDefaultAgent = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(createSsrRpc("143ea51f7421d9eebec9ad31c2df8eed5aa2e5d8035473dbfad39913fd7d54c0"));
948
- var runAgentTest = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(createSsrRpc("b0878f841b35f46b5f2dc9cbd71c5be5895ae1b8903b3b24c24dfcf86291d3e4"));
949
- var ICONS = {
950
- Terminal,
951
- MousePointerClick,
952
- Bot
953
- };
954
- var MODE_LABELS = {
955
- read_only: "Read only",
956
- workspace_write: "Can write in the workspace"
957
- };
800
+ var saveAgent = createServerFn({ method: "POST" }).validator((data) => data).handler(createSsrRpc("80883b912a5a0aba2aa583b505e4e289d765a3f7fbb793a97692f439e7f960ba"));
801
+ var chooseDefaultAgent = createServerFn({ method: "POST" }).validator((data) => data).handler(createSsrRpc("143ea51f7421d9eebec9ad31c2df8eed5aa2e5d8035473dbfad39913fd7d54c0"));
802
+ var runAgentTest = createServerFn({ method: "POST" }).validator((data) => data).handler(createSsrRpc("b0878f841b35f46b5f2dc9cbd71c5be5895ae1b8903b3b24c24dfcf86291d3e4"));
803
+ var $$splitComponentImporter$8 = () => import("./agents-CjbOUp3Z.mjs");
958
804
  var Route$16 = createFileRoute("/agents/")({
959
805
  loader: () => getAgentsPage(),
960
- component: AgentsPage
806
+ component: lazyRouteComponent($$splitComponentImporter$8, "component")
961
807
  });
962
- function AgentsPage() {
963
- const { agents } = Route$16.useLoaderData();
964
- useLiveRefresh();
965
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
966
- className: "flex flex-col gap-6",
967
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", {
968
- className: "text-2xl font-semibold tracking-tight",
969
- children: "Agents"
970
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
971
- className: "mt-1 text-sm text-muted-foreground",
972
- children: "The coding CLIs Loopable knows how to run. Presence comes from runners. Settings here are how every runner invokes that CLI."
973
- })] }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
974
- className: "flex flex-col gap-2",
975
- children: AGENT_MANIFESTS.map((manifest) => {
976
- const view = agents.find((agent) => agent.agentId === manifest.id);
977
- return view ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AgentRow, {
978
- manifest,
979
- view
980
- }, manifest.id) : null;
981
- })
982
- })]
983
- });
984
- }
985
- function presence(view) {
986
- const signedIn = view.onRunners.filter((runner) => runner.signedIn);
987
- const online = signedIn.filter((runner) => runner.status === "online");
988
- if (online.length > 0) return online.length === 1 ? `Signed in on ${online[0].name}` : `Signed in on ${online.length} runners`;
989
- if (signedIn.length > 0) return "Signed in, but every runner is offline";
990
- if (view.onRunners.length > 0) return "Installed, not signed in";
991
- return "Not on any runner";
992
- }
993
- function AgentRow({ manifest, view }) {
994
- const router = useRouter();
995
- const Icon = ICONS[manifest.icon] ?? Bot;
996
- const [open, setOpen] = (0, import_react.useState)(view.isDefault && !view.defaultIsImplicit);
997
- const [mode, setMode] = (0, import_react.useState)(view.settings.permissionMode);
998
- const [model, setModel] = (0, import_react.useState)(view.settings.model ?? "");
999
- const [seconds, setSeconds] = (0, import_react.useState)(String(Math.round(view.settings.timeoutMs / 1e3)));
1000
- const [busy, setBusy] = (0, import_react.useState)(null);
1001
- const [testOutput, setTestOutput] = (0, import_react.useState)(null);
1002
- const online = view.onRunners.some((runner) => runner.signedIn && runner.status === "online");
1003
- const act = async (kind, run) => {
1004
- setBusy(kind);
1005
- try {
1006
- await run();
1007
- } catch (error) {
1008
- toast.error(error instanceof Error ? error.message : String(error));
1009
- } finally {
1010
- setBusy(null);
1011
- }
1012
- };
1013
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Card, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1014
- className: "flex items-start gap-3 p-4",
1015
- children: [
1016
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
1017
- className: cn("mt-0.5 flex size-8 shrink-0 items-center justify-center rounded-md", online ? manifest.accent : "bg-muted text-muted-foreground"),
1018
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { className: "size-4" })
1019
- }),
1020
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", {
1021
- type: "button",
1022
- className: "min-w-0 flex-1 text-left",
1023
- onClick: () => setOpen((value) => !value),
1024
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1025
- className: "flex flex-wrap items-center gap-2",
1026
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
1027
- className: "font-medium",
1028
- children: manifest.name
1029
- }), view.isDefault ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Badge, { children: view.defaultIsImplicit ? "Default (only one signed in)" : "Default" }) : null]
1030
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
1031
- className: "mt-0.5 text-sm text-muted-foreground",
1032
- children: presence(view)
1033
- })]
1034
- }),
1035
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1036
- className: "flex shrink-0 items-center gap-1",
1037
- children: [!view.isDefault ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
1038
- variant: "ghost",
1039
- size: "sm",
1040
- disabled: busy !== null,
1041
- onClick: () => act("default", async () => {
1042
- await chooseDefaultAgent({ data: { agentId: manifest.id } });
1043
- await router.invalidate();
1044
- toast.success(`${manifest.name} will run the loops`);
1045
- }),
1046
- children: "Default"
1047
- }) : null, /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
1048
- variant: "ghost",
1049
- size: "icon-sm",
1050
- "aria-expanded": open,
1051
- "aria-label": "Settings",
1052
- onClick: () => setOpen((value) => !value),
1053
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronDown, { className: cn("size-4 transition-transform", open && "rotate-180") })
1054
- })]
1055
- })
1056
- ]
1057
- }), open ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(CardContent, {
1058
- className: "flex flex-col gap-4 border-t pt-4",
1059
- children: [
1060
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
1061
- className: "text-sm text-muted-foreground",
1062
- children: manifest.tagline
1063
- }),
1064
- !online ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
1065
- className: "text-sm text-muted-foreground",
1066
- children: manifest.installHint
1067
- }) : null,
1068
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1069
- className: "grid gap-4 sm:grid-cols-3",
1070
- children: [
1071
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1072
- className: "flex flex-col gap-2",
1073
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Label, {
1074
- htmlFor: `${manifest.id}-mode`,
1075
- children: "Permission"
1076
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Select$1, {
1077
- value: mode,
1078
- onValueChange: (value) => setMode(value),
1079
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectTrigger, {
1080
- id: `${manifest.id}-mode`,
1081
- className: "w-full",
1082
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectValue, { children: (value) => MODE_LABELS[value] ?? value })
1083
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectContent, { children: manifest.permissionModes.map((value) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
1084
- value,
1085
- children: MODE_LABELS[value]
1086
- }, value)) })]
1087
- })]
1088
- }),
1089
- manifest.supportsModel ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1090
- className: "flex flex-col gap-2",
1091
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Label, {
1092
- htmlFor: `${manifest.id}-model`,
1093
- children: "Model"
1094
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Input$1, {
1095
- id: `${manifest.id}-model`,
1096
- value: model,
1097
- placeholder: manifest.modelPlaceholder,
1098
- onChange: (event) => setModel(event.target.value)
1099
- })]
1100
- }) : null,
1101
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1102
- className: "flex flex-col gap-2",
1103
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Label, {
1104
- htmlFor: `${manifest.id}-timeout`,
1105
- children: "Timeout (seconds)"
1106
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Input$1, {
1107
- id: `${manifest.id}-timeout`,
1108
- value: seconds,
1109
- inputMode: "numeric",
1110
- onChange: (event) => setSeconds(event.target.value.replace(/\D/g, ""))
1111
- })]
1112
- })
1113
- ]
1114
- }),
1115
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("pre", {
1116
- className: "overflow-x-auto rounded-md bg-muted p-2.5 text-xs",
1117
- children: view.commandPreview
1118
- }),
1119
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1120
- className: "flex flex-wrap items-center gap-2",
1121
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
1122
- size: "sm",
1123
- disabled: busy !== null,
1124
- onClick: () => act("save", async () => {
1125
- await saveAgent({ data: {
1126
- agentId: manifest.id,
1127
- permissionMode: mode,
1128
- model: model.trim() ? model.trim() : null,
1129
- timeoutMs: Number(seconds) * 1e3
1130
- } });
1131
- await router.invalidate();
1132
- toast.success(`Saved ${manifest.name} settings`);
1133
- }),
1134
- children: busy === "save" ? "Saving..." : "Save"
1135
- }), view.installed ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button$1, {
1136
- variant: "outline",
1137
- size: "sm",
1138
- disabled: busy !== null,
1139
- onClick: () => act("test", async () => {
1140
- setTestOutput(null);
1141
- const result = await runAgentTest({ data: { agentId: manifest.id } });
1142
- setTestOutput(result.passed ? `Answered correctly in ${(result.durationMs / 1e3).toFixed(1)}s.` : `${result.detail ?? "The agent did not answer as expected."}\n${result.output}`.trim());
1143
- if (result.passed) toast.success(`${manifest.name} is working`);
1144
- else toast.error(`${manifest.name} did not pass`);
1145
- }),
1146
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CirclePlay, {}), busy === "test" ? "Running..." : "Test"]
1147
- }) : null]
1148
- }),
1149
- testOutput ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
1150
- className: "whitespace-pre-wrap text-xs text-muted-foreground",
1151
- children: testOutput
1152
- }) : null
1153
- ]
1154
- }) : null] });
1155
- }
1156
- var $$splitComponentImporter$7 = () => import("./connectors-BU0IiRge.mjs");
808
+ var $$splitComponentImporter$7 = () => import("./connectors-BfS5tQKr.mjs");
1157
809
  var Route$15 = createFileRoute("/connectors/")({
1158
810
  validateSearch: (search) => {
1159
811
  const result = {};
@@ -1164,22 +816,28 @@ var Route$15 = createFileRoute("/connectors/")({
1164
816
  loader: () => getConnectorOverview(),
1165
817
  component: lazyRouteComponent($$splitComponentImporter$7, "component")
1166
818
  });
1167
- var $$splitComponentImporter$6 = () => import("../_connectorId-C9yydJrC.mjs");
819
+ var $$splitComponentImporter$6 = () => import("../_connectorId-COuQN02a.mjs");
1168
820
  var Route$14 = createFileRoute("/connectors/$connectorId")({
1169
821
  loader: async ({ params }) => {
1170
822
  if (!connectorManifest(params.connectorId)) throw notFound();
1171
- const state = (await getConnectorOverview()).find((entry) => entry.connectorId === params.connectorId);
823
+ const [overview, page] = await Promise.all([getConnectorOverview(), getLoopsPage()]);
824
+ const state = overview.find((entry) => entry.connectorId === params.connectorId);
1172
825
  if (!state) throw notFound();
1173
- return state;
826
+ const loops = page.loops.filter((loop) => loop.connectorId === params.connectorId || loop.actionConnectorId === params.connectorId);
827
+ return {
828
+ ...state,
829
+ loops
830
+ };
1174
831
  },
1175
832
  component: lazyRouteComponent($$splitComponentImporter$6, "component")
1176
833
  });
1177
- var $$splitComponentImporter$5 = () => import("./inbox-CAUBa1I3.mjs");
834
+ var $$splitComponentImporter$5 = () => import("./inbox-D1ef761v.mjs");
1178
835
  var Route$13 = createFileRoute("/inbox/")({
1179
836
  loader: () => getInbox(),
1180
837
  component: lazyRouteComponent($$splitComponentImporter$5, "component")
1181
838
  });
1182
- var $$splitComponentImporter$4 = () => import("../_taskId-V4QLBO2N.mjs");
839
+ /** Ask an agent something once. The reply stays in Inbox; nothing is written back. */
840
+ var $$splitComponentImporter$4 = () => import("../_taskId-D3KkNJW1.mjs");
1183
841
  var Route$12 = createFileRoute("/inbox/$taskId")({
1184
842
  loader: async ({ params }) => {
1185
843
  const task = await getTaskById({ data: { id: params.taskId } });
@@ -1191,6 +849,7 @@ var Route$12 = createFileRoute("/inbox/$taskId")({
1191
849
  },
1192
850
  component: lazyRouteComponent($$splitComponentImporter$4, "component")
1193
851
  });
852
+ /** A titled block that opens and closes, for what is only sometimes worth reading. */
1194
853
  /**
1195
854
  * The findings that go on the lines rather than in the body. Shown separately
1196
855
  * because that is what they are: on GitHub they will be spread across the
@@ -1206,19 +865,16 @@ var Route$12 = createFileRoute("/inbox/$taskId")({
1206
865
  * sees it when it next renews its claim, which is why the button says it has
1207
866
  * been asked for rather than pretending it is already done.
1208
867
  */
1209
- var $$splitComponentImporter$3 = () => import("./loops-Dqoebe8q.mjs");
868
+ var $$splitComponentImporter$3 = () => import("./loops-DHPjzG-t.mjs");
1210
869
  var Route$11 = createFileRoute("/loops/")({
1211
870
  loader: () => getLoopsPage(),
1212
871
  component: lazyRouteComponent($$splitComponentImporter$3, "component")
1213
872
  });
1214
- /** Says what a loop still needs, without standing in the way of writing one. */
1215
- /**
1216
- * Where this loop writes, in its own words rather than the workflow's. A loop
1217
- * may have been pointed somewhere else since it was made, and the card is the
1218
- * one place someone scanning a list would notice.
1219
- */
1220
- var $$splitComponentImporter$2 = () => import("../_loopId-B0VG-eBC.mjs");
873
+ /** What still stands between these loops and running. */
874
+ /** Where this loop answers and with what, in a few words. */
875
+ var $$splitComponentImporter$2 = () => import("../_loopId-CZh6_Yhr.mjs");
1221
876
  var Route$10 = createFileRoute("/loops/$loopId")({
877
+ validateSearch: (search) => search.tab === "tasks" ? { tab: "tasks" } : {},
1222
878
  loader: async ({ params }) => {
1223
879
  const loop = await getLoopById({ data: { id: params.loopId } });
1224
880
  if (!loop) throw notFound();
@@ -1231,50 +887,53 @@ var Route$10 = createFileRoute("/loops/$loopId")({
1231
887
  },
1232
888
  component: lazyRouteComponent($$splitComponentImporter$2, "component")
1233
889
  });
890
+ /** One line under the title: where it answers, with what, and when it last looked. */
1234
891
  /**
1235
- * Whether the loop is really watching is the one thing a page cannot work out
1236
- * for itself, and the thing a person most wants to know after turning one on.
1237
- */
1238
- /**
1239
- * A loop that runs by itself still has to be shaped, and waiting for a real
1240
- * signal to arrive is a slow way to do it. Dry run means that shaping leaves
1241
- * no marks on a real repository.
892
+ * What matched but was never run: open before the loop existed, or held back
893
+ * by one of its rules. Neither runs by itself, so it stays folded into one
894
+ * line, with the reason said in the line.
1242
895
  */
1243
- var $$splitComponentImporter$1 = () => import("./new-bBsVaWD6.mjs");
896
+ /** Run the loop on one link, by hand, without waiting for something to arrive. */
897
+ var $$splitComponentImporter$1 = () => import("./new-ChKBdcXJ.mjs");
1244
898
  var Route$9 = createFileRoute("/loops/new")({
1245
899
  validateSearch: (search) => {
1246
900
  const result = {};
1247
901
  if (typeof search.connector === "string") result.connector = search.connector;
1248
902
  if (typeof search.workflow === "string") result.workflow = search.workflow;
903
+ if (search.custom === true || search.custom === "true" || search.custom === 1) result.custom = true;
1249
904
  return result;
1250
905
  },
1251
906
  loader: async () => ({ loops: (await getLoopsPage()).loops }),
1252
907
  component: lazyRouteComponent($$splitComponentImporter$1, "component")
1253
908
  });
909
+ /** The trigger a custom loop starts on before anything is picked. */
1254
910
  /**
1255
- * The second step, and the one that makes a loop.
1256
- *
1257
- * Nothing has been written down yet: the draft is worked out here from the
1258
- * workflow the URL names, and the loop begins when the form is saved. Picking
1259
- * a workflow used to create it, which meant a look around this page left a
1260
- * live loop behind, enabled and with none of its questions answered.
911
+ * The second step, and the one that makes a loop. Nothing is written down
912
+ * until the form is saved.
913
+ */
914
+ /**
915
+ * A loop with your own prompt. Underneath it is still one of the workflows,
916
+ * since a workflow is what knows how to watch for something and read the
917
+ * answer, but it starts with an empty prompt and a trigger you pick.
1261
918
  */
919
+ /** How many loops already use a workflow, said as a sentence rather than a count. */
1262
920
  /**
1263
- * There is no blank loop to write any more. Every loop is one of the
1264
- * workflows a connector offers, already knowing its own job, so choosing is
1265
- * the first half of making one and the settings are the second.
921
+ * Every loop is one of the workflows a connector offers, or a custom one
922
+ * built on a workflow's trigger. Custom and Schedule come first: they are the
923
+ * two not tied to something arriving from a service.
1266
924
  */
1267
925
  var Route$8 = createFileRoute("/machines/")({ beforeLoad: () => {
1268
926
  throw redirect({ to: "/runners" });
1269
927
  } });
1270
928
  var getRunnersPage = createServerFn({ method: "GET" }).handler(createSsrRpc("45dbbb612763589268984d236edcd17f5868ecf716df9625bfa46e7fec691f4e"));
1271
929
  var rotateRunnerJoinToken = createServerFn({ method: "POST" }).handler(createSsrRpc("b4f7a66e1bb69cc14ab84d8f5dc7f85119c9a9b1d065a6b4e51dd0ba84c3f60c"));
1272
- var removeRunner = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(createSsrRpc("611491b32d9583ccaae637ddf9fb2f842ff75994f50513ecd8273e305e889eb8"));
1273
- var $$splitComponentImporter = () => import("./runners-DGWaVvWi.mjs");
930
+ var removeRunner = createServerFn({ method: "POST" }).validator((data) => data).handler(createSsrRpc("611491b32d9583ccaae637ddf9fb2f842ff75994f50513ecd8273e305e889eb8"));
931
+ var $$splitComponentImporter = () => import("./runners-CrznFPHt.mjs");
1274
932
  var Route$7 = createFileRoute("/runners/")({
1275
933
  loader: () => getRunnersPage(),
1276
934
  component: lazyRouteComponent($$splitComponentImporter, "component")
1277
935
  });
936
+ /** Which agents this runner has, in a few words. */
1278
937
  async function claimAgentJob(runnerId) {
1279
938
  const row = await getRunner(runnerId);
1280
939
  if (!row || !isRunnerOnline(row)) return null;
@@ -1290,6 +949,14 @@ async function claimAgentJob(runnerId) {
1290
949
  await updateTask(task.id, { leaseUntil: null });
1291
950
  return null;
1292
951
  }
952
+ if (jobMinProtocol(job) > protocolOf(row)) {
953
+ await updateTask(task.id, {
954
+ state: "failed",
955
+ error: `${row.name} is too old for this job. Upgrade it with npm install -g loopable-cli, then run this again.`,
956
+ leaseUntil: null
957
+ });
958
+ continue;
959
+ }
1293
960
  return job;
1294
961
  }
1295
962
  return null;
@@ -1333,8 +1000,8 @@ async function completeAgentJob(taskId, runnerId, result) {
1333
1000
  }
1334
1001
  await absorbAgentOutput(taskId, result.output);
1335
1002
  }
1336
- function touchRunner(runnerId, inventory) {
1337
- return heartbeatRunner(runnerId, inventory);
1003
+ function touchRunner(runnerId, inventory, build = {}) {
1004
+ return heartbeatRunner(runnerId, inventory, build);
1338
1005
  }
1339
1006
  var Route$6 = createFileRoute("/api/runners/claim")({ server: { handlers: { POST: async ({ request }) => {
1340
1007
  const runnerId = await runnerIdForToken(bearer(request));
@@ -1345,7 +1012,11 @@ var Route$6 = createFileRoute("/api/runners/claim")({ server: { handlers: { POST
1345
1012
  var Route$5 = createFileRoute("/api/runners/heartbeat")({ server: { handlers: { POST: async ({ request }) => {
1346
1013
  const runnerId = await runnerIdForToken(bearer(request));
1347
1014
  if (!runnerId) return Response.json({ error: "Unknown runner." }, { status: 401 });
1348
- const view = await touchRunner(runnerId, (await request.json()).inventory ?? []);
1015
+ const body = await request.json();
1016
+ const view = await touchRunner(runnerId, body.inventory ?? [], {
1017
+ version: body.version,
1018
+ protocol: body.protocol
1019
+ });
1349
1020
  return Response.json(view);
1350
1021
  } } } });
1351
1022
  var Route$4 = createFileRoute("/api/runners/hello")({ server: { handlers: { POST: async ({ request }) => {
@@ -1354,7 +1025,9 @@ var Route$4 = createFileRoute("/api/runners/hello")({ server: { handlers: { POST
1354
1025
  const result = await joinRunner({
1355
1026
  joinToken: bearer(request),
1356
1027
  hostname: body.hostname ?? "unknown",
1357
- inventory: body.inventory ?? []
1028
+ inventory: body.inventory ?? [],
1029
+ version: body.version,
1030
+ protocol: body.protocol
1358
1031
  });
1359
1032
  return Response.json(result);
1360
1033
  } catch (error) {
@@ -1556,4 +1229,4 @@ function getRouter() {
1556
1229
  });
1557
1230
  }
1558
1231
  //#endregion
1559
- export { runPrompt as A, Card as B, lookLoopNow as C, saveLoop as D, runLoopBacklog as E, disconnectConnection as F, CardHeader as H, saveConnectionSettings as I, verifyConnection as L, beginQrLogin as M, connectWithToken as N, toggleLoop as O, continueQrLogin as P, Badge as R, Input$1 as S, reorderLoop as T, CardTitle as U, CardContent as V, cn as W, SelectItem as _, Route$9 as a, Label as b, Route$12 as c, Route$15 as d, useLiveRefresh as f, SelectContent as g, Select$1 as h, rotateRunnerJoinToken as i, stopTask as j, runLoopNow as k, Route$13 as l, isTaskActive as m, Route$7 as n, Route$10 as o, useLiveTasks as p, removeRunner as r, Route$11 as s, router_exports as t, Route$14 as u, SelectTrigger as v, removeLoop as w, Separator$1 as x, SelectValue as y, Button$1 as z };
1232
+ export { continueQrLogin as A, saveLoop as C, stopTask as D, runPrompt as E, saveConnectionSettings as M, verifyConnection as N, beginQrLogin as O, cn as P, runLoopBacklog as S, runLoopNow as T, Badge as _, Route$9 as a, removeLoop as b, Route$12 as c, Route$15 as d, Route$16 as f, Input$1 as g, saveAgent as h, rotateRunnerJoinToken as i, disconnectConnection as j, connectWithToken as k, Route$13 as l, runAgentTest as m, Route$7 as n, Route$10 as o, chooseDefaultAgent as p, removeRunner as r, Route$11 as s, router_exports as t, Route$14 as u, Button$1 as v, toggleLoop as w, reorderLoop as x, lookLoopNow as y };