loopable-cli 0.2.0 → 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 (138) 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-BzSY7M8I.js +1 -0
  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-COuQN02a.mjs +512 -0
  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/{callback-D20cVD2w.mjs → access-DTttr563.mjs} +37 -20
  46. package/.output/server/_ssr/{agents-CrUmIQpi.mjs → agents-B_qo1gpL.mjs} +196 -7
  47. package/.output/server/_ssr/{agents-Dms_6TBF.mjs → agents-Bz10gz_m.mjs} +8 -8
  48. package/.output/server/_ssr/agents-CjbOUp3Z.mjs +206 -0
  49. package/.output/server/_ssr/{alert-DQvzE7bu.mjs → alert-C2WBWfzo.mjs} +2 -2
  50. package/.output/server/_ssr/card-r1cV-PCN.mjs +36 -0
  51. package/.output/server/_ssr/{client-BtVRVhQ2.mjs → client-Dk3SD8Il.mjs} +5 -1
  52. package/.output/server/_ssr/connection-status-BogAL-Fj.mjs +32 -0
  53. package/.output/server/_ssr/{connections-ypex3qUd.mjs → connections-Cqvb6Gw2.mjs} +1109 -263
  54. package/.output/server/_ssr/connector-icon-DrhVbhs6.mjs +190 -0
  55. package/.output/server/_ssr/connectors-BfS5tQKr.mjs +81 -0
  56. package/.output/server/_ssr/{connectors-BZly0yGF.mjs → connectors-DJI-rzPU.mjs} +21 -10
  57. package/.output/server/_ssr/{createServerRpc-DPDrnFdH.mjs → createServerRpc-DDrp2nLT.mjs} +2 -2
  58. package/.output/server/_ssr/{gaps-D2Zh8sEo.mjs → gaps-CcvHiLcj.mjs} +18 -10
  59. package/.output/server/_ssr/inbox-D1ef761v.mjs +178 -0
  60. package/.output/server/_ssr/loop-form-DWwOR05R.mjs +346 -0
  61. package/.output/server/_ssr/{loops-Og_G3KyZ.mjs → loops-6WhUe_lc.mjs} +228 -25
  62. package/.output/server/_ssr/loops-DHPjzG-t.mjs +217 -0
  63. package/.output/server/_ssr/new-ChKBdcXJ.mjs +245 -0
  64. package/.output/server/_ssr/page-DZVxKvdn.mjs +120 -0
  65. package/.output/server/_ssr/{router-BbrBz36P.mjs → router-DdwREiOf.mjs} +167 -489
  66. package/.output/server/_ssr/runners-CK9Cx9fS.mjs +217 -0
  67. package/.output/server/_ssr/runners-CrznFPHt.mjs +140 -0
  68. package/.output/server/_ssr/runners-W-pgo5yo.mjs +55 -0
  69. package/.output/server/_ssr/{secrets-CRnpwGBh.mjs → secrets-CV5scFlt.mjs} +2 -2
  70. package/.output/server/_ssr/select-BtndHhHb.mjs +76 -0
  71. package/.output/server/_ssr/{setting-fields-mpXfGftI.mjs → setting-fields-LNgywAW7.mjs} +25 -20
  72. package/.output/server/_ssr/ssr.mjs +49 -40
  73. package/.output/server/_ssr/{start-BgTj-h_C.mjs → start-C_j-e4vF.mjs} +10 -6
  74. package/.output/server/_ssr/{switch-1pnd_bPJ.mjs → switch-odxNO_S4.mjs} +3 -3
  75. package/.output/server/_ssr/task-list-Dnmq_WeE.mjs +150 -0
  76. package/.output/server/_ssr/{tasks-LIi-W4cy.mjs → tasks-D-kXLgVP.mjs} +13 -13
  77. package/.output/server/_ssr/{tasks-B_QmDFVs.mjs → tasks-D7aUvA33.mjs} +55 -17
  78. package/.output/server/_ssr/{textarea-CHJTKtsJ.mjs → textarea-D2c_vTIC.mjs} +2 -2
  79. package/.output/server/_ssr/time-Cy8TZP0Z.mjs +13 -0
  80. package/.output/server/_ssr/use-live-tasks-BzhTkEOL.mjs +40 -0
  81. package/.output/server/_tanstack-start-manifest_v-2HUdn-2E.mjs +166 -0
  82. package/.output/server/_taskId-D3KkNJW1.mjs +263 -0
  83. package/.output/server/index.mjs +204 -162
  84. package/README.md +54 -19
  85. package/dist/cli.mjs +185 -67
  86. package/dist/dispatcher.mjs +1443 -277
  87. package/dist/runner.mjs +333 -46
  88. package/drizzle/0025_prompt_tasks_done.sql +2 -0
  89. package/drizzle/0026_guidance_into_prompt.sql +3 -0
  90. package/drizzle/0027_relative_folders.sql +5 -0
  91. package/drizzle/0028_runner_version.sql +5 -0
  92. package/drizzle/meta/_journal.json +29 -1
  93. package/package.json +23 -17
  94. package/.output/public/assets/_connectorId-m67WbiJ8.js +0 -1
  95. package/.output/public/assets/_loopId-Bf4K5sdd.js +0 -1
  96. package/.output/public/assets/_taskId-eCKkcVOx.js +0 -1
  97. package/.output/public/assets/alert-BT2EVMJP.js +0 -1
  98. package/.output/public/assets/arrow-left-CWUZt6k2.js +0 -1
  99. package/.output/public/assets/arrow-right-dy_oOg_c.js +0 -1
  100. package/.output/public/assets/connection-status-DCqqmY_6.js +0 -1
  101. package/.output/public/assets/connector-icon-qGpNsPgH.js +0 -1
  102. package/.output/public/assets/connectors-DxXlc9zF.js +0 -1
  103. package/.output/public/assets/external-link-DC11orFA.js +0 -1
  104. package/.output/public/assets/gaps-CoTA-PzA.js +0 -1
  105. package/.output/public/assets/inbox-CLEXUXCI.js +0 -1
  106. package/.output/public/assets/index-Cndsw84p.js +0 -16
  107. package/.output/public/assets/loader-B4Epchm_.js +0 -1
  108. package/.output/public/assets/loop-form-BvizlZdR.js +0 -1
  109. package/.output/public/assets/loops-Dbe_mf5T.js +0 -1
  110. package/.output/public/assets/new-DPEaXKAY.js +0 -1
  111. package/.output/public/assets/play-DD4xQgvi.js +0 -1
  112. package/.output/public/assets/preload-helper-CfdAYKD7.js +0 -3
  113. package/.output/public/assets/rolldown-runtime-CbXtAM7H.js +0 -1
  114. package/.output/public/assets/runners-BVBggS5B.js +0 -1
  115. package/.output/public/assets/select-Cppu-hti.js +0 -1
  116. package/.output/public/assets/setting-fields-BT1naVJX.js +0 -3
  117. package/.output/public/assets/styles-D3sonCfX.css +0 -2
  118. package/.output/public/assets/switch-hRIbNGOj.js +0 -1
  119. package/.output/public/assets/task-list-CrptnI1N.js +0 -1
  120. package/.output/public/assets/textarea-Fyuzi6Vp.js +0 -1
  121. package/.output/public/assets/trash-2-BXZzBsoV.js +0 -1
  122. package/.output/public/assets/useValueChanged-VVyq4hb9.js +0 -1
  123. package/.output/server/_connectorId-DcSnoRjk.mjs +0 -421
  124. package/.output/server/_loopId-G_TAkYtS.mjs +0 -272
  125. package/.output/server/_ssr/access-B7TcOZ7F.mjs +0 -42
  126. package/.output/server/_ssr/connection-status-BZr3Y-IC.mjs +0 -31
  127. package/.output/server/_ssr/connector-icon-CFrrHjzR.mjs +0 -37
  128. package/.output/server/_ssr/connectors-4yzKb0KG.mjs +0 -106
  129. package/.output/server/_ssr/inbox-C2VBGLa5.mjs +0 -114
  130. package/.output/server/_ssr/loop-form-D-U-kU4k.mjs +0 -413
  131. package/.output/server/_ssr/loops-D0XJ8eBb.mjs +0 -222
  132. package/.output/server/_ssr/new-CajvgIog.mjs +0 -169
  133. package/.output/server/_ssr/runners-B_WuF8UJ.mjs +0 -32
  134. package/.output/server/_ssr/runners-D-0_lfiq.mjs +0 -106
  135. package/.output/server/_ssr/runners-zqF_zbEc.mjs +0 -116
  136. package/.output/server/_ssr/task-list-BnQJ94Dk.mjs +0 -187
  137. package/.output/server/_tanstack-start-manifest_v-BCHAazE8.mjs +0 -129
  138. package/.output/server/_taskId-CIv0ajIx.mjs +0 -256
@@ -1,68 +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 { a as connectorManifest, f as saveAuthorizedConnection, m as takeAuthAttempt, o as connectorRuntime, p as startAuthAttempt } from "./connections-ypex3qUd.mjs";
11
- import { n as callbackUrl, t as appOrigin } from "./callback-D20cVD2w.mjs";
12
- import { t as AGENT_MANIFESTS } from "./agents-CrUmIQpi.mjs";
13
- import { r as bearer } from "./access-B7TcOZ7F.mjs";
14
- import { a as heartbeatRunner, d as runnerIdForToken, i as getRunner, o as isRunnerOnline, s as joinRunner } from "./runners-D-0_lfiq.mjs";
15
- import { f as taskRow, o as jobForTask, p as updateTask, s as jobRequiresHost, t as absorbAgentOutput } from "./tasks-B_QmDFVs.mjs";
16
- 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";
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
+ import { a as callbackUrl, i as bearer, n as appOrigin } from "./access-DTttr563.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";
17
15
  import { n as toast, t as Toaster } from "../_libs/sonner.mjs";
18
16
  import { t as z } from "../_libs/next-themes.mjs";
19
17
  import { randomBytes } from "node:crypto";
20
18
  import { appendFileSync, existsSync } from "node:fs";
21
19
  import { join } from "node:path";
22
20
  import { hostname } from "node:os";
23
- //#region node_modules/.nitro/vite/services/ssr/assets/card-vAtU-sOo.js
24
- var import_react = /* @__PURE__ */ __toESM(require_react());
25
- var import_jsx_runtime = require_jsx_runtime();
21
+ //#region node_modules/.nitro/vite/services/ssr/assets/createSsrRpc-Cukki1qM.js
26
22
  function cn(...inputs) {
27
23
  return twMerge(clsx(inputs));
28
24
  }
29
- 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", {
30
- variants: {
31
- variant: {
32
- default: "bg-primary text-primary-foreground hover:bg-primary/80",
33
- 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",
34
- 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",
35
- ghost: "hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50",
36
- 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",
37
- link: "text-primary underline-offset-4 hover:underline"
38
- },
39
- size: {
40
- default: "h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
41
- 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",
42
- 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",
43
- lg: "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
44
- icon: "size-8",
45
- "icon-xs": "size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3",
46
- "icon-sm": "size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg",
47
- "icon-lg": "size-9"
48
- }
49
- },
50
- defaultVariants: {
51
- variant: "default",
52
- size: "default"
53
- }
54
- });
55
- function Button$1({ className, variant = "default", size = "default", ...props }) {
56
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
57
- "data-slot": "button",
58
- className: cn(buttonVariants({
59
- variant,
60
- size,
61
- className
62
- })),
63
- ...props
64
- });
65
- }
66
25
  var createSsrRpc = (functionId) => {
67
26
  const url = "/_serverFn/" + functionId;
68
27
  const serverFnMeta = { id: functionId };
@@ -75,59 +34,8 @@ var createSsrRpc = (functionId) => {
75
34
  [TSS_SERVER_FUNCTION]: true
76
35
  });
77
36
  };
78
- 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!", {
79
- variants: { variant: {
80
- default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
81
- secondary: "bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80",
82
- 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",
83
- outline: "border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground",
84
- ghost: "hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50",
85
- link: "text-primary underline-offset-4 hover:underline"
86
- } },
87
- defaultVariants: { variant: "default" }
88
- });
89
- function Badge({ className, variant = "default", render, ...props }) {
90
- return useRender({
91
- defaultTagName: "span",
92
- props: mergeProps({ className: cn(badgeVariants({ variant }), className) }, props),
93
- render,
94
- state: {
95
- slot: "badge",
96
- variant
97
- }
98
- });
99
- }
100
- function Card({ className, size = "default", ...props }) {
101
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
102
- "data-slot": "card",
103
- "data-size": size,
104
- 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),
105
- ...props
106
- });
107
- }
108
- function CardHeader({ className, ...props }) {
109
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
110
- "data-slot": "card-header",
111
- 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),
112
- ...props
113
- });
114
- }
115
- function CardTitle({ className, ...props }) {
116
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
117
- "data-slot": "card-title",
118
- className: cn("font-heading text-base leading-snug font-medium group-data-[size=sm]/card:text-sm", className),
119
- ...props
120
- });
121
- }
122
- function CardContent({ className, ...props }) {
123
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
124
- "data-slot": "card-content",
125
- className: cn("px-(--card-spacing)", className),
126
- ...props
127
- });
128
- }
129
37
  //#endregion
130
- //#region node_modules/.nitro/vite/services/ssr/assets/connectors-BVFx4tKA.js
38
+ //#region node_modules/.nitro/vite/services/ssr/assets/connectors-pwWSovhz.js
131
39
  /**
132
40
  * Readiness and connections for every connector in the registry. Manifests are
133
41
  * imported directly by the client, so they are deliberately not sent again.
@@ -138,29 +46,34 @@ var getConnectorOverview = createServerFn({ method: "GET" }).handler(createSsrRp
138
46
  * than in the browser so the page needs no QR library and never learns how the
139
47
  * provider's login works.
140
48
  */
141
- var beginQrLogin = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(createSsrRpc("794964d675b97edaa881ab5cb209225ef0a4507e3d2771d6577658a058e7621f"));
49
+ var beginQrLogin = createServerFn({ method: "POST" }).validator((data) => data).handler(createSsrRpc("794964d675b97edaa881ab5cb209225ef0a4507e3d2771d6577658a058e7621f"));
142
50
  /**
143
51
  * One look at a pending scan. The credential never reaches the browser: a
144
52
  * confirmed login is saved here and the page is told only that it worked.
145
53
  */
146
- var continueQrLogin = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(createSsrRpc("43b97e3644ad73286fe5de3e99134bff099e1a8704f1ae91fe643d419eceba73"));
147
- var disconnectConnection = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(createSsrRpc("f94f0787ce6704ea24f18add66832e0367a0feb9a690d8ad6454abec5e5df193"));
148
- var verifyConnection = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(createSsrRpc("f9ade3084971078ee9f2bc16fcdcdfb5a8d66a0f9b634803bb611287c14e21d7"));
149
- var saveConnectionSettings = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(createSsrRpc("18bae5deb2a19571893f0067e374e537f16d6774f9b4e04f9b7349360f6f8fdc"));
54
+ var continueQrLogin = createServerFn({ method: "POST" }).validator((data) => data).handler(createSsrRpc("43b97e3644ad73286fe5de3e99134bff099e1a8704f1ae91fe643d419eceba73"));
55
+ /**
56
+ * A secret pasted in the UI. Stored here; the page is told only the account
57
+ * label, never the token back.
58
+ */
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"));
150
63
  //#endregion
151
- //#region node_modules/.nitro/vite/services/ssr/assets/tasks-DXjXqmnW.js
64
+ //#region node_modules/.nitro/vite/services/ssr/assets/tasks-CbDJxQNY.js
152
65
  var getInbox = createServerFn({ method: "GET" }).handler(createSsrRpc("48bc32df9d6f69f45df8dfbc6e798e1f99152dbd4b4ed65b149fa2612c65e687"));
153
- var getLoopTasks = createServerFn({ method: "GET" }).inputValidator((data) => data).handler(createSsrRpc("3f938fac3e5ec1631e700a2fdd4790e78abbefeb732ecfbbe6864cc19f0d5aac"));
154
- var getTaskById = createServerFn({ method: "GET" }).inputValidator((data) => data).handler(createSsrRpc("0033ef7c619c027b1428a722dff188d315ed335645afcb1033528630b7c95e88"));
155
- var getTaskLog = createServerFn({ method: "GET" }).inputValidator((data) => data).handler(createSsrRpc("81f567724371f0d6f24e636906e2e9ea5b7384422ac583c86a1050fb838664ce"));
156
- var runPrompt = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(createSsrRpc("bef559b165754b06d6d5cf7c6dccde3d5a6a326462430c621eb131f78665d6e1"));
157
- var runLoopNow = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(createSsrRpc("7258ce5fc6f23acf3aab039ecd4acdb006d42b2ddbf3ab8629b4000e101b51dc"));
158
- 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"));
159
72
  /** Whether the dispatcher is up, and how hard it is allowed to pull. */
160
73
  var getDispatcherState = createServerFn({ method: "GET" }).handler(createSsrRpc("921b049ebafe3c6fb5206fbe3e2100608d3333b944e18385e57da62a28233bc6"));
161
- var pauseDispatcher = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(createSsrRpc("0fa144f67263a4a51c51e8baf134a15b89c0afdf9b92b64660520a5bce247eec"));
74
+ var pauseDispatcher = createServerFn({ method: "POST" }).validator((data) => data).handler(createSsrRpc("0fa144f67263a4a51c51e8baf134a15b89c0afdf9b92b64660520a5bce247eec"));
162
75
  //#endregion
163
- //#region node_modules/.nitro/vite/services/ssr/assets/loops-DOq_YXyk.js
76
+ //#region node_modules/.nitro/vite/services/ssr/assets/loops-Dn50QW1N.js
164
77
  /**
165
78
  * Saving is both making and changing, told apart by whether there is an id.
166
79
  * A loop chosen and not yet saved has none, and nothing on the server knew
@@ -169,15 +82,18 @@ var pauseDispatcher = createServerFn({ method: "POST" }).inputValidator((data) =
169
82
  var edit = (data) => data;
170
83
  var getLoopsPage = createServerFn({ method: "GET" }).handler(createSsrRpc("6d5a90e9ec4ebf70d8e2ac4aff960533a5e68d21b289033cc7e0751d00554c93"));
171
84
  var getLoopReadiness = createServerFn({ method: "GET" }).handler(createSsrRpc("997d2dd097598c44811fe97def167b79d33e4349c51e783058da648e860c7c1f"));
172
- var getLoopById = createServerFn({ method: "GET" }).inputValidator((data) => data).handler(createSsrRpc("81a1af559b2a373901872aa9f9a6546f2f3bda8157d81c21a36ba0d1d337613b"));
173
- var saveLoop = createServerFn({ method: "POST" }).inputValidator(edit).handler(createSsrRpc("e91fc2fd51633c7ca9184fea4beb66fe775ed0b4e6323111a4690da916a78b00"));
174
- var toggleLoop = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(createSsrRpc("3fc8a7de98be979f9820a4f5a0ca2e234ed8a7df8e692abb8c4437765474eca0"));
175
- var removeLoop = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(createSsrRpc("b5a89ca2d07f4ccf2827b5132ca52e7c7a42d584e702b0ab3963b2d5763c35b0"));
176
- var reorderLoop = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(createSsrRpc("7d7d5f80a4f0d2b3bb659d7dc2a91ea5983880648b9d1aa24356e7340505930f"));
177
- var getLoopPollState = createServerFn({ method: "GET" }).inputValidator((data) => data).handler(createSsrRpc("4fd3855e3e9ed954e102071a55b98d91651c9b1e353161f7223768ed6f2b8c28"));
178
- var runLoopBacklog = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(createSsrRpc("65494cbfd06143dbd9a012808fbba21163d9902e387869503fb8b68b0674902b"));
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"));
179
93
  //#endregion
180
- //#region node_modules/.nitro/vite/services/ssr/assets/router-BbrBz36P.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();
181
97
  /**
182
98
  * Loopable's mark: something going round, and the thing it goes round for.
183
99
  *
@@ -220,6 +136,43 @@ function LoopableMark({ className }) {
220
136
  ]
221
137
  });
222
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
+ }
223
176
  /**
224
177
  * The dispatcher can stop between two page loads and nothing on a page would
225
178
  * change, so this asks rather than waiting to be told.
@@ -295,6 +248,28 @@ function DispatcherStatus() {
295
248
  })]
296
249
  });
297
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
+ }
298
273
  var MOBILE_BREAKPOINT = 768;
299
274
  function useIsMobile() {
300
275
  const [isMobile, setIsMobile] = import_react.useState(void 0);
@@ -317,14 +292,6 @@ function Input$1({ className, type, ...props }) {
317
292
  ...props
318
293
  });
319
294
  }
320
- function Separator$1({ className, orientation = "horizontal", ...props }) {
321
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Separator, {
322
- "data-slot": "separator",
323
- orientation,
324
- className: cn("shrink-0 bg-border data-horizontal:h-px data-horizontal:w-full data-vertical:w-px data-vertical:self-stretch", className),
325
- ...props
326
- });
327
- }
328
295
  function Sheet({ ...props }) {
329
296
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogRoot, {
330
297
  "data-slot": "sheet",
@@ -777,7 +744,7 @@ var Toaster$1 = ({ ...props }) => {
777
744
  ...props
778
745
  });
779
746
  };
780
- var styles_default = "/assets/styles-D3sonCfX.css";
747
+ var styles_default = "/assets/styles-B9B98zLk.css";
781
748
  var Route$18 = createRootRoute({
782
749
  head: () => ({
783
750
  meta: [
@@ -809,7 +776,7 @@ var Route$18 = createRootRoute({
809
776
  });
810
777
  function AppLayout() {
811
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", {
812
- className: "flex-1 p-6",
779
+ className: "flex-1 px-6 py-8 md:px-10",
813
780
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
814
781
  className: "mx-auto w-full max-w-4xl",
815
782
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Outlet, {})
@@ -829,326 +796,16 @@ function RootDocument({ children }) {
829
796
  var Route$17 = createFileRoute("/")({ beforeLoad: () => {
830
797
  throw redirect({ to: "/connectors" });
831
798
  } });
832
- function Label({ className, ...props }) {
833
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", {
834
- "data-slot": "label",
835
- 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),
836
- ...props
837
- });
838
- }
839
- var Select$1 = SelectRoot;
840
- function SelectValue({ className, ...props }) {
841
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectValue$1, {
842
- "data-slot": "select-value",
843
- className: cn("flex flex-1 text-left", className),
844
- ...props
845
- });
846
- }
847
- function SelectTrigger({ className, size = "default", children, ...props }) {
848
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectTrigger$1, {
849
- "data-slot": "select-trigger",
850
- "data-size": size,
851
- 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),
852
- ...props,
853
- 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" }) })]
854
- });
855
- }
856
- function SelectContent({ className, children, side = "bottom", sideOffset = 4, align = "center", alignOffset = 0, alignItemWithTrigger = true, ...props }) {
857
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectPortal, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectPositioner, {
858
- side,
859
- sideOffset,
860
- align,
861
- alignOffset,
862
- alignItemWithTrigger,
863
- className: "isolate z-50",
864
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectPopup, {
865
- "data-slot": "select-content",
866
- "data-align-trigger": alignItemWithTrigger,
867
- 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),
868
- ...props,
869
- children: [
870
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectScrollUpButton, {}),
871
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectList, { children }),
872
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectScrollDownButton, {})
873
- ]
874
- })
875
- }) });
876
- }
877
- function SelectItem({ className, children, ...props }) {
878
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectItem$1, {
879
- "data-slot": "select-item",
880
- 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),
881
- ...props,
882
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItemText, {
883
- className: "flex flex-1 shrink-0 gap-2 whitespace-nowrap",
884
- children
885
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItemIndicator, {
886
- render: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "pointer-events-none absolute right-2 flex size-4 items-center justify-center" }),
887
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Check, { className: "pointer-events-none" })
888
- })]
889
- });
890
- }
891
- function SelectScrollUpButton({ className, ...props }) {
892
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectScrollUpArrow, {
893
- "data-slot": "select-scroll-up-button",
894
- 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),
895
- ...props,
896
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronUp, {})
897
- });
898
- }
899
- function SelectScrollDownButton({ className, ...props }) {
900
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectScrollDownArrow, {
901
- "data-slot": "select-scroll-down-button",
902
- 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),
903
- ...props,
904
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronDown, {})
905
- });
906
- }
907
- /**
908
- * States still in flight. Used by the pages to know when
909
- * to keep looking, and by the reaper to find runs a dead process abandoned.
910
- */
911
- var TASK_ACTIVE_STATES = [
912
- "queued",
913
- "preparing",
914
- "awaiting_agent",
915
- "applying"
916
- ];
917
- function isTaskActive(state) {
918
- return TASK_ACTIVE_STATES.includes(state);
919
- }
920
- /**
921
- * The work happens in the dispatcher, so the page has to go and look. Polling only
922
- * while something is actually running keeps an idle inbox quiet.
923
- */
924
- function useLiveTasks(states, everyMs = 2e3) {
925
- useLiveRefresh(everyMs, states.some((task) => isTaskActive(task.state)));
926
- }
927
- /** Runner inventory changes without this page doing anything, so it looks again. */
928
- function useLiveRefresh(everyMs = 1e4, enabled = true) {
929
- const router = useRouter();
930
- (0, import_react.useEffect)(() => {
931
- if (!enabled) return;
932
- const timer = setInterval(() => void router.invalidate(), everyMs);
933
- return () => clearInterval(timer);
934
- }, [
935
- enabled,
936
- everyMs,
937
- router
938
- ]);
939
- }
940
799
  var getAgentsPage = createServerFn({ method: "GET" }).handler(createSsrRpc("bde588e9b0b81a4ee619a63872d6b8b76e22db006c7a3c4351bcd56e5f76bbed"));
941
- var saveAgent = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(createSsrRpc("80883b912a5a0aba2aa583b505e4e289d765a3f7fbb793a97692f439e7f960ba"));
942
- var chooseDefaultAgent = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(createSsrRpc("143ea51f7421d9eebec9ad31c2df8eed5aa2e5d8035473dbfad39913fd7d54c0"));
943
- var runAgentTest = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(createSsrRpc("b0878f841b35f46b5f2dc9cbd71c5be5895ae1b8903b3b24c24dfcf86291d3e4"));
944
- var ICONS = {
945
- Terminal,
946
- MousePointerClick,
947
- Bot
948
- };
949
- var MODE_LABELS = {
950
- read_only: "Read only",
951
- workspace_write: "Can write in the workspace"
952
- };
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");
953
804
  var Route$16 = createFileRoute("/agents/")({
954
805
  loader: () => getAgentsPage(),
955
- component: AgentsPage
806
+ component: lazyRouteComponent($$splitComponentImporter$8, "component")
956
807
  });
957
- function AgentsPage() {
958
- const { agents } = Route$16.useLoaderData();
959
- useLiveRefresh();
960
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
961
- className: "flex flex-col gap-6",
962
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", {
963
- className: "text-2xl font-semibold tracking-tight",
964
- children: "Agents"
965
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
966
- className: "mt-1 text-sm text-muted-foreground",
967
- children: "The coding CLIs Loopable knows how to run. Presence comes from runners. Settings here are how every runner invokes that CLI."
968
- })] }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
969
- className: "flex flex-col gap-2",
970
- children: AGENT_MANIFESTS.map((manifest) => {
971
- const view = agents.find((agent) => agent.agentId === manifest.id);
972
- return view ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AgentRow, {
973
- manifest,
974
- view
975
- }, manifest.id) : null;
976
- })
977
- })]
978
- });
979
- }
980
- function presence(view) {
981
- const signedIn = view.onRunners.filter((runner) => runner.signedIn);
982
- const online = signedIn.filter((runner) => runner.status === "online");
983
- if (online.length > 0) return online.length === 1 ? `Signed in on ${online[0].name}` : `Signed in on ${online.length} runners`;
984
- if (signedIn.length > 0) return "Signed in, but every runner is offline";
985
- if (view.onRunners.length > 0) return "Installed, not signed in";
986
- return "Not on any runner";
987
- }
988
- function AgentRow({ manifest, view }) {
989
- const router = useRouter();
990
- const Icon = ICONS[manifest.icon] ?? Bot;
991
- const [open, setOpen] = (0, import_react.useState)(view.isDefault && !view.defaultIsImplicit);
992
- const [mode, setMode] = (0, import_react.useState)(view.settings.permissionMode);
993
- const [model, setModel] = (0, import_react.useState)(view.settings.model ?? "");
994
- const [seconds, setSeconds] = (0, import_react.useState)(String(Math.round(view.settings.timeoutMs / 1e3)));
995
- const [busy, setBusy] = (0, import_react.useState)(null);
996
- const [testOutput, setTestOutput] = (0, import_react.useState)(null);
997
- const online = view.onRunners.some((runner) => runner.signedIn && runner.status === "online");
998
- const act = async (kind, run) => {
999
- setBusy(kind);
1000
- try {
1001
- await run();
1002
- } catch (error) {
1003
- toast.error(error instanceof Error ? error.message : String(error));
1004
- } finally {
1005
- setBusy(null);
1006
- }
1007
- };
1008
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Card, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1009
- className: "flex items-start gap-3 p-4",
1010
- children: [
1011
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
1012
- className: cn("mt-0.5 flex size-8 shrink-0 items-center justify-center rounded-md", online ? manifest.accent : "bg-muted text-muted-foreground"),
1013
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { className: "size-4" })
1014
- }),
1015
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", {
1016
- type: "button",
1017
- className: "min-w-0 flex-1 text-left",
1018
- onClick: () => setOpen((value) => !value),
1019
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1020
- className: "flex flex-wrap items-center gap-2",
1021
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
1022
- className: "font-medium",
1023
- children: manifest.name
1024
- }), view.isDefault ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Badge, { children: view.defaultIsImplicit ? "Default (only one signed in)" : "Default" }) : null]
1025
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
1026
- className: "mt-0.5 text-sm text-muted-foreground",
1027
- children: presence(view)
1028
- })]
1029
- }),
1030
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1031
- className: "flex shrink-0 items-center gap-1",
1032
- children: [!view.isDefault ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
1033
- variant: "ghost",
1034
- size: "sm",
1035
- disabled: busy !== null,
1036
- onClick: () => act("default", async () => {
1037
- await chooseDefaultAgent({ data: { agentId: manifest.id } });
1038
- await router.invalidate();
1039
- toast.success(`${manifest.name} will run the loops`);
1040
- }),
1041
- children: "Default"
1042
- }) : null, /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
1043
- variant: "ghost",
1044
- size: "icon-sm",
1045
- "aria-expanded": open,
1046
- "aria-label": "Settings",
1047
- onClick: () => setOpen((value) => !value),
1048
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronDown, { className: cn("size-4 transition-transform", open && "rotate-180") })
1049
- })]
1050
- })
1051
- ]
1052
- }), open ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(CardContent, {
1053
- className: "flex flex-col gap-4 border-t pt-4",
1054
- children: [
1055
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
1056
- className: "text-sm text-muted-foreground",
1057
- children: manifest.tagline
1058
- }),
1059
- !online ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
1060
- className: "text-sm text-muted-foreground",
1061
- children: manifest.installHint
1062
- }) : null,
1063
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1064
- className: "grid gap-4 sm:grid-cols-3",
1065
- children: [
1066
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1067
- className: "flex flex-col gap-2",
1068
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Label, {
1069
- htmlFor: `${manifest.id}-mode`,
1070
- children: "Permission"
1071
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Select$1, {
1072
- value: mode,
1073
- onValueChange: (value) => setMode(value),
1074
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectTrigger, {
1075
- id: `${manifest.id}-mode`,
1076
- className: "w-full",
1077
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectValue, { children: (value) => MODE_LABELS[value] ?? value })
1078
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectContent, { children: manifest.permissionModes.map((value) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
1079
- value,
1080
- children: MODE_LABELS[value]
1081
- }, value)) })]
1082
- })]
1083
- }),
1084
- manifest.supportsModel ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1085
- className: "flex flex-col gap-2",
1086
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Label, {
1087
- htmlFor: `${manifest.id}-model`,
1088
- children: "Model"
1089
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Input$1, {
1090
- id: `${manifest.id}-model`,
1091
- value: model,
1092
- placeholder: manifest.modelPlaceholder,
1093
- onChange: (event) => setModel(event.target.value)
1094
- })]
1095
- }) : null,
1096
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1097
- className: "flex flex-col gap-2",
1098
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Label, {
1099
- htmlFor: `${manifest.id}-timeout`,
1100
- children: "Timeout (seconds)"
1101
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Input$1, {
1102
- id: `${manifest.id}-timeout`,
1103
- value: seconds,
1104
- inputMode: "numeric",
1105
- onChange: (event) => setSeconds(event.target.value.replace(/\D/g, ""))
1106
- })]
1107
- })
1108
- ]
1109
- }),
1110
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("pre", {
1111
- className: "overflow-x-auto rounded-md bg-muted p-2.5 text-xs",
1112
- children: view.commandPreview
1113
- }),
1114
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1115
- className: "flex flex-wrap items-center gap-2",
1116
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
1117
- size: "sm",
1118
- disabled: busy !== null,
1119
- onClick: () => act("save", async () => {
1120
- await saveAgent({ data: {
1121
- agentId: manifest.id,
1122
- permissionMode: mode,
1123
- model: model.trim() ? model.trim() : null,
1124
- timeoutMs: Number(seconds) * 1e3
1125
- } });
1126
- await router.invalidate();
1127
- toast.success(`Saved ${manifest.name} settings`);
1128
- }),
1129
- children: busy === "save" ? "Saving..." : "Save"
1130
- }), view.installed ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button$1, {
1131
- variant: "outline",
1132
- size: "sm",
1133
- disabled: busy !== null,
1134
- onClick: () => act("test", async () => {
1135
- setTestOutput(null);
1136
- const result = await runAgentTest({ data: { agentId: manifest.id } });
1137
- 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());
1138
- if (result.passed) toast.success(`${manifest.name} is working`);
1139
- else toast.error(`${manifest.name} did not pass`);
1140
- }),
1141
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CirclePlay, {}), busy === "test" ? "Running..." : "Test"]
1142
- }) : null]
1143
- }),
1144
- testOutput ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
1145
- className: "whitespace-pre-wrap text-xs text-muted-foreground",
1146
- children: testOutput
1147
- }) : null
1148
- ]
1149
- }) : null] });
1150
- }
1151
- var $$splitComponentImporter$7 = () => import("./connectors-4yzKb0KG.mjs");
808
+ var $$splitComponentImporter$7 = () => import("./connectors-BfS5tQKr.mjs");
1152
809
  var Route$15 = createFileRoute("/connectors/")({
1153
810
  validateSearch: (search) => {
1154
811
  const result = {};
@@ -1159,22 +816,28 @@ var Route$15 = createFileRoute("/connectors/")({
1159
816
  loader: () => getConnectorOverview(),
1160
817
  component: lazyRouteComponent($$splitComponentImporter$7, "component")
1161
818
  });
1162
- var $$splitComponentImporter$6 = () => import("../_connectorId-DcSnoRjk.mjs");
819
+ var $$splitComponentImporter$6 = () => import("../_connectorId-COuQN02a.mjs");
1163
820
  var Route$14 = createFileRoute("/connectors/$connectorId")({
1164
821
  loader: async ({ params }) => {
1165
822
  if (!connectorManifest(params.connectorId)) throw notFound();
1166
- 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);
1167
825
  if (!state) throw notFound();
1168
- return state;
826
+ const loops = page.loops.filter((loop) => loop.connectorId === params.connectorId || loop.actionConnectorId === params.connectorId);
827
+ return {
828
+ ...state,
829
+ loops
830
+ };
1169
831
  },
1170
832
  component: lazyRouteComponent($$splitComponentImporter$6, "component")
1171
833
  });
1172
- var $$splitComponentImporter$5 = () => import("./inbox-C2VBGLa5.mjs");
834
+ var $$splitComponentImporter$5 = () => import("./inbox-D1ef761v.mjs");
1173
835
  var Route$13 = createFileRoute("/inbox/")({
1174
836
  loader: () => getInbox(),
1175
837
  component: lazyRouteComponent($$splitComponentImporter$5, "component")
1176
838
  });
1177
- var $$splitComponentImporter$4 = () => import("../_taskId-CIv0ajIx.mjs");
839
+ /** Ask an agent something once. The reply stays in Inbox; nothing is written back. */
840
+ var $$splitComponentImporter$4 = () => import("../_taskId-D3KkNJW1.mjs");
1178
841
  var Route$12 = createFileRoute("/inbox/$taskId")({
1179
842
  loader: async ({ params }) => {
1180
843
  const task = await getTaskById({ data: { id: params.taskId } });
@@ -1186,6 +849,7 @@ var Route$12 = createFileRoute("/inbox/$taskId")({
1186
849
  },
1187
850
  component: lazyRouteComponent($$splitComponentImporter$4, "component")
1188
851
  });
852
+ /** A titled block that opens and closes, for what is only sometimes worth reading. */
1189
853
  /**
1190
854
  * The findings that go on the lines rather than in the body. Shown separately
1191
855
  * because that is what they are: on GitHub they will be spread across the
@@ -1201,19 +865,16 @@ var Route$12 = createFileRoute("/inbox/$taskId")({
1201
865
  * sees it when it next renews its claim, which is why the button says it has
1202
866
  * been asked for rather than pretending it is already done.
1203
867
  */
1204
- var $$splitComponentImporter$3 = () => import("./loops-D0XJ8eBb.mjs");
868
+ var $$splitComponentImporter$3 = () => import("./loops-DHPjzG-t.mjs");
1205
869
  var Route$11 = createFileRoute("/loops/")({
1206
870
  loader: () => getLoopsPage(),
1207
871
  component: lazyRouteComponent($$splitComponentImporter$3, "component")
1208
872
  });
1209
- /** Says what a loop still needs, without standing in the way of writing one. */
1210
- /**
1211
- * Where this loop writes, in its own words rather than the workflow's. A loop
1212
- * may have been pointed somewhere else since it was made, and the card is the
1213
- * one place someone scanning a list would notice.
1214
- */
1215
- var $$splitComponentImporter$2 = () => import("../_loopId-G_TAkYtS.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");
1216
876
  var Route$10 = createFileRoute("/loops/$loopId")({
877
+ validateSearch: (search) => search.tab === "tasks" ? { tab: "tasks" } : {},
1217
878
  loader: async ({ params }) => {
1218
879
  const loop = await getLoopById({ data: { id: params.loopId } });
1219
880
  if (!loop) throw notFound();
@@ -1226,50 +887,53 @@ var Route$10 = createFileRoute("/loops/$loopId")({
1226
887
  },
1227
888
  component: lazyRouteComponent($$splitComponentImporter$2, "component")
1228
889
  });
890
+ /** One line under the title: where it answers, with what, and when it last looked. */
1229
891
  /**
1230
- * Whether the loop is really watching is the one thing a page cannot work out
1231
- * for itself, and the thing a person most wants to know after turning one on.
1232
- */
1233
- /**
1234
- * A loop that runs by itself still has to be shaped, and waiting for a real
1235
- * signal to arrive is a slow way to do it. Dry run means that shaping leaves
1236
- * 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.
1237
895
  */
1238
- var $$splitComponentImporter$1 = () => import("./new-CajvgIog.mjs");
896
+ /** Run the loop on one link, by hand, without waiting for something to arrive. */
897
+ var $$splitComponentImporter$1 = () => import("./new-ChKBdcXJ.mjs");
1239
898
  var Route$9 = createFileRoute("/loops/new")({
1240
899
  validateSearch: (search) => {
1241
900
  const result = {};
1242
901
  if (typeof search.connector === "string") result.connector = search.connector;
1243
902
  if (typeof search.workflow === "string") result.workflow = search.workflow;
903
+ if (search.custom === true || search.custom === "true" || search.custom === 1) result.custom = true;
1244
904
  return result;
1245
905
  },
1246
906
  loader: async () => ({ loops: (await getLoopsPage()).loops }),
1247
907
  component: lazyRouteComponent($$splitComponentImporter$1, "component")
1248
908
  });
909
+ /** The trigger a custom loop starts on before anything is picked. */
1249
910
  /**
1250
- * The second step, and the one that makes a loop.
1251
- *
1252
- * Nothing has been written down yet: the draft is worked out here from the
1253
- * workflow the URL names, and the loop begins when the form is saved. Picking
1254
- * a workflow used to create it, which meant a look around this page left a
1255
- * 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.
1256
918
  */
919
+ /** How many loops already use a workflow, said as a sentence rather than a count. */
1257
920
  /**
1258
- * There is no blank loop to write any more. Every loop is one of the
1259
- * workflows a connector offers, already knowing its own job, so choosing is
1260
- * 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.
1261
924
  */
1262
925
  var Route$8 = createFileRoute("/machines/")({ beforeLoad: () => {
1263
926
  throw redirect({ to: "/runners" });
1264
927
  } });
1265
928
  var getRunnersPage = createServerFn({ method: "GET" }).handler(createSsrRpc("45dbbb612763589268984d236edcd17f5868ecf716df9625bfa46e7fec691f4e"));
1266
929
  var rotateRunnerJoinToken = createServerFn({ method: "POST" }).handler(createSsrRpc("b4f7a66e1bb69cc14ab84d8f5dc7f85119c9a9b1d065a6b4e51dd0ba84c3f60c"));
1267
- var removeRunner = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(createSsrRpc("611491b32d9583ccaae637ddf9fb2f842ff75994f50513ecd8273e305e889eb8"));
1268
- var $$splitComponentImporter = () => import("./runners-zqF_zbEc.mjs");
930
+ var removeRunner = createServerFn({ method: "POST" }).validator((data) => data).handler(createSsrRpc("611491b32d9583ccaae637ddf9fb2f842ff75994f50513ecd8273e305e889eb8"));
931
+ var $$splitComponentImporter = () => import("./runners-CrznFPHt.mjs");
1269
932
  var Route$7 = createFileRoute("/runners/")({
1270
933
  loader: () => getRunnersPage(),
1271
934
  component: lazyRouteComponent($$splitComponentImporter, "component")
1272
935
  });
936
+ /** Which agents this runner has, in a few words. */
1273
937
  async function claimAgentJob(runnerId) {
1274
938
  const row = await getRunner(runnerId);
1275
939
  if (!row || !isRunnerOnline(row)) return null;
@@ -1285,6 +949,14 @@ async function claimAgentJob(runnerId) {
1285
949
  await updateTask(task.id, { leaseUntil: null });
1286
950
  return null;
1287
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
+ }
1288
960
  return job;
1289
961
  }
1290
962
  return null;
@@ -1328,8 +1000,8 @@ async function completeAgentJob(taskId, runnerId, result) {
1328
1000
  }
1329
1001
  await absorbAgentOutput(taskId, result.output);
1330
1002
  }
1331
- function touchRunner(runnerId, inventory) {
1332
- return heartbeatRunner(runnerId, inventory);
1003
+ function touchRunner(runnerId, inventory, build = {}) {
1004
+ return heartbeatRunner(runnerId, inventory, build);
1333
1005
  }
1334
1006
  var Route$6 = createFileRoute("/api/runners/claim")({ server: { handlers: { POST: async ({ request }) => {
1335
1007
  const runnerId = await runnerIdForToken(bearer(request));
@@ -1340,7 +1012,11 @@ var Route$6 = createFileRoute("/api/runners/claim")({ server: { handlers: { POST
1340
1012
  var Route$5 = createFileRoute("/api/runners/heartbeat")({ server: { handlers: { POST: async ({ request }) => {
1341
1013
  const runnerId = await runnerIdForToken(bearer(request));
1342
1014
  if (!runnerId) return Response.json({ error: "Unknown runner." }, { status: 401 });
1343
- 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
+ });
1344
1020
  return Response.json(view);
1345
1021
  } } } });
1346
1022
  var Route$4 = createFileRoute("/api/runners/hello")({ server: { handlers: { POST: async ({ request }) => {
@@ -1349,7 +1025,9 @@ var Route$4 = createFileRoute("/api/runners/hello")({ server: { handlers: { POST
1349
1025
  const result = await joinRunner({
1350
1026
  joinToken: bearer(request),
1351
1027
  hostname: body.hostname ?? "unknown",
1352
- inventory: body.inventory ?? []
1028
+ inventory: body.inventory ?? [],
1029
+ version: body.version,
1030
+ protocol: body.protocol
1353
1031
  });
1354
1032
  return Response.json(result);
1355
1033
  } catch (error) {
@@ -1551,4 +1229,4 @@ function getRouter() {
1551
1229
  });
1552
1230
  }
1553
1231
  //#endregion
1554
- export { stopTask as A, CardHeader as B, removeLoop as C, toggleLoop as D, saveLoop as E, verifyConnection as F, cn as H, Badge as I, Button$1 as L, continueQrLogin as M, disconnectConnection as N, runLoopNow as O, saveConnectionSettings as P, Card as R, Input$1 as S, runLoopBacklog as T, CardTitle as V, 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, beginQrLogin as j, runPrompt 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, reorderLoop as w, Separator$1 as x, SelectValue as y, CardContent 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 };