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.
- package/.output/nitro.json +17 -0
- package/.output/public/apple-touch-icon.png +0 -0
- package/.output/public/assets/_connectorId-BWjYn5tm.js +1 -0
- package/.output/public/assets/_loopId-Ds4RkyEc.js +1 -0
- package/.output/public/assets/_taskId-BOzICXDf.js +1 -0
- package/.output/public/assets/alert-Bn9-Zm0l.js +1 -0
- package/.output/public/assets/arrow-left-BF9juV9T.js +1 -0
- package/.output/public/assets/arrow-right-CzqvnOY1.js +1 -0
- package/.output/public/assets/button-HCY6DCY-.js +1 -0
- package/.output/public/assets/connection-status-CYZB8kLy.js +1 -0
- package/.output/public/assets/connector-icon-BZ_MUJV_.js +1 -0
- package/.output/public/assets/connectors-BvzttfSu.js +1 -0
- package/.output/public/assets/connectors-DdJNwRN_.js +1 -0
- package/.output/public/assets/external-link-D46X-bXa.js +1 -0
- package/.output/public/assets/gaps-D-I6xiDc.js +1 -0
- package/.output/public/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
- package/.output/public/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
- package/.output/public/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
- package/.output/public/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
- package/.output/public/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
- package/.output/public/assets/inbox-BUUu9xxE.js +1 -0
- package/.output/public/assets/index-ztzex0FN.js +10 -0
- package/.output/public/assets/loader-CcBl2u8m.js +1 -0
- package/.output/public/assets/loop-form-dB3CLcZb.js +1 -0
- package/.output/public/assets/loops-DRunQrxL.js +1 -0
- package/.output/public/assets/loops-oSdqMd29.js +1 -0
- package/.output/public/assets/manifests-DNMqf7Vi.js +7 -0
- package/.output/public/assets/new-uyFxVjuB.js +1 -0
- package/.output/public/assets/preload-helper-ORbuV86A.js +3 -0
- package/.output/public/assets/runners-DSU3r5Ww.js +1 -0
- package/.output/public/assets/setting-fields-ixkpdBb-.js +3 -0
- package/.output/public/assets/styles-DZL5V1o7.css +2 -0
- package/.output/public/assets/switch-d5ummmHY.js +1 -0
- package/.output/public/assets/task-list-BpOStt9v.js +1 -0
- package/.output/public/assets/tasks-B8MsKgic.js +1 -0
- package/.output/public/assets/trash-2-D6u6NpzO.js +1 -0
- package/.output/public/favicon.svg +9 -0
- package/.output/server/_chunks/ssr-renderer.mjs +26 -0
- package/.output/server/_connectorId-8nb5yOzG.mjs +421 -0
- package/.output/server/_libs/@base-ui/react+[...].mjs +12841 -0
- package/.output/server/_libs/@tanstack/react-router+[...].mjs +14349 -0
- package/.output/server/_libs/class-variance-authority+clsx.mjs +69 -0
- package/.output/server/_libs/dijkstrajs.mjs +118 -0
- package/.output/server/_libs/drizzle-orm.mjs +4349 -0
- package/.output/server/_libs/h3+rou3+srvx.mjs +1361 -0
- package/.output/server/_libs/lucide-react.mjs +911 -0
- package/.output/server/_libs/next-themes.mjs +53 -0
- package/.output/server/_libs/pngjs.mjs +1680 -0
- package/.output/server/_libs/qrcode.mjs +2583 -0
- package/.output/server/_libs/sonner.mjs +955 -0
- package/.output/server/_libs/tailwind-merge.mjs +3380 -0
- package/.output/server/_libs/tanstack__history.mjs +341 -0
- package/.output/server/_libs/ufo.mjs +64 -0
- package/.output/server/_loopId-BsUL1SeL.mjs +272 -0
- package/.output/server/_runtime.mjs +35 -0
- package/.output/server/_ssr/access-zIl15o8y.mjs +42 -0
- package/.output/server/_ssr/agents-Btql102J.mjs +657 -0
- package/.output/server/_ssr/agents-DAOZPgO2.mjs +76 -0
- package/.output/server/_ssr/alert-NEiOCTOl.mjs +37 -0
- package/.output/server/_ssr/callback-CUINvl7M.mjs +62 -0
- package/.output/server/_ssr/client-CKkOfh-s.mjs +335 -0
- package/.output/server/_ssr/connection-status-Bi5peUAk.mjs +31 -0
- package/.output/server/_ssr/connections-qnw2GVlP.mjs +2542 -0
- package/.output/server/_ssr/connector-icon-DZXQLLFo.mjs +37 -0
- package/.output/server/_ssr/connectors-BHNwuIMj.mjs +106 -0
- package/.output/server/_ssr/connectors-C28g0i4c.mjs +83 -0
- package/.output/server/_ssr/createServerRpc-DPDrnFdH.mjs +12 -0
- package/.output/server/_ssr/empty-plugin-adapters-BJ4m9L8g.mjs +5 -0
- package/.output/server/_ssr/gaps-RAFSXLZf.mjs +53 -0
- package/.output/server/_ssr/inbox-k5zHergU.mjs +35 -0
- package/.output/server/_ssr/loop-form-DlObSewZ.mjs +412 -0
- package/.output/server/_ssr/loops-APFcP0y6.mjs +311 -0
- package/.output/server/_ssr/loops-xt2Bn7_q.mjs +222 -0
- package/.output/server/_ssr/new-DfKyd2Wa.mjs +169 -0
- package/.output/server/_ssr/router-3WmpE5b_.mjs +1553 -0
- package/.output/server/_ssr/runners-By-mwc3F.mjs +32 -0
- package/.output/server/_ssr/runners-DI_NxpHC.mjs +116 -0
- package/.output/server/_ssr/runners-DtXs4Q8a.mjs +106 -0
- package/.output/server/_ssr/secrets-Hqti-niS.mjs +199 -0
- package/.output/server/_ssr/setting-fields-8vKFj4df.mjs +92 -0
- package/.output/server/_ssr/ssr.mjs +6605 -0
- package/.output/server/_ssr/start-Bdc2GJUI.mjs +51 -0
- package/.output/server/_ssr/switch-ReNglgH6.mjs +18 -0
- package/.output/server/_ssr/task-list-CUITIqkb.mjs +186 -0
- package/.output/server/_ssr/tasks-SAjNovX6.mjs +110 -0
- package/.output/server/_ssr/tasks-vcoodp_D.mjs +533 -0
- package/.output/server/_tanstack-start-manifest_v-C0cP5F0M.mjs +125 -0
- package/.output/server/_taskId-CXS3lod0.mjs +249 -0
- package/.output/server/index.mjs +541 -0
- package/README.md +81 -0
- package/dist/cli.mjs +165 -0
- package/dist/dispatcher.mjs +9871 -0
- package/dist/runner.mjs +657 -0
- package/drizzle/0000_cold_demogoblin.sql +29 -0
- package/drizzle/0001_fancy_blonde_phantom.sql +13 -0
- package/drizzle/0002_spooky_smiling_tiger.sql +16 -0
- package/drizzle/0003_small_golden_guardian.sql +25 -0
- package/drizzle/0004_wonderful_thunderbolts.sql +39 -0
- package/drizzle/0005_named_workflows.sql +7 -0
- package/drizzle/0006_signals.sql +18 -0
- package/drizzle/0007_review_comments.sql +1 -0
- package/drizzle/0008_held_signals.sql +1 -0
- package/drizzle/0009_work_item_ref.sql +58 -0
- package/drizzle/0010_signal_payload.sql +2 -0
- package/drizzle/0011_rule_poll_cursor.sql +1 -0
- package/drizzle/0012_loops.sql +63 -0
- package/drizzle/0013_loop_owns_action_and_prompt.sql +154 -0
- package/drizzle/0014_loop_poll_every.sql +4 -0
- package/drizzle/0015_machines.sql +10 -0
- package/drizzle/0016_task_machine.sql +1 -0
- package/drizzle/0017_loop_machine.sql +1 -0
- package/drizzle/0018_runners.sql +1 -0
- package/drizzle/0019_task_runner.sql +1 -0
- package/drizzle/0020_loop_runner.sql +1 -0
- package/drizzle/0021_drop_runner_kind.sql +1 -0
- package/drizzle/0022_drop_loop_runner.sql +1 -0
- package/drizzle/0023_shared_host.sql +6 -0
- package/drizzle/meta/0000_snapshot.json +206 -0
- package/drizzle/meta/0001_snapshot.json +284 -0
- package/drizzle/meta/0002_snapshot.json +390 -0
- package/drizzle/meta/0003_snapshot.json +566 -0
- package/drizzle/meta/0004_snapshot.json +633 -0
- package/drizzle/meta/0005_snapshot.json +626 -0
- package/drizzle/meta/0006_snapshot.json +752 -0
- package/drizzle/meta/0007_snapshot.json +759 -0
- package/drizzle/meta/0008_snapshot.json +766 -0
- package/drizzle/meta/0009_snapshot.json +752 -0
- package/drizzle/meta/0010_snapshot.json +766 -0
- package/drizzle/meta/0011_snapshot.json +773 -0
- package/drizzle/meta/0012_snapshot.json +773 -0
- package/drizzle/meta/0013_snapshot.json +817 -0
- package/drizzle/meta/_journal.json +174 -0
- 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 };
|