loopable-cli 0.1.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 (133) hide show
  1. package/.output/nitro.json +17 -0
  2. package/.output/public/apple-touch-icon.png +0 -0
  3. package/.output/public/assets/_connectorId-BWjYn5tm.js +1 -0
  4. package/.output/public/assets/_loopId-Ds4RkyEc.js +1 -0
  5. package/.output/public/assets/_taskId-BOzICXDf.js +1 -0
  6. package/.output/public/assets/alert-Bn9-Zm0l.js +1 -0
  7. package/.output/public/assets/arrow-left-BF9juV9T.js +1 -0
  8. package/.output/public/assets/arrow-right-CzqvnOY1.js +1 -0
  9. package/.output/public/assets/button-HCY6DCY-.js +1 -0
  10. package/.output/public/assets/connection-status-CYZB8kLy.js +1 -0
  11. package/.output/public/assets/connector-icon-BZ_MUJV_.js +1 -0
  12. package/.output/public/assets/connectors-BvzttfSu.js +1 -0
  13. package/.output/public/assets/connectors-DdJNwRN_.js +1 -0
  14. package/.output/public/assets/external-link-D46X-bXa.js +1 -0
  15. package/.output/public/assets/gaps-D-I6xiDc.js +1 -0
  16. package/.output/public/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
  17. package/.output/public/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
  18. package/.output/public/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
  19. package/.output/public/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
  20. package/.output/public/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
  21. package/.output/public/assets/inbox-BUUu9xxE.js +1 -0
  22. package/.output/public/assets/index-ztzex0FN.js +10 -0
  23. package/.output/public/assets/loader-CcBl2u8m.js +1 -0
  24. package/.output/public/assets/loop-form-dB3CLcZb.js +1 -0
  25. package/.output/public/assets/loops-DRunQrxL.js +1 -0
  26. package/.output/public/assets/loops-oSdqMd29.js +1 -0
  27. package/.output/public/assets/manifests-DNMqf7Vi.js +7 -0
  28. package/.output/public/assets/new-uyFxVjuB.js +1 -0
  29. package/.output/public/assets/preload-helper-ORbuV86A.js +3 -0
  30. package/.output/public/assets/runners-DSU3r5Ww.js +1 -0
  31. package/.output/public/assets/setting-fields-ixkpdBb-.js +3 -0
  32. package/.output/public/assets/styles-DZL5V1o7.css +2 -0
  33. package/.output/public/assets/switch-d5ummmHY.js +1 -0
  34. package/.output/public/assets/task-list-BpOStt9v.js +1 -0
  35. package/.output/public/assets/tasks-B8MsKgic.js +1 -0
  36. package/.output/public/assets/trash-2-D6u6NpzO.js +1 -0
  37. package/.output/public/favicon.svg +9 -0
  38. package/.output/server/_chunks/ssr-renderer.mjs +26 -0
  39. package/.output/server/_connectorId-8nb5yOzG.mjs +421 -0
  40. package/.output/server/_libs/@base-ui/react+[...].mjs +12841 -0
  41. package/.output/server/_libs/@tanstack/react-router+[...].mjs +14349 -0
  42. package/.output/server/_libs/class-variance-authority+clsx.mjs +69 -0
  43. package/.output/server/_libs/dijkstrajs.mjs +118 -0
  44. package/.output/server/_libs/drizzle-orm.mjs +4349 -0
  45. package/.output/server/_libs/h3+rou3+srvx.mjs +1361 -0
  46. package/.output/server/_libs/lucide-react.mjs +911 -0
  47. package/.output/server/_libs/next-themes.mjs +53 -0
  48. package/.output/server/_libs/pngjs.mjs +1680 -0
  49. package/.output/server/_libs/qrcode.mjs +2583 -0
  50. package/.output/server/_libs/sonner.mjs +955 -0
  51. package/.output/server/_libs/tailwind-merge.mjs +3380 -0
  52. package/.output/server/_libs/tanstack__history.mjs +341 -0
  53. package/.output/server/_libs/ufo.mjs +64 -0
  54. package/.output/server/_loopId-BsUL1SeL.mjs +272 -0
  55. package/.output/server/_runtime.mjs +35 -0
  56. package/.output/server/_ssr/access-zIl15o8y.mjs +42 -0
  57. package/.output/server/_ssr/agents-Btql102J.mjs +657 -0
  58. package/.output/server/_ssr/agents-DAOZPgO2.mjs +76 -0
  59. package/.output/server/_ssr/alert-NEiOCTOl.mjs +37 -0
  60. package/.output/server/_ssr/callback-CUINvl7M.mjs +62 -0
  61. package/.output/server/_ssr/client-CKkOfh-s.mjs +335 -0
  62. package/.output/server/_ssr/connection-status-Bi5peUAk.mjs +31 -0
  63. package/.output/server/_ssr/connections-qnw2GVlP.mjs +2542 -0
  64. package/.output/server/_ssr/connector-icon-DZXQLLFo.mjs +37 -0
  65. package/.output/server/_ssr/connectors-BHNwuIMj.mjs +106 -0
  66. package/.output/server/_ssr/connectors-C28g0i4c.mjs +83 -0
  67. package/.output/server/_ssr/createServerRpc-DPDrnFdH.mjs +12 -0
  68. package/.output/server/_ssr/empty-plugin-adapters-BJ4m9L8g.mjs +5 -0
  69. package/.output/server/_ssr/gaps-RAFSXLZf.mjs +53 -0
  70. package/.output/server/_ssr/inbox-k5zHergU.mjs +35 -0
  71. package/.output/server/_ssr/loop-form-DlObSewZ.mjs +412 -0
  72. package/.output/server/_ssr/loops-APFcP0y6.mjs +311 -0
  73. package/.output/server/_ssr/loops-xt2Bn7_q.mjs +222 -0
  74. package/.output/server/_ssr/new-DfKyd2Wa.mjs +169 -0
  75. package/.output/server/_ssr/router-3WmpE5b_.mjs +1553 -0
  76. package/.output/server/_ssr/runners-By-mwc3F.mjs +32 -0
  77. package/.output/server/_ssr/runners-DI_NxpHC.mjs +116 -0
  78. package/.output/server/_ssr/runners-DtXs4Q8a.mjs +106 -0
  79. package/.output/server/_ssr/secrets-Hqti-niS.mjs +199 -0
  80. package/.output/server/_ssr/setting-fields-8vKFj4df.mjs +92 -0
  81. package/.output/server/_ssr/ssr.mjs +6605 -0
  82. package/.output/server/_ssr/start-Bdc2GJUI.mjs +51 -0
  83. package/.output/server/_ssr/switch-ReNglgH6.mjs +18 -0
  84. package/.output/server/_ssr/task-list-CUITIqkb.mjs +186 -0
  85. package/.output/server/_ssr/tasks-SAjNovX6.mjs +110 -0
  86. package/.output/server/_ssr/tasks-vcoodp_D.mjs +533 -0
  87. package/.output/server/_tanstack-start-manifest_v-C0cP5F0M.mjs +125 -0
  88. package/.output/server/_taskId-CXS3lod0.mjs +249 -0
  89. package/.output/server/index.mjs +541 -0
  90. package/README.md +81 -0
  91. package/dist/cli.mjs +165 -0
  92. package/dist/dispatcher.mjs +9871 -0
  93. package/dist/runner.mjs +657 -0
  94. package/drizzle/0000_cold_demogoblin.sql +29 -0
  95. package/drizzle/0001_fancy_blonde_phantom.sql +13 -0
  96. package/drizzle/0002_spooky_smiling_tiger.sql +16 -0
  97. package/drizzle/0003_small_golden_guardian.sql +25 -0
  98. package/drizzle/0004_wonderful_thunderbolts.sql +39 -0
  99. package/drizzle/0005_named_workflows.sql +7 -0
  100. package/drizzle/0006_signals.sql +18 -0
  101. package/drizzle/0007_review_comments.sql +1 -0
  102. package/drizzle/0008_held_signals.sql +1 -0
  103. package/drizzle/0009_work_item_ref.sql +58 -0
  104. package/drizzle/0010_signal_payload.sql +2 -0
  105. package/drizzle/0011_rule_poll_cursor.sql +1 -0
  106. package/drizzle/0012_loops.sql +63 -0
  107. package/drizzle/0013_loop_owns_action_and_prompt.sql +154 -0
  108. package/drizzle/0014_loop_poll_every.sql +4 -0
  109. package/drizzle/0015_machines.sql +10 -0
  110. package/drizzle/0016_task_machine.sql +1 -0
  111. package/drizzle/0017_loop_machine.sql +1 -0
  112. package/drizzle/0018_runners.sql +1 -0
  113. package/drizzle/0019_task_runner.sql +1 -0
  114. package/drizzle/0020_loop_runner.sql +1 -0
  115. package/drizzle/0021_drop_runner_kind.sql +1 -0
  116. package/drizzle/0022_drop_loop_runner.sql +1 -0
  117. package/drizzle/0023_shared_host.sql +6 -0
  118. package/drizzle/meta/0000_snapshot.json +206 -0
  119. package/drizzle/meta/0001_snapshot.json +284 -0
  120. package/drizzle/meta/0002_snapshot.json +390 -0
  121. package/drizzle/meta/0003_snapshot.json +566 -0
  122. package/drizzle/meta/0004_snapshot.json +633 -0
  123. package/drizzle/meta/0005_snapshot.json +626 -0
  124. package/drizzle/meta/0006_snapshot.json +752 -0
  125. package/drizzle/meta/0007_snapshot.json +759 -0
  126. package/drizzle/meta/0008_snapshot.json +766 -0
  127. package/drizzle/meta/0009_snapshot.json +752 -0
  128. package/drizzle/meta/0010_snapshot.json +766 -0
  129. package/drizzle/meta/0011_snapshot.json +773 -0
  130. package/drizzle/meta/0012_snapshot.json +773 -0
  131. package/drizzle/meta/0013_snapshot.json +817 -0
  132. package/drizzle/meta/_journal.json +174 -0
  133. package/package.json +88 -0
@@ -0,0 +1,1553 @@
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 Button, 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 useRender } 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";
5
+ import { n as clsx, t as cva } from "../_libs/class-variance-authority+clsx.mjs";
6
+ import { t as twMerge } from "../_libs/tailwind-merge.mjs";
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-CKkOfh-s.mjs";
9
+ import { n as isHosted } from "./secrets-Hqti-niS.mjs";
10
+ import { a as connectorManifest, f as saveAuthorizedConnection, m as takeAuthAttempt, o as connectorRuntime, p as startAuthAttempt } from "./connections-qnw2GVlP.mjs";
11
+ import { n as callbackUrl, t as appOrigin } from "./callback-CUINvl7M.mjs";
12
+ import { t as AGENT_MANIFESTS } from "./agents-Btql102J.mjs";
13
+ import { r as bearer } from "./access-zIl15o8y.mjs";
14
+ import { a as heartbeatRunner, d as runnerIdForToken, i as getRunner, o as isRunnerOnline, s as joinRunner } from "./runners-DtXs4Q8a.mjs";
15
+ import { a as jobForTask, d as updateTask, o as jobRequiresHost, t as absorbAgentOutput, u as taskRow } from "./tasks-vcoodp_D.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";
17
+ import { n as toast, t as Toaster } from "../_libs/sonner.mjs";
18
+ import { t as z } from "../_libs/next-themes.mjs";
19
+ import { randomBytes } from "node:crypto";
20
+ import { appendFileSync, existsSync } from "node:fs";
21
+ import { join } from "node:path";
22
+ import { hostname } from "node:os";
23
+ //#region node_modules/.nitro/vite/services/ssr/assets/card-IpIuKJC_.js
24
+ var import_react = /* @__PURE__ */ __toESM(require_react());
25
+ var import_jsx_runtime = require_jsx_runtime();
26
+ function cn(...inputs) {
27
+ return twMerge(clsx(inputs));
28
+ }
29
+ var createSsrRpc = (functionId) => {
30
+ const url = "/_serverFn/" + functionId;
31
+ const serverFnMeta = { id: functionId };
32
+ const fn = async (...args) => {
33
+ return (await getServerFnById(functionId, { origin: "server" }))(...args);
34
+ };
35
+ return Object.assign(fn, {
36
+ url,
37
+ serverFnMeta,
38
+ [TSS_SERVER_FUNCTION]: true
39
+ });
40
+ };
41
+ 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!", {
42
+ variants: { variant: {
43
+ default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
44
+ secondary: "bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80",
45
+ 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",
46
+ outline: "border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground",
47
+ ghost: "hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50",
48
+ link: "text-primary underline-offset-4 hover:underline"
49
+ } },
50
+ defaultVariants: { variant: "default" }
51
+ });
52
+ function Badge({ className, variant = "default", render, ...props }) {
53
+ return useRender({
54
+ defaultTagName: "span",
55
+ props: mergeProps({ className: cn(badgeVariants({ variant }), className) }, props),
56
+ render,
57
+ state: {
58
+ slot: "badge",
59
+ variant
60
+ }
61
+ });
62
+ }
63
+ function Card({ className, size = "default", ...props }) {
64
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
65
+ "data-slot": "card",
66
+ "data-size": size,
67
+ 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),
68
+ ...props
69
+ });
70
+ }
71
+ function CardHeader({ className, ...props }) {
72
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
73
+ "data-slot": "card-header",
74
+ 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),
75
+ ...props
76
+ });
77
+ }
78
+ function CardTitle({ className, ...props }) {
79
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
80
+ "data-slot": "card-title",
81
+ className: cn("font-heading text-base leading-snug font-medium group-data-[size=sm]/card:text-sm", className),
82
+ ...props
83
+ });
84
+ }
85
+ function CardContent({ className, ...props }) {
86
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
87
+ "data-slot": "card-content",
88
+ className: cn("px-(--card-spacing)", className),
89
+ ...props
90
+ });
91
+ }
92
+ //#endregion
93
+ //#region node_modules/.nitro/vite/services/ssr/assets/connectors-vIoacH6V.js
94
+ /**
95
+ * Readiness and connections for every connector in the registry. Manifests are
96
+ * imported directly by the client, so they are deliberately not sent again.
97
+ */
98
+ var getConnectorOverview = createServerFn({ method: "GET" }).handler(createSsrRpc("0e209afb63fad4a95a5cfeaf4831610f23673145cbf5b8054765d79804a62d31"));
99
+ /**
100
+ * Starts a login that finishes on a phone. The code is rendered here rather
101
+ * than in the browser so the page needs no QR library and never learns how the
102
+ * provider's login works.
103
+ */
104
+ var beginQrLogin = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(createSsrRpc("794964d675b97edaa881ab5cb209225ef0a4507e3d2771d6577658a058e7621f"));
105
+ /**
106
+ * One look at a pending scan. The credential never reaches the browser: a
107
+ * confirmed login is saved here and the page is told only that it worked.
108
+ */
109
+ var continueQrLogin = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(createSsrRpc("43b97e3644ad73286fe5de3e99134bff099e1a8704f1ae91fe643d419eceba73"));
110
+ var disconnectConnection = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(createSsrRpc("f94f0787ce6704ea24f18add66832e0367a0feb9a690d8ad6454abec5e5df193"));
111
+ var verifyConnection = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(createSsrRpc("f9ade3084971078ee9f2bc16fcdcdfb5a8d66a0f9b634803bb611287c14e21d7"));
112
+ var saveConnectionSettings = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(createSsrRpc("18bae5deb2a19571893f0067e374e537f16d6774f9b4e04f9b7349360f6f8fdc"));
113
+ //#endregion
114
+ //#region node_modules/.nitro/vite/services/ssr/assets/tasks-rgWCNdRw.js
115
+ var getInbox = createServerFn({ method: "GET" }).handler(createSsrRpc("48bc32df9d6f69f45df8dfbc6e798e1f99152dbd4b4ed65b149fa2612c65e687"));
116
+ var getLoopTasks = createServerFn({ method: "GET" }).inputValidator((data) => data).handler(createSsrRpc("3f938fac3e5ec1631e700a2fdd4790e78abbefeb732ecfbbe6864cc19f0d5aac"));
117
+ var getTaskById = createServerFn({ method: "GET" }).inputValidator((data) => data).handler(createSsrRpc("0033ef7c619c027b1428a722dff188d315ed335645afcb1033528630b7c95e88"));
118
+ var getTaskLog = createServerFn({ method: "GET" }).inputValidator((data) => data).handler(createSsrRpc("81f567724371f0d6f24e636906e2e9ea5b7384422ac583c86a1050fb838664ce"));
119
+ var runLoopNow = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(createSsrRpc("7258ce5fc6f23acf3aab039ecd4acdb006d42b2ddbf3ab8629b4000e101b51dc"));
120
+ var stopTask = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(createSsrRpc("a57f49c322cc5afa2996e4a5777fa533fd963755d614d4a14eb32e51fb198d9d"));
121
+ /** Whether the dispatcher is up, and how hard it is allowed to pull. */
122
+ var getDispatcherState = createServerFn({ method: "GET" }).handler(createSsrRpc("921b049ebafe3c6fb5206fbe3e2100608d3333b944e18385e57da62a28233bc6"));
123
+ var pauseDispatcher = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(createSsrRpc("0fa144f67263a4a51c51e8baf134a15b89c0afdf9b92b64660520a5bce247eec"));
124
+ //#endregion
125
+ //#region node_modules/.nitro/vite/services/ssr/assets/loops-BBQDw1Mh.js
126
+ /**
127
+ * Saving is both making and changing, told apart by whether there is an id.
128
+ * A loop chosen and not yet saved has none, and nothing on the server knew
129
+ * about it until now.
130
+ */
131
+ var edit = (data) => data;
132
+ var getLoopsPage = createServerFn({ method: "GET" }).handler(createSsrRpc("6d5a90e9ec4ebf70d8e2ac4aff960533a5e68d21b289033cc7e0751d00554c93"));
133
+ var getLoopReadiness = createServerFn({ method: "GET" }).handler(createSsrRpc("997d2dd097598c44811fe97def167b79d33e4349c51e783058da648e860c7c1f"));
134
+ var getLoopById = createServerFn({ method: "GET" }).inputValidator((data) => data).handler(createSsrRpc("81a1af559b2a373901872aa9f9a6546f2f3bda8157d81c21a36ba0d1d337613b"));
135
+ var saveLoop = createServerFn({ method: "POST" }).inputValidator(edit).handler(createSsrRpc("e91fc2fd51633c7ca9184fea4beb66fe775ed0b4e6323111a4690da916a78b00"));
136
+ var toggleLoop = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(createSsrRpc("3fc8a7de98be979f9820a4f5a0ca2e234ed8a7df8e692abb8c4437765474eca0"));
137
+ var removeLoop = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(createSsrRpc("b5a89ca2d07f4ccf2827b5132ca52e7c7a42d584e702b0ab3963b2d5763c35b0"));
138
+ var reorderLoop = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(createSsrRpc("7d7d5f80a4f0d2b3bb659d7dc2a91ea5983880648b9d1aa24356e7340505930f"));
139
+ var getLoopPollState = createServerFn({ method: "GET" }).inputValidator((data) => data).handler(createSsrRpc("4fd3855e3e9ed954e102071a55b98d91651c9b1e353161f7223768ed6f2b8c28"));
140
+ var runLoopBacklog = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(createSsrRpc("65494cbfd06143dbd9a012808fbba21163d9902e387869503fb8b68b0674902b"));
141
+ //#endregion
142
+ //#region node_modules/.nitro/vite/services/ssr/assets/router-3WmpE5b_.js
143
+ /**
144
+ * Loopable's mark: something going round, and the thing it goes round for.
145
+ *
146
+ * A ring and an arrowhead on their own is the icon every application uses for
147
+ * "refresh", so the dot is what stops this being that: it reads as a loop
148
+ * with work at the centre of it rather than as a button that reloads. Three
149
+ * shapes and no more, because the smallest place it appears is a 16px browser
150
+ * tab, and a fourth turns the whole thing to porridge.
151
+ *
152
+ * The ring is cut square rather than rounded and the head overlaps where it
153
+ * stops, so the two meet as one shape. A round cap there pokes out past the
154
+ * head and puts a notch in the joint, which is invisible in a tab and obvious
155
+ * at the size a dock icon is.
156
+ *
157
+ * Kept in step with public/favicon.svg by hand. The copy buys an icon the
158
+ * browser can fetch without running the app, which is the point of a favicon.
159
+ */
160
+ function LoopableMark({ className }) {
161
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", {
162
+ viewBox: "0 0 24 24",
163
+ fill: "none",
164
+ "aria-hidden": true,
165
+ className,
166
+ children: [
167
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", {
168
+ d: "M17.14 5.87A8 8 0 1 1 12 4",
169
+ stroke: "currentColor",
170
+ strokeWidth: "2.6"
171
+ }),
172
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", {
173
+ d: "M15.4 4 10.8 5.9 10.8 2.1Z",
174
+ fill: "currentColor"
175
+ }),
176
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", {
177
+ cx: "12",
178
+ cy: "12",
179
+ r: "2.1",
180
+ fill: "currentColor"
181
+ })
182
+ ]
183
+ });
184
+ }
185
+ 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", {
186
+ variants: {
187
+ variant: {
188
+ default: "bg-primary text-primary-foreground hover:bg-primary/80",
189
+ 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",
190
+ 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",
191
+ ghost: "hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50",
192
+ 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",
193
+ link: "text-primary underline-offset-4 hover:underline"
194
+ },
195
+ size: {
196
+ default: "h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
197
+ 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",
198
+ 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",
199
+ lg: "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
200
+ icon: "size-8",
201
+ "icon-xs": "size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3",
202
+ "icon-sm": "size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg",
203
+ "icon-lg": "size-9"
204
+ }
205
+ },
206
+ defaultVariants: {
207
+ variant: "default",
208
+ size: "default"
209
+ }
210
+ });
211
+ function Button$1({ className, variant = "default", size = "default", ...props }) {
212
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
213
+ "data-slot": "button",
214
+ className: cn(buttonVariants({
215
+ variant,
216
+ size,
217
+ className
218
+ })),
219
+ ...props
220
+ });
221
+ }
222
+ /**
223
+ * The dispatcher can stop between two page loads and nothing on a page would
224
+ * change, so this asks rather than waiting to be told.
225
+ */
226
+ function useDispatcherState(everyMs = 1e4) {
227
+ const [dispatcher, setDispatcher] = (0, import_react.useState)(null);
228
+ const refresh = (0, import_react.useCallback)(async () => {
229
+ try {
230
+ setDispatcher(await getDispatcherState());
231
+ } catch {}
232
+ }, []);
233
+ (0, import_react.useEffect)(() => {
234
+ refresh();
235
+ const timer = setInterval(() => void refresh(), everyMs);
236
+ return () => clearInterval(timer);
237
+ }, [everyMs, refresh]);
238
+ return {
239
+ dispatcher,
240
+ refresh
241
+ };
242
+ }
243
+ /**
244
+ * Runs are dispatched from this process, so a queued task that never moves
245
+ * usually means it is not there. Saying so is the difference between a queue
246
+ * and a thing that quietly swallows work.
247
+ *
248
+ * It lives in the sidebar because it is true of the whole app rather than of
249
+ * whichever page is open, and because a page is the wrong place to learn that
250
+ * nothing you do on it will happen.
251
+ */
252
+ function DispatcherStatus() {
253
+ const { dispatcher, refresh } = useDispatcherState();
254
+ const [busy, setBusy] = (0, import_react.useState)(false);
255
+ if (!dispatcher) return null;
256
+ const down = !dispatcher.alive;
257
+ const toggle = async () => {
258
+ setBusy(true);
259
+ try {
260
+ await pauseDispatcher({ data: { paused: !dispatcher.paused } });
261
+ await refresh();
262
+ } catch (error) {
263
+ toast.error(error instanceof Error ? error.message : String(error));
264
+ } finally {
265
+ setBusy(false);
266
+ }
267
+ };
268
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
269
+ className: cn("flex flex-col gap-1.5 rounded-md px-2 py-1.5 text-xs", "group-data-[collapsible=icon]:items-center group-data-[collapsible=icon]:px-0", !down && !dispatcher.paused ? "text-muted-foreground" : "bg-amber-500/10 text-amber-700 dark:text-amber-500"),
270
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
271
+ className: "flex items-center gap-2",
272
+ children: [
273
+ down ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CircleAlert, { className: "size-3.5 shrink-0" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: cn("size-2 shrink-0 rounded-full", dispatcher.paused ? "bg-amber-500" : "bg-emerald-500") }),
274
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
275
+ className: "flex-1 truncate group-data-[collapsible=icon]:hidden",
276
+ children: down ? "Dispatcher not running" : dispatcher.paused ? "Paused" : "Dispatcher running"
277
+ }),
278
+ down ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button$1, {
279
+ size: "icon-sm",
280
+ variant: "ghost",
281
+ onClick: toggle,
282
+ disabled: busy,
283
+ title: dispatcher.paused ? "Resume" : "Pause",
284
+ className: "size-6 shrink-0 group-data-[collapsible=icon]:hidden",
285
+ children: [dispatcher.paused ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CirclePlay, {}) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CirclePause, {}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
286
+ className: "sr-only",
287
+ children: dispatcher.paused ? "Resume" : "Pause"
288
+ })]
289
+ })
290
+ ]
291
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
292
+ className: "text-[11px] leading-snug opacity-80 group-data-[collapsible=icon]:hidden",
293
+ children: down ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: "Queued tasks will wait until the dispatcher is running." }) : dispatcher.paused ? "Nothing will run until you start it again." : `${dispatcher.maxConcurrentRuns} run${dispatcher.maxConcurrentRuns === 1 ? "" : "s"} at a time${dispatcher.onlineRunners > 0 ? ` · ${dispatcher.onlineRunners} runner${dispatcher.onlineRunners === 1 ? "" : "s"} online` : ""}.`
294
+ })]
295
+ });
296
+ }
297
+ var MOBILE_BREAKPOINT = 768;
298
+ function useIsMobile() {
299
+ const [isMobile, setIsMobile] = import_react.useState(void 0);
300
+ import_react.useEffect(() => {
301
+ const mql = window.matchMedia(`(max-width: 767px)`);
302
+ const onChange = () => {
303
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
304
+ };
305
+ mql.addEventListener("change", onChange);
306
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
307
+ return () => mql.removeEventListener("change", onChange);
308
+ }, []);
309
+ return !!isMobile;
310
+ }
311
+ function Input$1({ className, type, ...props }) {
312
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Input, {
313
+ type,
314
+ "data-slot": "input",
315
+ className: cn("h-8 w-full min-w-0 rounded-lg border border-input bg-transparent px-2.5 py-1 text-base transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40", className),
316
+ ...props
317
+ });
318
+ }
319
+ function Separator$1({ className, orientation = "horizontal", ...props }) {
320
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Separator, {
321
+ "data-slot": "separator",
322
+ orientation,
323
+ className: cn("shrink-0 bg-border data-horizontal:h-px data-horizontal:w-full data-vertical:w-px data-vertical:self-stretch", className),
324
+ ...props
325
+ });
326
+ }
327
+ function Sheet({ ...props }) {
328
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogRoot, {
329
+ "data-slot": "sheet",
330
+ ...props
331
+ });
332
+ }
333
+ function SheetPortal({ ...props }) {
334
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogPortal, {
335
+ "data-slot": "sheet-portal",
336
+ ...props
337
+ });
338
+ }
339
+ function SheetOverlay({ className, ...props }) {
340
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogBackdrop, {
341
+ "data-slot": "sheet-overlay",
342
+ className: cn("fixed inset-0 z-50 bg-black/10 transition-opacity duration-150 data-ending-style:opacity-0 data-starting-style:opacity-0 supports-backdrop-filter:backdrop-blur-xs", className),
343
+ ...props
344
+ });
345
+ }
346
+ function SheetContent({ className, children, side = "right", showCloseButton = true, ...props }) {
347
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SheetPortal, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SheetOverlay, {}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DialogPopup, {
348
+ "data-slot": "sheet-content",
349
+ "data-side": side,
350
+ className: cn("fixed z-50 flex flex-col gap-4 bg-popover bg-clip-padding text-sm text-popover-foreground shadow-lg transition duration-200 ease-in-out data-ending-style:opacity-0 data-starting-style:opacity-0 data-[side=bottom]:inset-x-0 data-[side=bottom]:bottom-0 data-[side=bottom]:h-auto data-[side=bottom]:border-t data-[side=bottom]:data-ending-style:translate-y-[2.5rem] data-[side=bottom]:data-starting-style:translate-y-[2.5rem] data-[side=left]:inset-y-0 data-[side=left]:left-0 data-[side=left]:h-full data-[side=left]:w-3/4 data-[side=left]:border-r data-[side=left]:data-ending-style:translate-x-[-2.5rem] data-[side=left]:data-starting-style:translate-x-[-2.5rem] data-[side=right]:inset-y-0 data-[side=right]:right-0 data-[side=right]:h-full data-[side=right]:w-3/4 data-[side=right]:border-l data-[side=right]:data-ending-style:translate-x-[2.5rem] data-[side=right]:data-starting-style:translate-x-[2.5rem] data-[side=top]:inset-x-0 data-[side=top]:top-0 data-[side=top]:h-auto data-[side=top]:border-b data-[side=top]:data-ending-style:translate-y-[-2.5rem] data-[side=top]:data-starting-style:translate-y-[-2.5rem] data-[side=left]:sm:max-w-sm data-[side=right]:sm:max-w-sm", className),
351
+ ...props,
352
+ children: [children, showCloseButton && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DialogClose, {
353
+ "data-slot": "sheet-close",
354
+ render: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
355
+ variant: "ghost",
356
+ className: "absolute top-3 right-3",
357
+ size: "icon-sm"
358
+ }),
359
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(X, {}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
360
+ className: "sr-only",
361
+ children: "Close"
362
+ })]
363
+ })]
364
+ })] });
365
+ }
366
+ function SheetHeader({ className, ...props }) {
367
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
368
+ "data-slot": "sheet-header",
369
+ className: cn("flex flex-col gap-0.5 p-4", className),
370
+ ...props
371
+ });
372
+ }
373
+ function SheetTitle({ className, ...props }) {
374
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogTitle, {
375
+ "data-slot": "sheet-title",
376
+ className: cn("font-heading text-base font-medium text-foreground", className),
377
+ ...props
378
+ });
379
+ }
380
+ function SheetDescription({ className, ...props }) {
381
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogDescription, {
382
+ "data-slot": "sheet-description",
383
+ className: cn("text-sm text-muted-foreground", className),
384
+ ...props
385
+ });
386
+ }
387
+ function Tooltip$1({ ...props }) {
388
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TooltipRoot, {
389
+ "data-slot": "tooltip",
390
+ ...props
391
+ });
392
+ }
393
+ function TooltipTrigger({ ...props }) {
394
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TooltipTrigger$1, {
395
+ "data-slot": "tooltip-trigger",
396
+ ...props
397
+ });
398
+ }
399
+ function TooltipContent({ className, side = "top", sideOffset = 4, align = "center", alignOffset = 0, children, ...props }) {
400
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TooltipPortal, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TooltipPositioner, {
401
+ align,
402
+ alignOffset,
403
+ side,
404
+ sideOffset,
405
+ className: "isolate z-50",
406
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(TooltipPopup, {
407
+ "data-slot": "tooltip-content",
408
+ className: cn("z-50 inline-flex w-fit max-w-xs origin-(--transform-origin) items-center gap-1.5 rounded-md bg-foreground px-3 py-1.5 text-xs text-background has-data-[slot=kbd]:pr-1.5 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-[slot=kbd]:relative **:data-[slot=kbd]:isolate **:data-[slot=kbd]:z-50 **:data-[slot=kbd]:rounded-sm data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 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),
409
+ ...props,
410
+ children: [children, /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TooltipArrow, { className: "z-50 size-2.5 translate-y-[calc(-50%-2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground data-[side=bottom]:top-1 data-[side=inline-end]:top-1/2! data-[side=inline-end]:-left-1 data-[side=inline-end]:-translate-y-1/2 data-[side=inline-start]:top-1/2! data-[side=inline-start]:-right-1 data-[side=inline-start]:-translate-y-1/2 data-[side=left]:top-1/2! data-[side=left]:-right-1 data-[side=left]:-translate-y-1/2 data-[side=right]:top-1/2! data-[side=right]:-left-1 data-[side=right]:-translate-y-1/2 data-[side=top]:-bottom-2.5" })]
411
+ })
412
+ }) });
413
+ }
414
+ var SIDEBAR_COOKIE_NAME = "sidebar_state";
415
+ var SIDEBAR_COOKIE_MAX_AGE = 604800;
416
+ var SIDEBAR_WIDTH = "16rem";
417
+ var SIDEBAR_WIDTH_MOBILE = "18rem";
418
+ var SIDEBAR_WIDTH_ICON = "3rem";
419
+ var SIDEBAR_KEYBOARD_SHORTCUT = "b";
420
+ var SidebarContext = import_react.createContext(null);
421
+ function useSidebar() {
422
+ const context = import_react.useContext(SidebarContext);
423
+ if (!context) throw new Error("useSidebar must be used within a SidebarProvider.");
424
+ return context;
425
+ }
426
+ function SidebarProvider({ defaultOpen = true, open: openProp, onOpenChange: setOpenProp, className, style, children, ...props }) {
427
+ const isMobile = useIsMobile();
428
+ const [openMobile, setOpenMobile] = import_react.useState(false);
429
+ const [_open, _setOpen] = import_react.useState(defaultOpen);
430
+ const open = openProp ?? _open;
431
+ const setOpen = import_react.useCallback((value) => {
432
+ const openState = typeof value === "function" ? value(open) : value;
433
+ if (setOpenProp) setOpenProp(openState);
434
+ else _setOpen(openState);
435
+ document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`;
436
+ }, [setOpenProp, open]);
437
+ const toggleSidebar = import_react.useCallback(() => {
438
+ return isMobile ? setOpenMobile((open) => !open) : setOpen((open) => !open);
439
+ }, [
440
+ isMobile,
441
+ setOpen,
442
+ setOpenMobile
443
+ ]);
444
+ import_react.useEffect(() => {
445
+ const handleKeyDown = (event) => {
446
+ if (event.key === SIDEBAR_KEYBOARD_SHORTCUT && (event.metaKey || event.ctrlKey)) {
447
+ event.preventDefault();
448
+ toggleSidebar();
449
+ }
450
+ };
451
+ window.addEventListener("keydown", handleKeyDown);
452
+ return () => window.removeEventListener("keydown", handleKeyDown);
453
+ }, [toggleSidebar]);
454
+ const state = open ? "expanded" : "collapsed";
455
+ const contextValue = import_react.useMemo(() => ({
456
+ state,
457
+ open,
458
+ setOpen,
459
+ isMobile,
460
+ openMobile,
461
+ setOpenMobile,
462
+ toggleSidebar
463
+ }), [
464
+ state,
465
+ open,
466
+ setOpen,
467
+ isMobile,
468
+ openMobile,
469
+ setOpenMobile,
470
+ toggleSidebar
471
+ ]);
472
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SidebarContext.Provider, {
473
+ value: contextValue,
474
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
475
+ "data-slot": "sidebar-wrapper",
476
+ style: {
477
+ "--sidebar-width": SIDEBAR_WIDTH,
478
+ "--sidebar-width-icon": SIDEBAR_WIDTH_ICON,
479
+ ...style
480
+ },
481
+ className: cn("group/sidebar-wrapper flex min-h-svh w-full has-data-[variant=inset]:bg-sidebar", className),
482
+ ...props,
483
+ children
484
+ })
485
+ });
486
+ }
487
+ function Sidebar({ side = "left", variant = "sidebar", collapsible = "offcanvas", className, children, dir, ...props }) {
488
+ const { isMobile, state, openMobile, setOpenMobile } = useSidebar();
489
+ if (collapsible === "none") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
490
+ "data-slot": "sidebar",
491
+ className: cn("flex h-full w-(--sidebar-width) flex-col bg-sidebar text-sidebar-foreground", className),
492
+ ...props,
493
+ children
494
+ });
495
+ if (isMobile) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Sheet, {
496
+ open: openMobile,
497
+ onOpenChange: setOpenMobile,
498
+ ...props,
499
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SheetContent, {
500
+ dir,
501
+ "data-sidebar": "sidebar",
502
+ "data-slot": "sidebar",
503
+ "data-mobile": "true",
504
+ className: "w-(--sidebar-width) bg-sidebar p-0 text-sidebar-foreground [&>button]:hidden",
505
+ style: { "--sidebar-width": SIDEBAR_WIDTH_MOBILE },
506
+ side,
507
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SheetHeader, {
508
+ className: "sr-only",
509
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SheetTitle, { children: "Sidebar" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SheetDescription, { children: "Displays the mobile sidebar." })]
510
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
511
+ className: "flex h-full w-full flex-col",
512
+ children
513
+ })]
514
+ })
515
+ });
516
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
517
+ className: "group peer hidden text-sidebar-foreground md:block",
518
+ "data-state": state,
519
+ "data-collapsible": state === "collapsed" ? collapsible : "",
520
+ "data-variant": variant,
521
+ "data-side": side,
522
+ "data-slot": "sidebar",
523
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
524
+ "data-slot": "sidebar-gap",
525
+ className: cn("relative w-(--sidebar-width) bg-transparent transition-[width] duration-200 ease-linear", "group-data-[collapsible=offcanvas]:w-0", "group-data-[side=right]:rotate-180", variant === "floating" || variant === "inset" ? "group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4)))]" : "group-data-[collapsible=icon]:w-(--sidebar-width-icon)")
526
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
527
+ "data-slot": "sidebar-container",
528
+ "data-side": side,
529
+ className: cn("fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear data-[side=left]:left-0 data-[side=left]:group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)] data-[side=right]:right-0 data-[side=right]:group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)] md:flex", variant === "floating" || variant === "inset" ? "p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]" : "group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l", className),
530
+ ...props,
531
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
532
+ "data-sidebar": "sidebar",
533
+ "data-slot": "sidebar-inner",
534
+ className: "flex size-full flex-col bg-sidebar group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:shadow-sm group-data-[variant=floating]:ring-1 group-data-[variant=floating]:ring-sidebar-border",
535
+ children
536
+ })
537
+ })]
538
+ });
539
+ }
540
+ function SidebarTrigger({ className, onClick, ...props }) {
541
+ const { toggleSidebar } = useSidebar();
542
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button$1, {
543
+ "data-sidebar": "trigger",
544
+ "data-slot": "sidebar-trigger",
545
+ variant: "ghost",
546
+ size: "icon-sm",
547
+ className: cn(className),
548
+ onClick: (event) => {
549
+ onClick?.(event);
550
+ toggleSidebar();
551
+ },
552
+ ...props,
553
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(PanelLeft, {}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
554
+ className: "sr-only",
555
+ children: "Toggle Sidebar"
556
+ })]
557
+ });
558
+ }
559
+ function SidebarInset({ className, ...props }) {
560
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("main", {
561
+ "data-slot": "sidebar-inset",
562
+ className: cn("relative flex w-full flex-1 flex-col bg-background md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow-sm md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2", className),
563
+ ...props
564
+ });
565
+ }
566
+ function SidebarHeader({ className, ...props }) {
567
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
568
+ "data-slot": "sidebar-header",
569
+ "data-sidebar": "header",
570
+ className: cn("flex flex-col gap-2 p-2", className),
571
+ ...props
572
+ });
573
+ }
574
+ function SidebarFooter({ className, ...props }) {
575
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
576
+ "data-slot": "sidebar-footer",
577
+ "data-sidebar": "footer",
578
+ className: cn("flex flex-col gap-2 p-2", className),
579
+ ...props
580
+ });
581
+ }
582
+ function SidebarContent({ className, ...props }) {
583
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
584
+ "data-slot": "sidebar-content",
585
+ "data-sidebar": "content",
586
+ className: cn("no-scrollbar flex min-h-0 flex-1 flex-col gap-0 overflow-auto group-data-[collapsible=icon]:overflow-hidden", className),
587
+ ...props
588
+ });
589
+ }
590
+ function SidebarGroup({ className, ...props }) {
591
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
592
+ "data-slot": "sidebar-group",
593
+ "data-sidebar": "group",
594
+ className: cn("relative flex w-full min-w-0 flex-col p-2", className),
595
+ ...props
596
+ });
597
+ }
598
+ function SidebarGroupLabel({ className, render, ...props }) {
599
+ return useRender({
600
+ defaultTagName: "div",
601
+ props: mergeProps({ className: cn("flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium text-sidebar-foreground/70 ring-sidebar-ring outline-hidden transition-[margin,opacity] duration-200 ease-linear group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0 focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0", className) }, props),
602
+ render,
603
+ state: {
604
+ slot: "sidebar-group-label",
605
+ sidebar: "group-label"
606
+ }
607
+ });
608
+ }
609
+ function SidebarGroupContent({ className, ...props }) {
610
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
611
+ "data-slot": "sidebar-group-content",
612
+ "data-sidebar": "group-content",
613
+ className: cn("w-full text-sm", className),
614
+ ...props
615
+ });
616
+ }
617
+ function SidebarMenu({ className, ...props }) {
618
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", {
619
+ "data-slot": "sidebar-menu",
620
+ "data-sidebar": "menu",
621
+ className: cn("flex w-full min-w-0 flex-col gap-0", className),
622
+ ...props
623
+ });
624
+ }
625
+ function SidebarMenuItem({ className, ...props }) {
626
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", {
627
+ "data-slot": "sidebar-menu-item",
628
+ "data-sidebar": "menu-item",
629
+ className: cn("group/menu-item relative", className),
630
+ ...props
631
+ });
632
+ }
633
+ var sidebarMenuButtonVariants = cva("peer/menu-button group/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm ring-sidebar-ring outline-hidden transition-[width,height,padding] group-has-data-[sidebar=menu-action]/menu-item:pr-8 group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-open:hover:bg-sidebar-accent data-open:hover:text-sidebar-accent-foreground data-active:bg-sidebar-accent data-active:font-medium data-active:text-sidebar-accent-foreground [&_svg]:size-4 [&_svg]:shrink-0 [&>span:last-child]:truncate", {
634
+ variants: {
635
+ variant: {
636
+ default: "hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
637
+ outline: "bg-background shadow-[0_0_0_1px_var(--sidebar-border)] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_var(--sidebar-accent)]"
638
+ },
639
+ size: {
640
+ default: "h-8 text-sm",
641
+ sm: "h-7 text-xs",
642
+ lg: "h-12 text-sm group-data-[collapsible=icon]:p-0!"
643
+ }
644
+ },
645
+ defaultVariants: {
646
+ variant: "default",
647
+ size: "default"
648
+ }
649
+ });
650
+ function SidebarMenuButton({ render, isActive = false, variant = "default", size = "default", tooltip, className, ...props }) {
651
+ const { isMobile, state } = useSidebar();
652
+ const comp = useRender({
653
+ defaultTagName: "button",
654
+ props: mergeProps({ className: cn(sidebarMenuButtonVariants({
655
+ variant,
656
+ size
657
+ }), className) }, props),
658
+ render: !tooltip ? render : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TooltipTrigger, { render }),
659
+ state: {
660
+ slot: "sidebar-menu-button",
661
+ sidebar: "menu-button",
662
+ size,
663
+ active: isActive
664
+ }
665
+ });
666
+ if (!tooltip) return comp;
667
+ if (typeof tooltip === "string") tooltip = { children: tooltip };
668
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Tooltip$1, { children: [comp, /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TooltipContent, {
669
+ side: "right",
670
+ align: "center",
671
+ hidden: state !== "collapsed" || isMobile,
672
+ ...tooltip
673
+ })] });
674
+ }
675
+ var WORK = [{
676
+ label: "Inbox",
677
+ to: "/inbox",
678
+ icon: Inbox
679
+ }, {
680
+ label: "Loops",
681
+ to: "/loops",
682
+ icon: Workflow
683
+ }];
684
+ var SETUP = [
685
+ {
686
+ label: "Connectors",
687
+ to: "/connectors",
688
+ icon: Plug
689
+ },
690
+ {
691
+ label: "Agents",
692
+ to: "/agents",
693
+ icon: Bot
694
+ },
695
+ {
696
+ label: "Runners",
697
+ to: "/runners",
698
+ icon: Server
699
+ },
700
+ {
701
+ label: "Settings",
702
+ icon: Settings,
703
+ soon: true
704
+ }
705
+ ];
706
+ function NavGroup({ label, items }) {
707
+ const pathname = useRouterState({ select: (state) => state.location.pathname });
708
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SidebarGroup, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SidebarGroupLabel, { children: label }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SidebarGroupContent, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SidebarMenu, { children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SidebarMenuItem, { children: item.to ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SidebarMenuButton, {
709
+ isActive: pathname.startsWith(item.to),
710
+ tooltip: item.label,
711
+ render: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Link, { to: item.to }),
712
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(item.icon, {}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: item.label })]
713
+ }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SidebarMenuButton, {
714
+ disabled: true,
715
+ tooltip: `${item.label} (soon)`,
716
+ className: "cursor-default opacity-60",
717
+ children: [
718
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(item.icon, {}),
719
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: item.label }),
720
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Badge, {
721
+ variant: "outline",
722
+ className: "ml-auto",
723
+ children: "Soon"
724
+ })
725
+ ]
726
+ }) }, item.label)) }) })] });
727
+ }
728
+ function AppSidebar() {
729
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Sidebar, {
730
+ collapsible: "icon",
731
+ children: [
732
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SidebarHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
733
+ className: "flex items-center gap-1",
734
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Link, {
735
+ to: "/connectors",
736
+ className: "flex min-w-0 flex-1 items-center gap-2 px-1 py-1 group-data-[collapsible=icon]:hidden",
737
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
738
+ className: "flex size-7 shrink-0 items-center justify-center rounded-md bg-primary text-primary-foreground",
739
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LoopableMark, { className: "size-4.5" })
740
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
741
+ className: "truncate font-medium",
742
+ children: "Loopable"
743
+ })]
744
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SidebarTrigger, { className: "shrink-0" })]
745
+ }) }),
746
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SidebarContent, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(NavGroup, {
747
+ label: "Work",
748
+ items: WORK
749
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(NavGroup, {
750
+ label: "Setup",
751
+ items: SETUP
752
+ })] }),
753
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SidebarFooter, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DispatcherStatus, {}) })
754
+ ]
755
+ });
756
+ }
757
+ var Toaster$1 = ({ ...props }) => {
758
+ const { theme = "system" } = z();
759
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Toaster, {
760
+ theme,
761
+ className: "toaster group",
762
+ icons: {
763
+ success: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CircleCheck, { className: "size-4" }),
764
+ info: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Info, { className: "size-4" }),
765
+ warning: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TriangleAlert, { className: "size-4" }),
766
+ error: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(OctagonX, { className: "size-4" }),
767
+ loading: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LoaderCircle, { className: "size-4 animate-spin" })
768
+ },
769
+ style: {
770
+ "--normal-bg": "var(--popover)",
771
+ "--normal-text": "var(--popover-foreground)",
772
+ "--normal-border": "var(--border)",
773
+ "--border-radius": "var(--radius)"
774
+ },
775
+ toastOptions: { classNames: { toast: "cn-toast" } },
776
+ ...props
777
+ });
778
+ };
779
+ var styles_default = "/assets/styles-DZL5V1o7.css";
780
+ var Route$18 = createRootRoute({
781
+ head: () => ({
782
+ meta: [
783
+ { charSet: "utf-8" },
784
+ {
785
+ name: "viewport",
786
+ content: "width=device-width, initial-scale=1"
787
+ },
788
+ { title: "Loopable" }
789
+ ],
790
+ links: [
791
+ {
792
+ rel: "stylesheet",
793
+ href: styles_default
794
+ },
795
+ {
796
+ rel: "icon",
797
+ href: "/favicon.svg",
798
+ type: "image/svg+xml"
799
+ },
800
+ {
801
+ rel: "apple-touch-icon",
802
+ href: "/apple-touch-icon.png"
803
+ }
804
+ ]
805
+ }),
806
+ shellComponent: RootDocument,
807
+ component: AppLayout
808
+ });
809
+ function AppLayout() {
810
+ 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", {
811
+ className: "flex-1 p-6",
812
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
813
+ className: "mx-auto w-full max-w-4xl",
814
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Outlet, {})
815
+ })
816
+ }) })] });
817
+ }
818
+ function RootDocument({ children }) {
819
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("html", {
820
+ lang: "en",
821
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("head", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HeadContent, {}) }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("body", { children: [
822
+ children,
823
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Toaster$1, {}),
824
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Scripts, {})
825
+ ] })]
826
+ });
827
+ }
828
+ var Route$17 = createFileRoute("/")({ beforeLoad: () => {
829
+ throw redirect({ to: "/connectors" });
830
+ } });
831
+ function Label({ className, ...props }) {
832
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", {
833
+ "data-slot": "label",
834
+ 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),
835
+ ...props
836
+ });
837
+ }
838
+ var Select$1 = SelectRoot;
839
+ function SelectValue({ className, ...props }) {
840
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectValue$1, {
841
+ "data-slot": "select-value",
842
+ className: cn("flex flex-1 text-left", className),
843
+ ...props
844
+ });
845
+ }
846
+ function SelectTrigger({ className, size = "default", children, ...props }) {
847
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectTrigger$1, {
848
+ "data-slot": "select-trigger",
849
+ "data-size": size,
850
+ 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),
851
+ ...props,
852
+ 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" }) })]
853
+ });
854
+ }
855
+ function SelectContent({ className, children, side = "bottom", sideOffset = 4, align = "center", alignOffset = 0, alignItemWithTrigger = true, ...props }) {
856
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectPortal, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectPositioner, {
857
+ side,
858
+ sideOffset,
859
+ align,
860
+ alignOffset,
861
+ alignItemWithTrigger,
862
+ className: "isolate z-50",
863
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectPopup, {
864
+ "data-slot": "select-content",
865
+ "data-align-trigger": alignItemWithTrigger,
866
+ 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),
867
+ ...props,
868
+ children: [
869
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectScrollUpButton, {}),
870
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectList, { children }),
871
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectScrollDownButton, {})
872
+ ]
873
+ })
874
+ }) });
875
+ }
876
+ function SelectItem({ className, children, ...props }) {
877
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectItem$1, {
878
+ "data-slot": "select-item",
879
+ 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),
880
+ ...props,
881
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItemText, {
882
+ className: "flex flex-1 shrink-0 gap-2 whitespace-nowrap",
883
+ children
884
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItemIndicator, {
885
+ render: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "pointer-events-none absolute right-2 flex size-4 items-center justify-center" }),
886
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Check, { className: "pointer-events-none" })
887
+ })]
888
+ });
889
+ }
890
+ function SelectScrollUpButton({ className, ...props }) {
891
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectScrollUpArrow, {
892
+ "data-slot": "select-scroll-up-button",
893
+ 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),
894
+ ...props,
895
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronUp, {})
896
+ });
897
+ }
898
+ function SelectScrollDownButton({ className, ...props }) {
899
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectScrollDownArrow, {
900
+ "data-slot": "select-scroll-down-button",
901
+ 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),
902
+ ...props,
903
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronDown, {})
904
+ });
905
+ }
906
+ /**
907
+ * States still in flight. Used by the pages to know when
908
+ * to keep looking, and by the reaper to find runs a dead process abandoned.
909
+ */
910
+ var TASK_ACTIVE_STATES = [
911
+ "queued",
912
+ "preparing",
913
+ "awaiting_agent",
914
+ "applying"
915
+ ];
916
+ function isTaskActive(state) {
917
+ return TASK_ACTIVE_STATES.includes(state);
918
+ }
919
+ /**
920
+ * The work happens in the dispatcher, so the page has to go and look. Polling only
921
+ * while something is actually running keeps an idle inbox quiet.
922
+ */
923
+ function useLiveTasks(states, everyMs = 2e3) {
924
+ useLiveRefresh(everyMs, states.some((task) => isTaskActive(task.state)));
925
+ }
926
+ /** Runner inventory changes without this page doing anything, so it looks again. */
927
+ function useLiveRefresh(everyMs = 1e4, enabled = true) {
928
+ const router = useRouter();
929
+ (0, import_react.useEffect)(() => {
930
+ if (!enabled) return;
931
+ const timer = setInterval(() => void router.invalidate(), everyMs);
932
+ return () => clearInterval(timer);
933
+ }, [
934
+ enabled,
935
+ everyMs,
936
+ router
937
+ ]);
938
+ }
939
+ var getAgentsPage = createServerFn({ method: "GET" }).handler(createSsrRpc("bde588e9b0b81a4ee619a63872d6b8b76e22db006c7a3c4351bcd56e5f76bbed"));
940
+ var saveAgent = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(createSsrRpc("80883b912a5a0aba2aa583b505e4e289d765a3f7fbb793a97692f439e7f960ba"));
941
+ var chooseDefaultAgent = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(createSsrRpc("143ea51f7421d9eebec9ad31c2df8eed5aa2e5d8035473dbfad39913fd7d54c0"));
942
+ var runAgentTest = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(createSsrRpc("b0878f841b35f46b5f2dc9cbd71c5be5895ae1b8903b3b24c24dfcf86291d3e4"));
943
+ var ICONS = {
944
+ Terminal,
945
+ MousePointerClick,
946
+ Bot
947
+ };
948
+ var MODE_LABELS = {
949
+ read_only: "Read only",
950
+ workspace_write: "Can write in the workspace"
951
+ };
952
+ var Route$16 = createFileRoute("/agents/")({
953
+ loader: () => getAgentsPage(),
954
+ component: AgentsPage
955
+ });
956
+ function AgentsPage() {
957
+ const { agents } = Route$16.useLoaderData();
958
+ useLiveRefresh();
959
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
960
+ className: "flex flex-col gap-6",
961
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", {
962
+ className: "text-2xl font-semibold tracking-tight",
963
+ children: "Agents"
964
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
965
+ className: "mt-1 text-sm text-muted-foreground",
966
+ children: "The coding CLIs Loopable knows how to run. Presence comes from runners. Settings here are how every runner invokes that CLI."
967
+ })] }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
968
+ className: "flex flex-col gap-2",
969
+ children: AGENT_MANIFESTS.map((manifest) => {
970
+ const view = agents.find((agent) => agent.agentId === manifest.id);
971
+ return view ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AgentRow, {
972
+ manifest,
973
+ view
974
+ }, manifest.id) : null;
975
+ })
976
+ })]
977
+ });
978
+ }
979
+ function presence(view) {
980
+ const signedIn = view.onRunners.filter((runner) => runner.signedIn);
981
+ const online = signedIn.filter((runner) => runner.status === "online");
982
+ if (online.length > 0) return online.length === 1 ? `Signed in on ${online[0].name}` : `Signed in on ${online.length} runners`;
983
+ if (signedIn.length > 0) return "Signed in, but every runner is offline";
984
+ if (view.onRunners.length > 0) return "Installed, not signed in";
985
+ return "Not on any runner";
986
+ }
987
+ function AgentRow({ manifest, view }) {
988
+ const router = useRouter();
989
+ const Icon = ICONS[manifest.icon] ?? Bot;
990
+ const [open, setOpen] = (0, import_react.useState)(view.isDefault && !view.defaultIsImplicit);
991
+ const [mode, setMode] = (0, import_react.useState)(view.settings.permissionMode);
992
+ const [model, setModel] = (0, import_react.useState)(view.settings.model ?? "");
993
+ const [seconds, setSeconds] = (0, import_react.useState)(String(Math.round(view.settings.timeoutMs / 1e3)));
994
+ const [busy, setBusy] = (0, import_react.useState)(null);
995
+ const [testOutput, setTestOutput] = (0, import_react.useState)(null);
996
+ const online = view.onRunners.some((runner) => runner.signedIn && runner.status === "online");
997
+ const act = async (kind, run) => {
998
+ setBusy(kind);
999
+ try {
1000
+ await run();
1001
+ } catch (error) {
1002
+ toast.error(error instanceof Error ? error.message : String(error));
1003
+ } finally {
1004
+ setBusy(null);
1005
+ }
1006
+ };
1007
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Card, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1008
+ className: "flex items-start gap-3 p-4",
1009
+ children: [
1010
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
1011
+ className: cn("mt-0.5 flex size-8 shrink-0 items-center justify-center rounded-md", online ? manifest.accent : "bg-muted text-muted-foreground"),
1012
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { className: "size-4" })
1013
+ }),
1014
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", {
1015
+ type: "button",
1016
+ className: "min-w-0 flex-1 text-left",
1017
+ onClick: () => setOpen((value) => !value),
1018
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1019
+ className: "flex flex-wrap items-center gap-2",
1020
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
1021
+ className: "font-medium",
1022
+ children: manifest.name
1023
+ }), view.isDefault ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Badge, { children: view.defaultIsImplicit ? "Default (only one signed in)" : "Default" }) : null]
1024
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
1025
+ className: "mt-0.5 text-sm text-muted-foreground",
1026
+ children: presence(view)
1027
+ })]
1028
+ }),
1029
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1030
+ className: "flex shrink-0 items-center gap-1",
1031
+ children: [!view.isDefault ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
1032
+ variant: "ghost",
1033
+ size: "sm",
1034
+ disabled: busy !== null,
1035
+ onClick: () => act("default", async () => {
1036
+ await chooseDefaultAgent({ data: { agentId: manifest.id } });
1037
+ await router.invalidate();
1038
+ toast.success(`${manifest.name} will run the loops`);
1039
+ }),
1040
+ children: "Default"
1041
+ }) : null, /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
1042
+ variant: "ghost",
1043
+ size: "icon-sm",
1044
+ "aria-expanded": open,
1045
+ "aria-label": "Settings",
1046
+ onClick: () => setOpen((value) => !value),
1047
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronDown, { className: cn("size-4 transition-transform", open && "rotate-180") })
1048
+ })]
1049
+ })
1050
+ ]
1051
+ }), open ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(CardContent, {
1052
+ className: "flex flex-col gap-4 border-t pt-4",
1053
+ children: [
1054
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
1055
+ className: "text-sm text-muted-foreground",
1056
+ children: manifest.tagline
1057
+ }),
1058
+ !online ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
1059
+ className: "text-sm text-muted-foreground",
1060
+ children: manifest.installHint
1061
+ }) : null,
1062
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1063
+ className: "grid gap-4 sm:grid-cols-3",
1064
+ children: [
1065
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1066
+ className: "flex flex-col gap-2",
1067
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Label, {
1068
+ htmlFor: `${manifest.id}-mode`,
1069
+ children: "Permission"
1070
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Select$1, {
1071
+ value: mode,
1072
+ onValueChange: (value) => setMode(value),
1073
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectTrigger, {
1074
+ id: `${manifest.id}-mode`,
1075
+ className: "w-full",
1076
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectValue, { children: (value) => MODE_LABELS[value] ?? value })
1077
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectContent, { children: manifest.permissionModes.map((value) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
1078
+ value,
1079
+ children: MODE_LABELS[value]
1080
+ }, value)) })]
1081
+ })]
1082
+ }),
1083
+ manifest.supportsModel ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1084
+ className: "flex flex-col gap-2",
1085
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Label, {
1086
+ htmlFor: `${manifest.id}-model`,
1087
+ children: "Model"
1088
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Input$1, {
1089
+ id: `${manifest.id}-model`,
1090
+ value: model,
1091
+ placeholder: manifest.modelPlaceholder,
1092
+ onChange: (event) => setModel(event.target.value)
1093
+ })]
1094
+ }) : null,
1095
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1096
+ className: "flex flex-col gap-2",
1097
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Label, {
1098
+ htmlFor: `${manifest.id}-timeout`,
1099
+ children: "Timeout (seconds)"
1100
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Input$1, {
1101
+ id: `${manifest.id}-timeout`,
1102
+ value: seconds,
1103
+ inputMode: "numeric",
1104
+ onChange: (event) => setSeconds(event.target.value.replace(/\D/g, ""))
1105
+ })]
1106
+ })
1107
+ ]
1108
+ }),
1109
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("pre", {
1110
+ className: "overflow-x-auto rounded-md bg-muted p-2.5 text-xs",
1111
+ children: view.commandPreview
1112
+ }),
1113
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1114
+ className: "flex flex-wrap items-center gap-2",
1115
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
1116
+ size: "sm",
1117
+ disabled: busy !== null,
1118
+ onClick: () => act("save", async () => {
1119
+ await saveAgent({ data: {
1120
+ agentId: manifest.id,
1121
+ permissionMode: mode,
1122
+ model: model.trim() ? model.trim() : null,
1123
+ timeoutMs: Number(seconds) * 1e3
1124
+ } });
1125
+ await router.invalidate();
1126
+ toast.success(`Saved ${manifest.name} settings`);
1127
+ }),
1128
+ children: busy === "save" ? "Saving..." : "Save"
1129
+ }), view.installed ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button$1, {
1130
+ variant: "outline",
1131
+ size: "sm",
1132
+ disabled: busy !== null,
1133
+ onClick: () => act("test", async () => {
1134
+ setTestOutput(null);
1135
+ const result = await runAgentTest({ data: { agentId: manifest.id } });
1136
+ 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());
1137
+ if (result.passed) toast.success(`${manifest.name} is working`);
1138
+ else toast.error(`${manifest.name} did not pass`);
1139
+ }),
1140
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CirclePlay, {}), busy === "test" ? "Running..." : "Test"]
1141
+ }) : null]
1142
+ }),
1143
+ testOutput ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
1144
+ className: "whitespace-pre-wrap text-xs text-muted-foreground",
1145
+ children: testOutput
1146
+ }) : null
1147
+ ]
1148
+ }) : null] });
1149
+ }
1150
+ var $$splitComponentImporter$7 = () => import("./connectors-BHNwuIMj.mjs");
1151
+ var Route$15 = createFileRoute("/connectors/")({
1152
+ validateSearch: (search) => {
1153
+ const result = {};
1154
+ if (typeof search.connected === "string") result.connected = search.connected;
1155
+ if (typeof search.error === "string") result.error = search.error;
1156
+ return result;
1157
+ },
1158
+ loader: () => getConnectorOverview(),
1159
+ component: lazyRouteComponent($$splitComponentImporter$7, "component")
1160
+ });
1161
+ var $$splitComponentImporter$6 = () => import("../_connectorId-8nb5yOzG.mjs");
1162
+ var Route$14 = createFileRoute("/connectors/$connectorId")({
1163
+ loader: async ({ params }) => {
1164
+ if (!connectorManifest(params.connectorId)) throw notFound();
1165
+ const state = (await getConnectorOverview()).find((entry) => entry.connectorId === params.connectorId);
1166
+ if (!state) throw notFound();
1167
+ return state;
1168
+ },
1169
+ component: lazyRouteComponent($$splitComponentImporter$6, "component")
1170
+ });
1171
+ var $$splitComponentImporter$5 = () => import("./inbox-k5zHergU.mjs");
1172
+ var Route$13 = createFileRoute("/inbox/")({
1173
+ loader: async () => ({ tasks: await getInbox() }),
1174
+ component: lazyRouteComponent($$splitComponentImporter$5, "component")
1175
+ });
1176
+ var $$splitComponentImporter$4 = () => import("../_taskId-CXS3lod0.mjs");
1177
+ var Route$12 = createFileRoute("/inbox/$taskId")({
1178
+ loader: async ({ params }) => {
1179
+ const task = await getTaskById({ data: { id: params.taskId } });
1180
+ if (!task) throw notFound();
1181
+ return {
1182
+ task,
1183
+ log: await getTaskLog({ data: { id: params.taskId } })
1184
+ };
1185
+ },
1186
+ component: lazyRouteComponent($$splitComponentImporter$4, "component")
1187
+ });
1188
+ /**
1189
+ * The findings that go on the lines rather than in the body. Shown separately
1190
+ * because that is what they are: on GitHub they will be spread across the
1191
+ * diff, and this is the only place they can be read as a list.
1192
+ */
1193
+ /**
1194
+ * Everything the agent printed, as it prints it. Four minutes of a spinner
1195
+ * tells you nothing; this is what tells you the run is alive, and afterwards
1196
+ * it is the only place that explains a failure.
1197
+ */
1198
+ /**
1199
+ * The run is in the other process, so this only leaves a note. The dispatcher
1200
+ * sees it when it next renews its claim, which is why the button says it has
1201
+ * been asked for rather than pretending it is already done.
1202
+ */
1203
+ var $$splitComponentImporter$3 = () => import("./loops-xt2Bn7_q.mjs");
1204
+ var Route$11 = createFileRoute("/loops/")({
1205
+ loader: () => getLoopsPage(),
1206
+ component: lazyRouteComponent($$splitComponentImporter$3, "component")
1207
+ });
1208
+ /** Says what a loop still needs, without standing in the way of writing one. */
1209
+ /**
1210
+ * Where this loop writes, in its own words rather than the workflow's. A loop
1211
+ * may have been pointed somewhere else since it was made, and the card is the
1212
+ * one place someone scanning a list would notice.
1213
+ */
1214
+ var $$splitComponentImporter$2 = () => import("../_loopId-BsUL1SeL.mjs");
1215
+ var Route$10 = createFileRoute("/loops/$loopId")({
1216
+ loader: async ({ params }) => {
1217
+ const loop = await getLoopById({ data: { id: params.loopId } });
1218
+ if (!loop) throw notFound();
1219
+ return {
1220
+ loop,
1221
+ tasks: await getLoopTasks({ data: { loopId: params.loopId } }),
1222
+ poll: await getLoopPollState({ data: { id: params.loopId } }),
1223
+ readiness: await getLoopReadiness()
1224
+ };
1225
+ },
1226
+ component: lazyRouteComponent($$splitComponentImporter$2, "component")
1227
+ });
1228
+ /**
1229
+ * Whether the loop is really watching is the one thing a page cannot work out
1230
+ * for itself, and the thing a person most wants to know after turning one on.
1231
+ */
1232
+ /**
1233
+ * A loop that runs by itself still has to be shaped, and waiting for a real
1234
+ * signal to arrive is a slow way to do it. Dry run means that shaping leaves
1235
+ * no marks on a real repository.
1236
+ */
1237
+ var $$splitComponentImporter$1 = () => import("./new-DfKyd2Wa.mjs");
1238
+ var Route$9 = createFileRoute("/loops/new")({
1239
+ validateSearch: (search) => {
1240
+ const result = {};
1241
+ if (typeof search.connector === "string") result.connector = search.connector;
1242
+ if (typeof search.workflow === "string") result.workflow = search.workflow;
1243
+ return result;
1244
+ },
1245
+ loader: async () => ({ loops: (await getLoopsPage()).loops }),
1246
+ component: lazyRouteComponent($$splitComponentImporter$1, "component")
1247
+ });
1248
+ /**
1249
+ * The second step, and the one that makes a loop.
1250
+ *
1251
+ * Nothing has been written down yet: the draft is worked out here from the
1252
+ * workflow the URL names, and the loop begins when the form is saved. Picking
1253
+ * a workflow used to create it, which meant a look around this page left a
1254
+ * live loop behind, enabled and with none of its questions answered.
1255
+ */
1256
+ /**
1257
+ * There is no blank loop to write any more. Every loop is one of the
1258
+ * workflows a connector offers, already knowing its own job, so choosing is
1259
+ * the first half of making one and the settings are the second.
1260
+ */
1261
+ var Route$8 = createFileRoute("/machines/")({ beforeLoad: () => {
1262
+ throw redirect({ to: "/runners" });
1263
+ } });
1264
+ var getRunnersPage = createServerFn({ method: "GET" }).handler(createSsrRpc("45dbbb612763589268984d236edcd17f5868ecf716df9625bfa46e7fec691f4e"));
1265
+ var rotateRunnerJoinToken = createServerFn({ method: "POST" }).handler(createSsrRpc("b4f7a66e1bb69cc14ab84d8f5dc7f85119c9a9b1d065a6b4e51dd0ba84c3f60c"));
1266
+ var removeRunner = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(createSsrRpc("611491b32d9583ccaae637ddf9fb2f842ff75994f50513ecd8273e305e889eb8"));
1267
+ var $$splitComponentImporter = () => import("./runners-DI_NxpHC.mjs");
1268
+ var Route$7 = createFileRoute("/runners/")({
1269
+ loader: () => getRunnersPage(),
1270
+ component: lazyRouteComponent($$splitComponentImporter, "component")
1271
+ });
1272
+ async function claimAgentJob(runnerId) {
1273
+ const row = await getRunner(runnerId);
1274
+ if (!row || !isRunnerOnline(row)) return null;
1275
+ const waiting = await db().select().from(tasks).where(and(eq(tasks.state, "awaiting_agent"), eq(tasks.runnerId, runnerId), or(isNull(tasks.leaseUntil), lte(tasks.leaseUntil, /* @__PURE__ */ new Date())))).all();
1276
+ for (const task of waiting) {
1277
+ const claimed = await db().update(tasks).set({
1278
+ leaseUntil: new Date(Date.now() + 6e4),
1279
+ updatedAt: /* @__PURE__ */ new Date()
1280
+ }).where(and(eq(tasks.id, task.id), eq(tasks.state, "awaiting_agent"))).run();
1281
+ if (rowsChanged(claimed) !== 1) continue;
1282
+ const job = await jobForTask(task.id);
1283
+ if (jobRequiresHost(job) && row.hostname !== hostname()) {
1284
+ await updateTask(task.id, { leaseUntil: null });
1285
+ return null;
1286
+ }
1287
+ return job;
1288
+ }
1289
+ return null;
1290
+ }
1291
+ async function appendAgentLog(taskId, runnerId, chunk) {
1292
+ const task = await taskRow(taskId);
1293
+ if (!task || task.runnerId !== runnerId) throw new Error("This run is not yours.");
1294
+ const next = `${task.logText ?? ""}${chunk}`;
1295
+ if (isHosted()) {
1296
+ await updateTask(taskId, { logText: next });
1297
+ return;
1298
+ }
1299
+ const path = task.logPath ?? join(runDir(taskId), "agent.log");
1300
+ if (!existsSync(path) && !task.logPath) await updateTask(taskId, {
1301
+ logPath: path,
1302
+ logText: next
1303
+ });
1304
+ else await updateTask(taskId, { logText: next });
1305
+ appendFileSync(task.logPath ?? path, chunk);
1306
+ }
1307
+ async function completeAgentJob(taskId, runnerId, result) {
1308
+ const task = await taskRow(taskId);
1309
+ if (!task || task.runnerId !== runnerId) throw new Error("This run is not yours.");
1310
+ await updateTask(taskId, {
1311
+ agentCommand: result.command,
1312
+ leaseUntil: null
1313
+ });
1314
+ if (result.aborted) {
1315
+ await updateTask(taskId, {
1316
+ state: "cancelled",
1317
+ error: "Stopped before it finished."
1318
+ });
1319
+ return;
1320
+ }
1321
+ if (!result.ok) {
1322
+ await updateTask(taskId, {
1323
+ state: "failed",
1324
+ error: result.detail ?? "The agent did not finish."
1325
+ });
1326
+ return;
1327
+ }
1328
+ await absorbAgentOutput(taskId, result.output);
1329
+ }
1330
+ function touchRunner(runnerId, inventory) {
1331
+ return heartbeatRunner(runnerId, inventory);
1332
+ }
1333
+ var Route$6 = createFileRoute("/api/runners/claim")({ server: { handlers: { POST: async ({ request }) => {
1334
+ const runnerId = await runnerIdForToken(bearer(request));
1335
+ if (!runnerId) return Response.json({ error: "Unknown runner." }, { status: 401 });
1336
+ const job = await claimAgentJob(runnerId);
1337
+ return Response.json({ job });
1338
+ } } } });
1339
+ var Route$5 = createFileRoute("/api/runners/heartbeat")({ server: { handlers: { POST: async ({ request }) => {
1340
+ const runnerId = await runnerIdForToken(bearer(request));
1341
+ if (!runnerId) return Response.json({ error: "Unknown runner." }, { status: 401 });
1342
+ const view = await touchRunner(runnerId, (await request.json()).inventory ?? []);
1343
+ return Response.json(view);
1344
+ } } } });
1345
+ var Route$4 = createFileRoute("/api/runners/hello")({ server: { handlers: { POST: async ({ request }) => {
1346
+ try {
1347
+ const body = await request.json();
1348
+ const result = await joinRunner({
1349
+ joinToken: bearer(request),
1350
+ hostname: body.hostname ?? "unknown",
1351
+ inventory: body.inventory ?? []
1352
+ });
1353
+ return Response.json(result);
1354
+ } catch (error) {
1355
+ const message = error instanceof Error ? error.message : String(error);
1356
+ const status = message.includes("join token") ? 401 : 400;
1357
+ return Response.json({ error: message }, { status });
1358
+ }
1359
+ } } } });
1360
+ var Route$3 = createFileRoute("/api/connectors/$connectorId/authorize")({ server: { handlers: { GET: async ({ request, params }) => {
1361
+ const { connectorId } = params;
1362
+ const origin = appOrigin(request.url);
1363
+ const failed = (message) => Response.redirect(`${origin}/connectors?error=${encodeURIComponent(message)}`, 302);
1364
+ const manifest = connectorManifest(connectorId);
1365
+ if (!manifest) return failed(`Unknown connector: ${connectorId}`);
1366
+ if (manifest.auth.kind !== "oauth_redirect") return failed(`${manifest.name} does not use browser authorization.`);
1367
+ try {
1368
+ const runtime = connectorRuntime(connectorId);
1369
+ const readiness = await runtime.readiness();
1370
+ if (!readiness.ready) return failed(readiness.reason);
1371
+ if (!runtime.auth.startAuthorization) return failed(`${manifest.name} cannot start an authorization.`);
1372
+ const state = randomBytes(16).toString("base64url");
1373
+ const redirectUri = callbackUrl(request.url, connectorId);
1374
+ const { redirectUrl, verifier } = await runtime.auth.startAuthorization({
1375
+ redirectUri,
1376
+ state
1377
+ });
1378
+ await startAuthAttempt({
1379
+ connectorId,
1380
+ state,
1381
+ verifier,
1382
+ redirectUri
1383
+ });
1384
+ return Response.redirect(redirectUrl, 302);
1385
+ } catch (error) {
1386
+ return failed(error instanceof Error ? error.message : String(error));
1387
+ }
1388
+ } } } });
1389
+ var Route$2 = createFileRoute("/api/connectors/$connectorId/callback")({ server: { handlers: { GET: async ({ request, params }) => {
1390
+ const origin = appOrigin(request.url);
1391
+ const url = new URL(request.url);
1392
+ const done = (query) => Response.redirect(`${origin}/connectors?${query}`, 302);
1393
+ const failed = (message) => done(`error=${encodeURIComponent(message)}`);
1394
+ const denied = url.searchParams.get("error");
1395
+ if (denied) return failed(url.searchParams.get("error_description") ?? denied);
1396
+ const code = url.searchParams.get("code");
1397
+ const state = url.searchParams.get("state");
1398
+ if (!code || !state) return failed("No authorization code came back.");
1399
+ try {
1400
+ const attempt = await takeAuthAttempt(state);
1401
+ if (attempt.connectorId !== params.connectorId) return failed("This authorization belongs to a different connector.");
1402
+ const runtime = connectorRuntime(attempt.connectorId);
1403
+ if (!runtime.auth.completeAuthorization) return failed("This connector cannot complete a browser authorization.");
1404
+ const result = await runtime.auth.completeAuthorization({
1405
+ code,
1406
+ redirectUri: attempt.redirectUri,
1407
+ verifier: attempt.verifier
1408
+ });
1409
+ const connection = await saveAuthorizedConnection(attempt.connectorId, result);
1410
+ return done(`connected=${encodeURIComponent(connection.accountLabel)}`);
1411
+ } catch (error) {
1412
+ return failed(error instanceof Error ? error.message : String(error));
1413
+ }
1414
+ } } } });
1415
+ var Route$1 = createFileRoute("/api/runners/jobs/$taskId/complete")({ server: { handlers: { POST: async ({ request, params }) => {
1416
+ const runnerId = await runnerIdForToken(bearer(request));
1417
+ if (!runnerId) return Response.json({ error: "Unknown runner." }, { status: 401 });
1418
+ const body = await request.json();
1419
+ try {
1420
+ await completeAgentJob(params.taskId, runnerId, {
1421
+ ok: body.ok !== false,
1422
+ output: body.output ?? "",
1423
+ detail: body.detail,
1424
+ aborted: body.aborted,
1425
+ command: body.command ?? ""
1426
+ });
1427
+ return Response.json({ ok: true });
1428
+ } catch (error) {
1429
+ return Response.json({ error: error instanceof Error ? error.message : String(error) }, { status: 409 });
1430
+ }
1431
+ } } } });
1432
+ var Route = createFileRoute("/api/runners/jobs/$taskId/log")({ server: { handlers: { POST: async ({ request, params }) => {
1433
+ const runnerId = await runnerIdForToken(bearer(request));
1434
+ if (!runnerId) return Response.json({ error: "Unknown runner." }, { status: 401 });
1435
+ const body = await request.json();
1436
+ try {
1437
+ await appendAgentLog(params.taskId, runnerId, body.chunk ?? "");
1438
+ return Response.json({ ok: true });
1439
+ } catch (error) {
1440
+ return Response.json({ error: error instanceof Error ? error.message : String(error) }, { status: 409 });
1441
+ }
1442
+ } } } });
1443
+ var IndexRoute = Route$17.update({
1444
+ id: "/",
1445
+ path: "/",
1446
+ getParentRoute: () => Route$18
1447
+ });
1448
+ var AgentsIndexRoute = Route$16.update({
1449
+ id: "/agents/",
1450
+ path: "/agents/",
1451
+ getParentRoute: () => Route$18
1452
+ });
1453
+ var ConnectorsIndexRoute = Route$15.update({
1454
+ id: "/connectors/",
1455
+ path: "/connectors/",
1456
+ getParentRoute: () => Route$18
1457
+ });
1458
+ var ConnectorsConnectorIdRoute = Route$14.update({
1459
+ id: "/connectors/$connectorId",
1460
+ path: "/connectors/$connectorId",
1461
+ getParentRoute: () => Route$18
1462
+ });
1463
+ var InboxIndexRoute = Route$13.update({
1464
+ id: "/inbox/",
1465
+ path: "/inbox/",
1466
+ getParentRoute: () => Route$18
1467
+ });
1468
+ var InboxTaskIdRoute = Route$12.update({
1469
+ id: "/inbox/$taskId",
1470
+ path: "/inbox/$taskId",
1471
+ getParentRoute: () => Route$18
1472
+ });
1473
+ var LoopsIndexRoute = Route$11.update({
1474
+ id: "/loops/",
1475
+ path: "/loops/",
1476
+ getParentRoute: () => Route$18
1477
+ });
1478
+ var rootRouteChildren = {
1479
+ IndexRoute,
1480
+ ConnectorsConnectorIdRoute,
1481
+ InboxTaskIdRoute,
1482
+ LoopsLoopIdRoute: Route$10.update({
1483
+ id: "/loops/$loopId",
1484
+ path: "/loops/$loopId",
1485
+ getParentRoute: () => Route$18
1486
+ }),
1487
+ LoopsNewRoute: Route$9.update({
1488
+ id: "/loops/new",
1489
+ path: "/loops/new",
1490
+ getParentRoute: () => Route$18
1491
+ }),
1492
+ AgentsIndexRoute,
1493
+ ConnectorsIndexRoute,
1494
+ InboxIndexRoute,
1495
+ LoopsIndexRoute,
1496
+ MachinesIndexRoute: Route$8.update({
1497
+ id: "/machines/",
1498
+ path: "/machines/",
1499
+ getParentRoute: () => Route$18
1500
+ }),
1501
+ RunnersIndexRoute: Route$7.update({
1502
+ id: "/runners/",
1503
+ path: "/runners/",
1504
+ getParentRoute: () => Route$18
1505
+ }),
1506
+ ApiRunnersClaimRoute: Route$6.update({
1507
+ id: "/api/runners/claim",
1508
+ path: "/api/runners/claim",
1509
+ getParentRoute: () => Route$18
1510
+ }),
1511
+ ApiRunnersHeartbeatRoute: Route$5.update({
1512
+ id: "/api/runners/heartbeat",
1513
+ path: "/api/runners/heartbeat",
1514
+ getParentRoute: () => Route$18
1515
+ }),
1516
+ ApiRunnersHelloRoute: Route$4.update({
1517
+ id: "/api/runners/hello",
1518
+ path: "/api/runners/hello",
1519
+ getParentRoute: () => Route$18
1520
+ }),
1521
+ ApiConnectorsConnectorIdAuthorizeRoute: Route$3.update({
1522
+ id: "/api/connectors/$connectorId/authorize",
1523
+ path: "/api/connectors/$connectorId/authorize",
1524
+ getParentRoute: () => Route$18
1525
+ }),
1526
+ ApiConnectorsConnectorIdCallbackRoute: Route$2.update({
1527
+ id: "/api/connectors/$connectorId/callback",
1528
+ path: "/api/connectors/$connectorId/callback",
1529
+ getParentRoute: () => Route$18
1530
+ }),
1531
+ ApiRunnersJobsTaskIdCompleteRoute: Route$1.update({
1532
+ id: "/api/runners/jobs/$taskId/complete",
1533
+ path: "/api/runners/jobs/$taskId/complete",
1534
+ getParentRoute: () => Route$18
1535
+ }),
1536
+ ApiRunnersJobsTaskIdLogRoute: Route.update({
1537
+ id: "/api/runners/jobs/$taskId/log",
1538
+ path: "/api/runners/jobs/$taskId/log",
1539
+ getParentRoute: () => Route$18
1540
+ })
1541
+ };
1542
+ var routeTree = Route$18._addFileChildren(rootRouteChildren)._addFileTypes();
1543
+ var router_exports = /* @__PURE__ */ __exportAll({ getRouter: () => getRouter });
1544
+ function getRouter() {
1545
+ return createRouter({
1546
+ routeTree,
1547
+ scrollRestoration: true,
1548
+ defaultPreload: "intent",
1549
+ defaultPreloadStaleTime: 0
1550
+ });
1551
+ }
1552
+ //#endregion
1553
+ export { stopTask as A, CardTitle as B, Button$1 as C, saveLoop as D, runLoopBacklog as E, verifyConnection as F, Badge as I, Card as L, continueQrLogin as M, disconnectConnection as N, toggleLoop as O, saveConnectionSettings as P, CardContent as R, Input$1 as S, reorderLoop as T, cn 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, 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, CardHeader as z };