loopable-cli 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.output/nitro.json +1 -1
- package/.output/public/assets/_connectorId-CDCuSgpN.js +1 -0
- package/.output/public/assets/_loopId-DOnIsVTW.js +1 -0
- package/.output/public/assets/_taskId-BO5MZnlZ.js +1 -0
- package/.output/public/assets/agents-CBozi7oi.js +1 -0
- package/.output/public/assets/alert-Bn_wjgTX.js +1 -0
- package/.output/public/assets/button-K3ypIhAZ.js +1 -0
- package/.output/public/assets/card-DiVnaTIE.js +1 -0
- package/.output/public/assets/chevron-right-BrctLtFK.js +1 -0
- package/.output/public/assets/connection-status-DKWelvLO.js +1 -0
- package/.output/public/assets/connector-icon-CEbdE71G.js +1 -0
- package/.output/public/assets/connectors-JyX3IzJj.js +1 -0
- package/.output/public/assets/gaps-PjmcbaWo.js +1 -0
- package/.output/public/assets/inbox-CujRR0Cz.js +1 -0
- package/.output/public/assets/index-C9SQ_Baq.js +16 -0
- package/.output/public/assets/loop-form-Dw3cuTzw.js +1 -0
- package/.output/public/assets/loops-DcEfZa2_.js +1 -0
- package/.output/public/assets/manifests-yJpevyye.js +1 -0
- package/.output/public/assets/new-CwsnNuRW.js +1 -0
- package/.output/public/assets/page-bvnfRMum.js +1 -0
- package/.output/public/assets/play-yHLs4DjK.js +1 -0
- package/.output/public/assets/plus-Ber25lrU.js +1 -0
- package/.output/public/assets/preload-helper-BoU_7EQC.js +3 -0
- package/.output/public/assets/refresh-cw-BzSY7M8I.js +1 -0
- package/.output/public/assets/rotate-ccw-DLv9EfCX.js +1 -0
- package/.output/public/assets/runners-DzX-RU3i.js +2 -0
- package/.output/public/assets/select-C9grIFwG.js +1 -0
- package/.output/public/assets/setting-fields-DUc3npgh.js +3 -0
- package/.output/public/assets/styles-B9B98zLk.css +2 -0
- package/.output/public/assets/switch-DwABVI6d.js +1 -0
- package/.output/public/assets/task-list-Ddk8-qt7.js +1 -0
- package/.output/public/assets/textarea-Ds-gIYUQ.js +1 -0
- package/.output/public/assets/time-goBWd-5J.js +1 -0
- package/.output/public/assets/use-live-tasks-DZNNuECy.js +1 -0
- package/.output/public/assets/useCompositeListItem-P4Nmmn7j.js +1 -0
- package/.output/public/assets/usePositioner-B7i9Ejzh.js +1 -0
- package/.output/server/_chunks/ssr-renderer.mjs +1 -1
- package/.output/server/_connectorId-COuQN02a.mjs +512 -0
- package/.output/server/_libs/@base-ui/react+[...].mjs +1410 -804
- package/.output/server/_libs/@tanstack/react-router+[...].mjs +1 -1
- package/.output/server/_libs/lucide-react.mjs +93 -84
- package/.output/server/_libs/next-themes.mjs +1 -1
- package/.output/server/_libs/sonner.mjs +1 -1
- package/.output/server/_loopId-CZh6_Yhr.mjs +310 -0
- package/.output/server/_ssr/{callback-D20cVD2w.mjs → access-DTttr563.mjs} +37 -20
- package/.output/server/_ssr/{agents-CrUmIQpi.mjs → agents-B_qo1gpL.mjs} +196 -7
- package/.output/server/_ssr/{agents-Dms_6TBF.mjs → agents-Bz10gz_m.mjs} +8 -8
- package/.output/server/_ssr/agents-CjbOUp3Z.mjs +206 -0
- package/.output/server/_ssr/{alert-DQvzE7bu.mjs → alert-C2WBWfzo.mjs} +2 -2
- package/.output/server/_ssr/card-r1cV-PCN.mjs +36 -0
- package/.output/server/_ssr/{client-BtVRVhQ2.mjs → client-Dk3SD8Il.mjs} +5 -1
- package/.output/server/_ssr/connection-status-BogAL-Fj.mjs +32 -0
- package/.output/server/_ssr/{connections-ypex3qUd.mjs → connections-Cqvb6Gw2.mjs} +1109 -263
- package/.output/server/_ssr/connector-icon-DrhVbhs6.mjs +190 -0
- package/.output/server/_ssr/connectors-BfS5tQKr.mjs +81 -0
- package/.output/server/_ssr/{connectors-BZly0yGF.mjs → connectors-DJI-rzPU.mjs} +21 -10
- package/.output/server/_ssr/{createServerRpc-DPDrnFdH.mjs → createServerRpc-DDrp2nLT.mjs} +2 -2
- package/.output/server/_ssr/{gaps-D2Zh8sEo.mjs → gaps-CcvHiLcj.mjs} +18 -10
- package/.output/server/_ssr/inbox-D1ef761v.mjs +178 -0
- package/.output/server/_ssr/loop-form-DWwOR05R.mjs +346 -0
- package/.output/server/_ssr/{loops-Og_G3KyZ.mjs → loops-6WhUe_lc.mjs} +228 -25
- package/.output/server/_ssr/loops-DHPjzG-t.mjs +217 -0
- package/.output/server/_ssr/new-ChKBdcXJ.mjs +245 -0
- package/.output/server/_ssr/page-DZVxKvdn.mjs +120 -0
- package/.output/server/_ssr/{router-BbrBz36P.mjs → router-DdwREiOf.mjs} +167 -489
- package/.output/server/_ssr/runners-CK9Cx9fS.mjs +217 -0
- package/.output/server/_ssr/runners-CrznFPHt.mjs +140 -0
- package/.output/server/_ssr/runners-W-pgo5yo.mjs +55 -0
- package/.output/server/_ssr/{secrets-CRnpwGBh.mjs → secrets-CV5scFlt.mjs} +2 -2
- package/.output/server/_ssr/select-BtndHhHb.mjs +76 -0
- package/.output/server/_ssr/{setting-fields-mpXfGftI.mjs → setting-fields-LNgywAW7.mjs} +25 -20
- package/.output/server/_ssr/ssr.mjs +49 -40
- package/.output/server/_ssr/{start-BgTj-h_C.mjs → start-C_j-e4vF.mjs} +10 -6
- package/.output/server/_ssr/{switch-1pnd_bPJ.mjs → switch-odxNO_S4.mjs} +3 -3
- package/.output/server/_ssr/task-list-Dnmq_WeE.mjs +150 -0
- package/.output/server/_ssr/{tasks-LIi-W4cy.mjs → tasks-D-kXLgVP.mjs} +13 -13
- package/.output/server/_ssr/{tasks-B_QmDFVs.mjs → tasks-D7aUvA33.mjs} +55 -17
- package/.output/server/_ssr/{textarea-CHJTKtsJ.mjs → textarea-D2c_vTIC.mjs} +2 -2
- package/.output/server/_ssr/time-Cy8TZP0Z.mjs +13 -0
- package/.output/server/_ssr/use-live-tasks-BzhTkEOL.mjs +40 -0
- package/.output/server/_tanstack-start-manifest_v-2HUdn-2E.mjs +166 -0
- package/.output/server/_taskId-D3KkNJW1.mjs +263 -0
- package/.output/server/index.mjs +204 -162
- package/README.md +54 -19
- package/dist/cli.mjs +185 -67
- package/dist/dispatcher.mjs +1443 -277
- package/dist/runner.mjs +333 -46
- package/drizzle/0025_prompt_tasks_done.sql +2 -0
- package/drizzle/0026_guidance_into_prompt.sql +3 -0
- package/drizzle/0027_relative_folders.sql +5 -0
- package/drizzle/0028_runner_version.sql +5 -0
- package/drizzle/meta/_journal.json +29 -1
- package/package.json +23 -17
- package/.output/public/assets/_connectorId-m67WbiJ8.js +0 -1
- package/.output/public/assets/_loopId-Bf4K5sdd.js +0 -1
- package/.output/public/assets/_taskId-eCKkcVOx.js +0 -1
- package/.output/public/assets/alert-BT2EVMJP.js +0 -1
- package/.output/public/assets/arrow-left-CWUZt6k2.js +0 -1
- package/.output/public/assets/arrow-right-dy_oOg_c.js +0 -1
- package/.output/public/assets/connection-status-DCqqmY_6.js +0 -1
- package/.output/public/assets/connector-icon-qGpNsPgH.js +0 -1
- package/.output/public/assets/connectors-DxXlc9zF.js +0 -1
- package/.output/public/assets/external-link-DC11orFA.js +0 -1
- package/.output/public/assets/gaps-CoTA-PzA.js +0 -1
- package/.output/public/assets/inbox-CLEXUXCI.js +0 -1
- package/.output/public/assets/index-Cndsw84p.js +0 -16
- package/.output/public/assets/loader-B4Epchm_.js +0 -1
- package/.output/public/assets/loop-form-BvizlZdR.js +0 -1
- package/.output/public/assets/loops-Dbe_mf5T.js +0 -1
- package/.output/public/assets/new-DPEaXKAY.js +0 -1
- package/.output/public/assets/play-DD4xQgvi.js +0 -1
- package/.output/public/assets/preload-helper-CfdAYKD7.js +0 -3
- package/.output/public/assets/rolldown-runtime-CbXtAM7H.js +0 -1
- package/.output/public/assets/runners-BVBggS5B.js +0 -1
- package/.output/public/assets/select-Cppu-hti.js +0 -1
- package/.output/public/assets/setting-fields-BT1naVJX.js +0 -3
- package/.output/public/assets/styles-D3sonCfX.css +0 -2
- package/.output/public/assets/switch-hRIbNGOj.js +0 -1
- package/.output/public/assets/task-list-CrptnI1N.js +0 -1
- package/.output/public/assets/textarea-Fyuzi6Vp.js +0 -1
- package/.output/public/assets/trash-2-BXZzBsoV.js +0 -1
- package/.output/public/assets/useValueChanged-VVyq4hb9.js +0 -1
- package/.output/server/_connectorId-DcSnoRjk.mjs +0 -421
- package/.output/server/_loopId-G_TAkYtS.mjs +0 -272
- package/.output/server/_ssr/access-B7TcOZ7F.mjs +0 -42
- package/.output/server/_ssr/connection-status-BZr3Y-IC.mjs +0 -31
- package/.output/server/_ssr/connector-icon-CFrrHjzR.mjs +0 -37
- package/.output/server/_ssr/connectors-4yzKb0KG.mjs +0 -106
- package/.output/server/_ssr/inbox-C2VBGLa5.mjs +0 -114
- package/.output/server/_ssr/loop-form-D-U-kU4k.mjs +0 -413
- package/.output/server/_ssr/loops-D0XJ8eBb.mjs +0 -222
- package/.output/server/_ssr/new-CajvgIog.mjs +0 -169
- package/.output/server/_ssr/runners-B_WuF8UJ.mjs +0 -32
- package/.output/server/_ssr/runners-D-0_lfiq.mjs +0 -106
- package/.output/server/_ssr/runners-zqF_zbEc.mjs +0 -116
- package/.output/server/_ssr/task-list-BnQJ94Dk.mjs +0 -187
- package/.output/server/_tanstack-start-manifest_v-BCHAazE8.mjs +0 -129
- package/.output/server/_taskId-CIv0ajIx.mjs +0 -256
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { f as eq } from "../_libs/drizzle-orm.mjs";
|
|
2
|
+
import { f as runners, g as tasks, s as db } from "./client-Dk3SD8Il.mjs";
|
|
3
|
+
import { a as runnerTokenKey, o as secretStore, r as joinTokenKey } from "./secrets-CV5scFlt.mjs";
|
|
4
|
+
import { randomBytes, randomUUID } from "node:crypto";
|
|
5
|
+
import { hostname } from "node:os";
|
|
6
|
+
//#region node_modules/.nitro/vite/services/ssr/assets/runners-CK9Cx9fS.js
|
|
7
|
+
/** This build's version, as published to npm. */
|
|
8
|
+
var VERSION = {
|
|
9
|
+
name: "loopable-cli",
|
|
10
|
+
version: "0.2.1",
|
|
11
|
+
description: "CLI to run Loopable: App, Dispatcher, and Runner.",
|
|
12
|
+
license: "MIT",
|
|
13
|
+
type: "module",
|
|
14
|
+
bin: { "loopable": "./dist/cli.mjs" },
|
|
15
|
+
files: [
|
|
16
|
+
".output",
|
|
17
|
+
"dist",
|
|
18
|
+
"drizzle"
|
|
19
|
+
],
|
|
20
|
+
engines: { "node": ">=22" },
|
|
21
|
+
packageManager: "pnpm@11.22.0",
|
|
22
|
+
repository: {
|
|
23
|
+
"type": "git",
|
|
24
|
+
"url": "git+https://github.com/imsobear/loopable.git"
|
|
25
|
+
},
|
|
26
|
+
bugs: { "url": "https://github.com/imsobear/loopable/issues" },
|
|
27
|
+
homepage: "https://github.com/imsobear/loopable#readme",
|
|
28
|
+
publishConfig: { "access": "public" },
|
|
29
|
+
imports: { "#/*": "./src/*" },
|
|
30
|
+
scripts: {
|
|
31
|
+
"dev": "node --experimental-strip-types scripts/dev.ts",
|
|
32
|
+
"start": "node dist/cli.mjs start",
|
|
33
|
+
"dispatcher": "LOOPABLE_DEV=1 node --experimental-strip-types src/cli.ts dispatcher",
|
|
34
|
+
"runner": "LOOPABLE_DEV=1 node --experimental-strip-types src/cli.ts runner",
|
|
35
|
+
"generate-routes": "tsr generate",
|
|
36
|
+
"build": "vite build && node scripts/build-cli.mjs",
|
|
37
|
+
"preview": "vite preview",
|
|
38
|
+
"test": "vitest run",
|
|
39
|
+
"test:watch": "vitest",
|
|
40
|
+
"db:generate": "drizzle-kit generate",
|
|
41
|
+
"db:studio": "LOOPABLE_DEV=1 drizzle-kit studio",
|
|
42
|
+
"typecheck": "tsc --noEmit",
|
|
43
|
+
"release": "node scripts/release.mjs",
|
|
44
|
+
"deploy": "LOOPABLE_CF=1 vite build && wrangler deploy",
|
|
45
|
+
"site:dev": "pnpm --filter loopable-site dev",
|
|
46
|
+
"site:build": "pnpm --filter loopable-site build",
|
|
47
|
+
"site:deploy": "pnpm --filter loopable-site deploy",
|
|
48
|
+
"prepublishOnly": "pnpm typecheck && pnpm test && pnpm build"
|
|
49
|
+
},
|
|
50
|
+
dependencies: { "@libsql/client": "^0.18.0" },
|
|
51
|
+
devDependencies: {
|
|
52
|
+
"@base-ui/react": "^1.7.0",
|
|
53
|
+
"@cloudflare/containers": "^0.3.7",
|
|
54
|
+
"@cloudflare/vite-plugin": "^1.54.10",
|
|
55
|
+
"@fontsource-variable/geist": "^5.3.0",
|
|
56
|
+
"@tailwindcss/vite": "4.1.18",
|
|
57
|
+
"@tanstack/devtools-vite": "0.8.5",
|
|
58
|
+
"@tanstack/react-devtools": "0.10.12",
|
|
59
|
+
"@tanstack/react-router": "1.170.32",
|
|
60
|
+
"@tanstack/react-router-devtools": "1.167.1",
|
|
61
|
+
"@tanstack/react-start": "1.168.49",
|
|
62
|
+
"@tanstack/router-cli": "1.167.33",
|
|
63
|
+
"@types/node": "^22.10.2",
|
|
64
|
+
"@types/qrcode": "^1.5.6",
|
|
65
|
+
"@types/react": "^19.2.0",
|
|
66
|
+
"@types/react-dom": "^19.2.0",
|
|
67
|
+
"@vitejs/plugin-react": "^6.0.1",
|
|
68
|
+
"class-variance-authority": "^0.7.1",
|
|
69
|
+
"clsx": "^2.1.1",
|
|
70
|
+
"drizzle-kit": "^0.31.10",
|
|
71
|
+
"drizzle-orm": "^0.45.2",
|
|
72
|
+
"esbuild": "^0.28.2",
|
|
73
|
+
"lucide-react": "^1.39.0",
|
|
74
|
+
"next-themes": "^0.4.6",
|
|
75
|
+
"nitro": "3.0.260610-beta",
|
|
76
|
+
"qrcode": "^1.5.4",
|
|
77
|
+
"react": "19.2.0",
|
|
78
|
+
"react-dom": "19.2.0",
|
|
79
|
+
"shadcn": "^4.19.1",
|
|
80
|
+
"sonner": "^2.0.8",
|
|
81
|
+
"tailwind-merge": "^3.6.0",
|
|
82
|
+
"tailwindcss": "4.1.18",
|
|
83
|
+
"tw-animate-css": "^1.4.0",
|
|
84
|
+
"typescript": "^6.0.2",
|
|
85
|
+
"vite": "^8.0.0",
|
|
86
|
+
"vitest": "^4.1.11",
|
|
87
|
+
"wrangler": "^4.132.0"
|
|
88
|
+
}
|
|
89
|
+
}.version;
|
|
90
|
+
/** Whether `a` is an older x.y.z than `b`. Anything unreadable counts as older. */
|
|
91
|
+
function olderThan(a, b) {
|
|
92
|
+
if (!a) return true;
|
|
93
|
+
const pa = a.split(".").map((part) => Number.parseInt(part, 10));
|
|
94
|
+
const pb = b.split(".").map((part) => Number.parseInt(part, 10));
|
|
95
|
+
for (let i = 0; i < 3; i++) {
|
|
96
|
+
const x = pa[i] ?? 0;
|
|
97
|
+
const y = pb[i] ?? 0;
|
|
98
|
+
if (Number.isNaN(x)) return true;
|
|
99
|
+
if (x !== y) return x < y;
|
|
100
|
+
}
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
var ONLINE_MS = 45e3;
|
|
104
|
+
function protocolOf(row) {
|
|
105
|
+
return row.protocol ?? 1;
|
|
106
|
+
}
|
|
107
|
+
function buildFields(build) {
|
|
108
|
+
return {
|
|
109
|
+
version: typeof build.version === "string" && build.version ? build.version : null,
|
|
110
|
+
protocol: typeof build.protocol === "number" ? build.protocol : null
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
function isRunnerOnline(row, now = Date.now()) {
|
|
114
|
+
if (row.status !== "online") return false;
|
|
115
|
+
if (!row.lastSeenAt) return false;
|
|
116
|
+
return now - row.lastSeenAt.getTime() < ONLINE_MS;
|
|
117
|
+
}
|
|
118
|
+
function toView(row, now = Date.now()) {
|
|
119
|
+
const online = isRunnerOnline(row, now);
|
|
120
|
+
return {
|
|
121
|
+
id: row.id,
|
|
122
|
+
name: row.name,
|
|
123
|
+
hostname: row.hostname,
|
|
124
|
+
status: online ? "online" : "offline",
|
|
125
|
+
inventory: row.inventory,
|
|
126
|
+
lastSeenAt: row.lastSeenAt?.toISOString() ?? null,
|
|
127
|
+
version: row.version,
|
|
128
|
+
protocol: protocolOf(row),
|
|
129
|
+
outdated: protocolOf(row) < 2 || olderThan(row.version, VERSION)
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
async function listRunners() {
|
|
133
|
+
await db().delete(runners).where(eq(runners.id, "host")).run();
|
|
134
|
+
const now = Date.now();
|
|
135
|
+
return (await db().select().from(runners).all()).map((row) => toView(row, now));
|
|
136
|
+
}
|
|
137
|
+
async function getRunner(id) {
|
|
138
|
+
return await db().select().from(runners).where(eq(runners.id, id)).get();
|
|
139
|
+
}
|
|
140
|
+
async function onlineRunnerCount() {
|
|
141
|
+
return (await listRunners()).filter((row) => row.status === "online").length;
|
|
142
|
+
}
|
|
143
|
+
/** Agent ids signed in on an online runner. */
|
|
144
|
+
async function availableAgentIds(requiresHost = false, minProtocol = 0) {
|
|
145
|
+
const local = hostname();
|
|
146
|
+
return [...new Set((await listRunners()).filter((row) => row.status === "online" && (!requiresHost || row.hostname === local) && row.protocol >= minProtocol).flatMap((row) => row.inventory.filter((entry) => entry.installed && entry.signedIn).map((entry) => entry.agentId)))];
|
|
147
|
+
}
|
|
148
|
+
async function getJoinToken() {
|
|
149
|
+
const existing = await secretStore().get(joinTokenKey());
|
|
150
|
+
if (existing) return existing;
|
|
151
|
+
const token = randomBytes(24).toString("base64url");
|
|
152
|
+
await secretStore().set(joinTokenKey(), token);
|
|
153
|
+
return token;
|
|
154
|
+
}
|
|
155
|
+
async function rotateJoinToken() {
|
|
156
|
+
const token = randomBytes(24).toString("base64url");
|
|
157
|
+
await secretStore().set(joinTokenKey(), token);
|
|
158
|
+
return token;
|
|
159
|
+
}
|
|
160
|
+
async function heartbeatRunner(runnerId, inventory, build = {}) {
|
|
161
|
+
if (!await getRunner(runnerId)) throw new Error("Unknown runner.");
|
|
162
|
+
await db().update(runners).set({
|
|
163
|
+
status: "online",
|
|
164
|
+
inventory,
|
|
165
|
+
lastSeenAt: /* @__PURE__ */ new Date(),
|
|
166
|
+
...buildFields(build)
|
|
167
|
+
}).where(eq(runners.id, runnerId)).run();
|
|
168
|
+
return toView(await getRunner(runnerId));
|
|
169
|
+
}
|
|
170
|
+
async function joinRunner(input) {
|
|
171
|
+
const expected = await getJoinToken();
|
|
172
|
+
if (!input.joinToken || input.joinToken !== expected) throw new Error("That join token is not valid.");
|
|
173
|
+
const existing = (await db().select().from(runners).all()).find((row) => row.hostname === input.hostname);
|
|
174
|
+
const id = existing?.id ?? randomUUID();
|
|
175
|
+
const name = existing?.name ?? input.hostname;
|
|
176
|
+
const now = /* @__PURE__ */ new Date();
|
|
177
|
+
if (existing) await db().update(runners).set({
|
|
178
|
+
status: "online",
|
|
179
|
+
inventory: input.inventory,
|
|
180
|
+
lastSeenAt: now,
|
|
181
|
+
hostname: input.hostname,
|
|
182
|
+
...buildFields(input)
|
|
183
|
+
}).where(eq(runners.id, id)).run();
|
|
184
|
+
else await db().insert(runners).values({
|
|
185
|
+
id,
|
|
186
|
+
name,
|
|
187
|
+
hostname: input.hostname,
|
|
188
|
+
status: "online",
|
|
189
|
+
inventory: input.inventory,
|
|
190
|
+
lastSeenAt: now,
|
|
191
|
+
...buildFields(input)
|
|
192
|
+
}).run();
|
|
193
|
+
const token = randomBytes(24).toString("base64url");
|
|
194
|
+
await secretStore().set(runnerTokenKey(id), token);
|
|
195
|
+
return {
|
|
196
|
+
runnerId: id,
|
|
197
|
+
runnerToken: token,
|
|
198
|
+
name
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
async function runnerIdForToken(token) {
|
|
202
|
+
if (!token) return null;
|
|
203
|
+
for (const row of await db().select().from(runners).all()) {
|
|
204
|
+
const stored = await secretStore().get(runnerTokenKey(row.id)) ?? await secretStore().get(`runners.machine.${row.id}`);
|
|
205
|
+
if (stored && stored === token) return row.id;
|
|
206
|
+
}
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
async function forgetRunner(id) {
|
|
210
|
+
if (!await getRunner(id)) throw new Error("Unknown runner.");
|
|
211
|
+
if ((await db().select().from(tasks).all()).some((task) => task.runnerId === id && task.state === "awaiting_agent")) throw new Error("This runner still has a run in flight.");
|
|
212
|
+
await db().delete(runners).where(eq(runners.id, id)).run();
|
|
213
|
+
await secretStore().delete(runnerTokenKey(id));
|
|
214
|
+
await secretStore().delete(`runners.machine.${id}`);
|
|
215
|
+
}
|
|
216
|
+
//#endregion
|
|
217
|
+
export { getRunner as a, joinRunner as c, protocolOf as d, rotateJoinToken as f, getJoinToken as i, listRunners as l, availableAgentIds as n, heartbeatRunner as o, runnerIdForToken as p, forgetRunner as r, isRunnerOnline as s, VERSION as t, onlineRunnerCount as u };
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { K as require_jsx_runtime } from "../_libs/@base-ui/react+[...].mjs";
|
|
2
|
+
import { m as useRouter } from "../_libs/@tanstack/react-router+[...].mjs";
|
|
3
|
+
import { t as VERSION } from "./runners-CK9Cx9fS.mjs";
|
|
4
|
+
import { n as agentManifest } from "./agents-B_qo1gpL.mjs";
|
|
5
|
+
import { o as Server, s as RotateCcw, w as Copy } from "../_libs/lucide-react.mjs";
|
|
6
|
+
import { n as toast } from "../_libs/sonner.mjs";
|
|
7
|
+
import { P as cn, i as rotateRunnerJoinToken, n as Route$7, r as removeRunner, v as Button$1 } from "./router-DdwREiOf.mjs";
|
|
8
|
+
import { a as List, o as PageHeader, t as Empty } from "./page-DZVxKvdn.mjs";
|
|
9
|
+
import { n as useLiveRefresh } from "./use-live-tasks-BzhTkEOL.mjs";
|
|
10
|
+
//#region node_modules/.nitro/vite/services/ssr/assets/runners-CrznFPHt.js
|
|
11
|
+
var import_jsx_runtime = require_jsx_runtime();
|
|
12
|
+
/** Which agents this runner has, in a few words. */
|
|
13
|
+
function agentsOn(runner) {
|
|
14
|
+
if (runner.inventory.length === 0) return "No agents found";
|
|
15
|
+
return runner.inventory.map((entry) => {
|
|
16
|
+
const name = agentManifest(entry.agentId)?.name ?? entry.agentId;
|
|
17
|
+
return entry.signedIn ? name : entry.installed ? `${name} (not signed in)` : `${name} (missing)`;
|
|
18
|
+
}).join(", ");
|
|
19
|
+
}
|
|
20
|
+
function RunnersPage() {
|
|
21
|
+
const { runners, joinToken, origin } = Route$7.useLoaderData();
|
|
22
|
+
const router = useRouter();
|
|
23
|
+
useLiveRefresh();
|
|
24
|
+
const commands = ["npm install -g loopable-cli", `loopable runner --url ${origin} --token ${joinToken}`].join("\n");
|
|
25
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
26
|
+
className: "flex flex-col gap-6",
|
|
27
|
+
children: [
|
|
28
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(PageHeader, {
|
|
29
|
+
title: "Runners",
|
|
30
|
+
description: `Machines that run agents for your loops. This App is v${VERSION}.`
|
|
31
|
+
}),
|
|
32
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
33
|
+
className: "flex flex-col gap-2",
|
|
34
|
+
children: [
|
|
35
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
36
|
+
className: "flex items-center justify-between gap-3",
|
|
37
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", {
|
|
38
|
+
className: "text-sm font-medium",
|
|
39
|
+
children: "Join a runner"
|
|
40
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button$1, {
|
|
41
|
+
variant: "ghost",
|
|
42
|
+
size: "sm",
|
|
43
|
+
onClick: async () => {
|
|
44
|
+
if (!window.confirm("Rotate the join token? Runners already joined keep working.")) return;
|
|
45
|
+
try {
|
|
46
|
+
await rotateRunnerJoinToken();
|
|
47
|
+
await router.invalidate();
|
|
48
|
+
toast.success("Join token rotated");
|
|
49
|
+
} catch (error) {
|
|
50
|
+
toast.error(error instanceof Error ? error.message : String(error));
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(RotateCcw, {}), "Rotate token"]
|
|
54
|
+
})]
|
|
55
|
+
}),
|
|
56
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
57
|
+
className: "flex items-start gap-2 rounded-xl bg-muted p-1 pl-3",
|
|
58
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("pre", {
|
|
59
|
+
className: "min-w-0 flex-1 overflow-x-auto py-1.5 text-xs leading-relaxed",
|
|
60
|
+
children: commands
|
|
61
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
|
|
62
|
+
variant: "ghost",
|
|
63
|
+
size: "icon-sm",
|
|
64
|
+
"aria-label": "Copy",
|
|
65
|
+
onClick: async () => {
|
|
66
|
+
await navigator.clipboard.writeText(commands);
|
|
67
|
+
toast.success("Copied");
|
|
68
|
+
},
|
|
69
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Copy, {})
|
|
70
|
+
})]
|
|
71
|
+
}),
|
|
72
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
73
|
+
className: "text-xs text-muted-foreground",
|
|
74
|
+
children: "Needs Node 22+ and an agent CLI signed in on that machine. Skip the first line if the CLI is already installed."
|
|
75
|
+
})
|
|
76
|
+
]
|
|
77
|
+
}),
|
|
78
|
+
runners.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Empty, { children: "No runner has joined yet. Tasks wait until one is online." }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(List, { children: runners.map((runner) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
79
|
+
className: "flex items-center gap-3 px-4 py-3",
|
|
80
|
+
children: [
|
|
81
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
82
|
+
className: "flex size-8 shrink-0 items-center justify-center rounded-md bg-muted text-muted-foreground",
|
|
83
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Server, { className: "size-4" })
|
|
84
|
+
}),
|
|
85
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
86
|
+
className: "min-w-0 flex-1",
|
|
87
|
+
children: [
|
|
88
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
89
|
+
className: "flex items-center gap-2",
|
|
90
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
91
|
+
className: "truncate text-sm font-medium",
|
|
92
|
+
children: runner.name
|
|
93
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
|
|
94
|
+
className: "flex items-center gap-1.5 text-xs text-muted-foreground",
|
|
95
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: cn("size-1.5 rounded-full", runner.status === "online" ? "bg-emerald-500" : "bg-muted-foreground/50") }), runner.status === "online" ? "Online" : "Offline"]
|
|
96
|
+
})]
|
|
97
|
+
}),
|
|
98
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", {
|
|
99
|
+
className: "mt-0.5 truncate text-xs text-muted-foreground",
|
|
100
|
+
children: [
|
|
101
|
+
runner.hostname,
|
|
102
|
+
" · ",
|
|
103
|
+
runner.version ? `v${runner.version}` : "version unknown",
|
|
104
|
+
" ·",
|
|
105
|
+
" ",
|
|
106
|
+
agentsOn(runner)
|
|
107
|
+
]
|
|
108
|
+
}),
|
|
109
|
+
runner.outdated ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", {
|
|
110
|
+
className: "mt-1 text-xs text-amber-700 dark:text-amber-500",
|
|
111
|
+
children: [
|
|
112
|
+
"Older than this App (v",
|
|
113
|
+
VERSION,
|
|
114
|
+
"), so it may not take every job. On that machine, run ",
|
|
115
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { children: "npm install -g loopable-cli" }),
|
|
116
|
+
" and restart the runner."
|
|
117
|
+
]
|
|
118
|
+
}) : null
|
|
119
|
+
]
|
|
120
|
+
}),
|
|
121
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
|
|
122
|
+
variant: "ghost",
|
|
123
|
+
size: "sm",
|
|
124
|
+
onClick: async () => {
|
|
125
|
+
try {
|
|
126
|
+
await removeRunner({ data: { id: runner.id } });
|
|
127
|
+
await router.invalidate();
|
|
128
|
+
} catch (error) {
|
|
129
|
+
toast.error(error instanceof Error ? error.message : String(error));
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
children: "Forget"
|
|
133
|
+
})
|
|
134
|
+
]
|
|
135
|
+
}, runner.id)) })
|
|
136
|
+
]
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
//#endregion
|
|
140
|
+
export { RunnersPage as component };
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { r as createServerFn } from "./ssr.mjs";
|
|
2
|
+
import { f as rotateJoinToken, i as getJoinToken, l as listRunners, r as forgetRunner } from "./runners-CK9Cx9fS.mjs";
|
|
3
|
+
import { t as createServerRpc } from "./createServerRpc-DDrp2nLT.mjs";
|
|
4
|
+
import { networkInterfaces } from "node:os";
|
|
5
|
+
//#region node_modules/.nitro/vite/services/ssr/assets/runners-W-pgo5yo.js
|
|
6
|
+
function configuredOrigin() {
|
|
7
|
+
return process.env.LOOPABLE_BASE_URL?.replace(/\/$/, "") || null;
|
|
8
|
+
}
|
|
9
|
+
/** First non-internal IPv4, preferring typical LAN ranges. */
|
|
10
|
+
function lanIPv4(nics = networkInterfaces()) {
|
|
11
|
+
const found = [];
|
|
12
|
+
for (const addrs of Object.values(nics)) for (const addr of addrs ?? []) {
|
|
13
|
+
if (addr.internal) continue;
|
|
14
|
+
if (!["IPv4", "4"].includes(String(addr.family))) continue;
|
|
15
|
+
found.push(addr.address);
|
|
16
|
+
}
|
|
17
|
+
return found.find((ip) => ip.startsWith("192.168.")) ?? found.find((ip) => ip.startsWith("10.")) ?? found.find((ip) => {
|
|
18
|
+
const b = Number(ip.split(".")[1]);
|
|
19
|
+
return ip.startsWith("172.") && b >= 16 && b <= 31;
|
|
20
|
+
}) ?? found[0] ?? null;
|
|
21
|
+
}
|
|
22
|
+
/** Where runners should address this Loopable. A LAN IP when one exists. */
|
|
23
|
+
function loopableOrigin(nics = networkInterfaces()) {
|
|
24
|
+
const configured = configuredOrigin();
|
|
25
|
+
if (configured) return configured;
|
|
26
|
+
const port = process.env.PORT ?? process.env.NITRO_PORT ?? "4321";
|
|
27
|
+
return `http://${lanIPv4(nics) ?? "127.0.0.1"}:${port}`;
|
|
28
|
+
}
|
|
29
|
+
var getRunnersPage_createServerFn_handler = createServerRpc({
|
|
30
|
+
id: "45dbbb612763589268984d236edcd17f5868ecf716df9625bfa46e7fec691f4e",
|
|
31
|
+
name: "getRunnersPage",
|
|
32
|
+
filename: "src/server/functions/runners.ts"
|
|
33
|
+
}, (opts) => getRunnersPage.__executeServer(opts));
|
|
34
|
+
var getRunnersPage = createServerFn({ method: "GET" }).handler(getRunnersPage_createServerFn_handler, async () => ({
|
|
35
|
+
runners: await listRunners(),
|
|
36
|
+
joinToken: await getJoinToken(),
|
|
37
|
+
origin: loopableOrigin()
|
|
38
|
+
}));
|
|
39
|
+
var rotateRunnerJoinToken_createServerFn_handler = createServerRpc({
|
|
40
|
+
id: "b4f7a66e1bb69cc14ab84d8f5dc7f85119c9a9b1d065a6b4e51dd0ba84c3f60c",
|
|
41
|
+
name: "rotateRunnerJoinToken",
|
|
42
|
+
filename: "src/server/functions/runners.ts"
|
|
43
|
+
}, (opts) => rotateRunnerJoinToken.__executeServer(opts));
|
|
44
|
+
var rotateRunnerJoinToken = createServerFn({ method: "POST" }).handler(rotateRunnerJoinToken_createServerFn_handler, async () => await rotateJoinToken());
|
|
45
|
+
var removeRunner_createServerFn_handler = createServerRpc({
|
|
46
|
+
id: "611491b32d9583ccaae637ddf9fb2f842ff75994f50513ecd8273e305e889eb8",
|
|
47
|
+
name: "removeRunner",
|
|
48
|
+
filename: "src/server/functions/runners.ts"
|
|
49
|
+
}, (opts) => removeRunner.__executeServer(opts));
|
|
50
|
+
var removeRunner = createServerFn({ method: "POST" }).validator((data) => data).handler(removeRunner_createServerFn_handler, async ({ data }) => {
|
|
51
|
+
await forgetRunner(data.id);
|
|
52
|
+
return await listRunners();
|
|
53
|
+
});
|
|
54
|
+
//#endregion
|
|
55
|
+
export { getRunnersPage_createServerFn_handler, removeRunner_createServerFn_handler, rotateRunnerJoinToken_createServerFn_handler };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { f as eq } from "../_libs/drizzle-orm.mjs";
|
|
2
|
-
import { m as secrets, o as dataDir, p as secretNamespace, s as db } from "./client-
|
|
2
|
+
import { m as secrets, o as dataDir, p as secretNamespace, s as db } from "./client-Dk3SD8Il.mjs";
|
|
3
3
|
import { chmodSync, existsSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { execFileSync } from "node:child_process";
|
|
6
|
-
//#region node_modules/.nitro/vite/services/ssr/assets/secrets-
|
|
6
|
+
//#region node_modules/.nitro/vite/services/ssr/assets/secrets-CV5scFlt.js
|
|
7
7
|
/**
|
|
8
8
|
* Hosted Loopable shares a network database between the App Worker and the
|
|
9
9
|
* Dispatcher. A local laptop still uses a file, and needs none of this.
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import "../_runtime.mjs";
|
|
2
|
+
import { C as SelectValue$1, K as require_jsx_runtime, S as SelectIcon, T as SelectRoot, _ as SelectItem$1, b as SelectPositioner, g as SelectItemIndicator, h as SelectItemText, m as SelectScrollDownArrow, p as SelectScrollUpArrow, q as require_react, v as SelectList, w as SelectTrigger$1, x as SelectPortal, y as SelectPopup } from "../_libs/@base-ui/react+[...].mjs";
|
|
3
|
+
import { L as ChevronDown, P as ChevronUp, R as Check } from "../_libs/lucide-react.mjs";
|
|
4
|
+
import { P as cn } from "./router-DdwREiOf.mjs";
|
|
5
|
+
require_react();
|
|
6
|
+
var import_jsx_runtime = require_jsx_runtime();
|
|
7
|
+
var Select$1 = SelectRoot;
|
|
8
|
+
function SelectValue({ className, ...props }) {
|
|
9
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectValue$1, {
|
|
10
|
+
"data-slot": "select-value",
|
|
11
|
+
className: cn("flex flex-1 text-left", className),
|
|
12
|
+
...props
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
function SelectTrigger({ className, size = "default", children, ...props }) {
|
|
16
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectTrigger$1, {
|
|
17
|
+
"data-slot": "select-trigger",
|
|
18
|
+
"data-size": size,
|
|
19
|
+
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),
|
|
20
|
+
...props,
|
|
21
|
+
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" }) })]
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
function SelectContent({ className, children, side = "bottom", sideOffset = 4, align = "center", alignOffset = 0, alignItemWithTrigger = true, ...props }) {
|
|
25
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectPortal, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectPositioner, {
|
|
26
|
+
side,
|
|
27
|
+
sideOffset,
|
|
28
|
+
align,
|
|
29
|
+
alignOffset,
|
|
30
|
+
alignItemWithTrigger,
|
|
31
|
+
className: "isolate z-50",
|
|
32
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectPopup, {
|
|
33
|
+
"data-slot": "select-content",
|
|
34
|
+
"data-align-trigger": alignItemWithTrigger,
|
|
35
|
+
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),
|
|
36
|
+
...props,
|
|
37
|
+
children: [
|
|
38
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectScrollUpButton, {}),
|
|
39
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectList, { children }),
|
|
40
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectScrollDownButton, {})
|
|
41
|
+
]
|
|
42
|
+
})
|
|
43
|
+
}) });
|
|
44
|
+
}
|
|
45
|
+
function SelectItem({ className, children, ...props }) {
|
|
46
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectItem$1, {
|
|
47
|
+
"data-slot": "select-item",
|
|
48
|
+
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),
|
|
49
|
+
...props,
|
|
50
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItemText, {
|
|
51
|
+
className: "flex flex-1 shrink-0 gap-2 whitespace-nowrap",
|
|
52
|
+
children
|
|
53
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItemIndicator, {
|
|
54
|
+
render: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "pointer-events-none absolute right-2 flex size-4 items-center justify-center" }),
|
|
55
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Check, { className: "pointer-events-none" })
|
|
56
|
+
})]
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
function SelectScrollUpButton({ className, ...props }) {
|
|
60
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectScrollUpArrow, {
|
|
61
|
+
"data-slot": "select-scroll-up-button",
|
|
62
|
+
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),
|
|
63
|
+
...props,
|
|
64
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronUp, {})
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
function SelectScrollDownButton({ className, ...props }) {
|
|
68
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectScrollDownArrow, {
|
|
69
|
+
"data-slot": "select-scroll-down-button",
|
|
70
|
+
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),
|
|
71
|
+
...props,
|
|
72
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronDown, {})
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
//#endregion
|
|
76
|
+
export { SelectValue as a, SelectTrigger as i, SelectContent as n, SelectItem as r, Select$1 as t };
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { t as
|
|
5
|
-
|
|
1
|
+
import { K as require_jsx_runtime } from "../_libs/@base-ui/react+[...].mjs";
|
|
2
|
+
import { g as Input$1 } from "./router-DdwREiOf.mjs";
|
|
3
|
+
import { r as Label } from "./page-DZVxKvdn.mjs";
|
|
4
|
+
import { a as SelectValue, i as SelectTrigger, n as SelectContent, r as SelectItem, t as Select$1 } from "./select-BtndHhHb.mjs";
|
|
5
|
+
import { t as Switch$1 } from "./switch-odxNO_S4.mjs";
|
|
6
|
+
import { t as Textarea } from "./textarea-D2c_vTIC.mjs";
|
|
7
|
+
//#region node_modules/.nitro/vite/services/ssr/assets/setting-fields-LNgywAW7.js
|
|
6
8
|
var import_jsx_runtime = require_jsx_runtime();
|
|
7
9
|
/**
|
|
8
10
|
* One renderer for anything a connector declares as a field, so connection
|
|
@@ -31,16 +33,19 @@ function SettingFieldInputs({ fields, values, onChange, idPrefix = "" }) {
|
|
|
31
33
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: fields.map((field) => {
|
|
32
34
|
const id = `${idPrefix}${field.key}`;
|
|
33
35
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
34
|
-
className: "flex flex-col gap-
|
|
36
|
+
className: "flex flex-col gap-1.5",
|
|
35
37
|
children: field.kind === "boolean" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
36
38
|
className: "flex items-center justify-between gap-4",
|
|
37
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
38
|
-
|
|
39
|
-
children:
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
40
|
+
className: "flex flex-col gap-0.5",
|
|
41
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Label, {
|
|
42
|
+
htmlFor: id,
|
|
43
|
+
children: field.label
|
|
44
|
+
}), field.help ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
45
|
+
className: "text-xs text-muted-foreground",
|
|
46
|
+
children: field.help
|
|
47
|
+
}) : null]
|
|
48
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Switch$1, {
|
|
44
49
|
id,
|
|
45
50
|
checked: Boolean(values[field.key]),
|
|
46
51
|
onCheckedChange: (checked) => onChange(field.key, checked)
|
|
@@ -50,13 +55,9 @@ function SettingFieldInputs({ fields, values, onChange, idPrefix = "" }) {
|
|
|
50
55
|
htmlFor: id,
|
|
51
56
|
children: field.label
|
|
52
57
|
}),
|
|
53
|
-
field.help ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
54
|
-
className: "text-xs text-muted-foreground",
|
|
55
|
-
children: field.help
|
|
56
|
-
}) : null,
|
|
57
58
|
field.kind === "string_list" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Textarea, {
|
|
58
59
|
id,
|
|
59
|
-
rows:
|
|
60
|
+
rows: 3,
|
|
60
61
|
placeholder: field.placeholder,
|
|
61
62
|
value: (values[field.key] ?? []).join("\n"),
|
|
62
63
|
onChange: (event) => onChange(field.key, event.target.value.split("\n").map((line) => line.trim()).filter(Boolean))
|
|
@@ -65,7 +66,7 @@ function SettingFieldInputs({ fields, values, onChange, idPrefix = "" }) {
|
|
|
65
66
|
onValueChange: (value) => onChange(field.key, value),
|
|
66
67
|
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectTrigger, {
|
|
67
68
|
id,
|
|
68
|
-
className: "w-full",
|
|
69
|
+
className: "w-full sm:max-w-xs",
|
|
69
70
|
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectValue, { children: (value) => field.options.find((option) => option.value === value)?.label ?? value })
|
|
70
71
|
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectContent, { children: field.options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
|
|
71
72
|
value: option.value,
|
|
@@ -76,7 +77,11 @@ function SettingFieldInputs({ fields, values, onChange, idPrefix = "" }) {
|
|
|
76
77
|
placeholder: field.placeholder,
|
|
77
78
|
value: values[field.key],
|
|
78
79
|
onChange: (event) => onChange(field.key, event.target.value)
|
|
79
|
-
})
|
|
80
|
+
}),
|
|
81
|
+
field.help ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
82
|
+
className: "text-xs text-muted-foreground",
|
|
83
|
+
children: field.help
|
|
84
|
+
}) : null
|
|
80
85
|
] })
|
|
81
86
|
}, field.key);
|
|
82
87
|
}) });
|