loopable-cli 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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-C3xBnKfy.js → refresh-cw-BzSY7M8I.js} +1 -1
- 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-C9yydJrC.mjs → _connectorId-COuQN02a.mjs} +198 -188
- 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/{agents-DG643yoJ.mjs → agents-B_qo1gpL.mjs} +195 -6
- package/.output/server/_ssr/{agents-C7B6RJ6-.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-Df-aDpfJ.mjs → connections-Cqvb6Gw2.mjs} +372 -325
- package/.output/server/_ssr/connector-icon-DrhVbhs6.mjs +190 -0
- package/.output/server/_ssr/connectors-BfS5tQKr.mjs +81 -0
- package/.output/server/_ssr/{connectors-e0mf2pMs.mjs → connectors-DJI-rzPU.mjs} +10 -10
- package/.output/server/_ssr/{createServerRpc-DPDrnFdH.mjs → createServerRpc-DDrp2nLT.mjs} +2 -2
- package/.output/server/_ssr/{gaps-Cz76ZicM.mjs → gaps-CcvHiLcj.mjs} +15 -7
- package/.output/server/_ssr/inbox-D1ef761v.mjs +178 -0
- package/.output/server/_ssr/loop-form-DWwOR05R.mjs +346 -0
- package/.output/server/_ssr/{loops-D8WuK7Ke.mjs → loops-6WhUe_lc.mjs} +37 -27
- 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-54IDWTVz.mjs → router-DdwREiOf.mjs} +162 -489
- package/.output/server/_ssr/runners-CK9Cx9fS.mjs +217 -0
- package/.output/server/_ssr/runners-CrznFPHt.mjs +140 -0
- package/.output/server/_ssr/{runners-Cj6z9Mqh.mjs → runners-W-pgo5yo.mjs} +5 -5
- 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-DzBlslan.mjs → setting-fields-LNgywAW7.mjs} +25 -20
- package/.output/server/_ssr/ssr.mjs +43 -42
- package/.output/server/_ssr/{start-Vq3Oan3v.mjs → start-C_j-e4vF.mjs} +9 -4
- 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-Qtml5TlK.mjs → tasks-D-kXLgVP.mjs} +13 -13
- package/.output/server/_ssr/{tasks-BDqznm57.mjs → tasks-D7aUvA33.mjs} +44 -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 +199 -164
- package/README.md +36 -6
- package/dist/cli.mjs +161 -8
- package/dist/dispatcher.mjs +671 -285
- 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 +4 -2
- package/.output/public/assets/_connectorId-CkygNLny.js +0 -1
- package/.output/public/assets/_loopId-Cshlc5Ja.js +0 -1
- package/.output/public/assets/_taskId-BKDIJ_Fg.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-BUoK6Cru.js +0 -1
- package/.output/public/assets/connectors-CeLBtEtg.js +0 -1
- package/.output/public/assets/external-link-DC11orFA.js +0 -1
- package/.output/public/assets/gaps-BtFoD7mF.js +0 -1
- package/.output/public/assets/inbox-BYNjkNDJ.js +0 -1
- package/.output/public/assets/index-0yP3Jn_G.js +0 -18
- package/.output/public/assets/loader-B4Epchm_.js +0 -1
- package/.output/public/assets/loop-form-DnSJNc1k.js +0 -1
- package/.output/public/assets/loops-1CvbqO6m.js +0 -1
- package/.output/public/assets/new-m5NlqYQT.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-D8QVl-Y6.js +0 -1
- package/.output/public/assets/select-Cppu-hti.js +0 -1
- package/.output/public/assets/setting-fields-bJ_gTG7Q.js +0 -3
- package/.output/public/assets/styles-ClTGaXU0.css +0 -2
- package/.output/public/assets/switch-hRIbNGOj.js +0 -1
- package/.output/public/assets/task-list-BAxjcVgr.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/_loopId-B0VG-eBC.mjs +0 -301
- package/.output/server/_ssr/connection-status-BZr3Y-IC.mjs +0 -31
- package/.output/server/_ssr/connector-icon-WvxwZ5TF.mjs +0 -56
- package/.output/server/_ssr/connectors-BU0IiRge.mjs +0 -107
- package/.output/server/_ssr/inbox-CAUBa1I3.mjs +0 -114
- package/.output/server/_ssr/loop-form-FqSaXiHl.mjs +0 -413
- package/.output/server/_ssr/loops-Dqoebe8q.mjs +0 -222
- package/.output/server/_ssr/new-bBsVaWD6.mjs +0 -169
- package/.output/server/_ssr/runners-D2UAdLyj.mjs +0 -106
- package/.output/server/_ssr/runners-DGWaVvWi.mjs +0 -116
- package/.output/server/_ssr/task-list-_qgBS9LU.mjs +0 -187
- package/.output/server/_tanstack-start-manifest_v-CvheE7ZF.mjs +0 -131
- package/.output/server/_taskId-V4QLBO2N.mjs +0 -256
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
import { U as require_jsx_runtime } from "../_libs/@base-ui/react+[...].mjs";
|
|
2
|
-
import { f as Link, p as useNavigate } from "../_libs/@tanstack/react-router+[...].mjs";
|
|
3
|
-
import { c as connectorWorkflow, n as allWorkflows, o as connectorManifest } from "./connections-Df-aDpfJ.mjs";
|
|
4
|
-
import { B as ArrowLeft, z as ArrowRight } from "../_libs/lucide-react.mjs";
|
|
5
|
-
import { B as Card, R as Badge, V as CardContent, a as Route$9, z as Button$1 } from "./router-54IDWTVz.mjs";
|
|
6
|
-
import { t as ConnectorIcon } from "./connector-icon-WvxwZ5TF.mjs";
|
|
7
|
-
import { t as LoopForm } from "./loop-form-FqSaXiHl.mjs";
|
|
8
|
-
//#region node_modules/.nitro/vite/services/ssr/assets/new-bBsVaWD6.js
|
|
9
|
-
var import_jsx_runtime = require_jsx_runtime();
|
|
10
|
-
/**
|
|
11
|
-
* One of a connector's workflows, as a loop nobody has saved yet.
|
|
12
|
-
*
|
|
13
|
-
* Nothing here touches the database, which is the point: choosing a workflow
|
|
14
|
-
* used to create the loop, so a look around the page left a live loop behind,
|
|
15
|
-
* enabled and with none of its questions answered. Now choosing only fills a
|
|
16
|
-
* form in, and the loop begins at Save.
|
|
17
|
-
*
|
|
18
|
-
* Deliberately client-safe, so the form can be filled in without a round trip
|
|
19
|
-
* and without anything on the server having to remember a half-made loop.
|
|
20
|
-
*/
|
|
21
|
-
function draftForWorkflow(connectorId, workflowId) {
|
|
22
|
-
const workflow = connectorWorkflow(connectorId, workflowId);
|
|
23
|
-
if (!workflow) throw new Error(`Unknown workflow: ${workflowId}`);
|
|
24
|
-
return {
|
|
25
|
-
id: null,
|
|
26
|
-
name: workflow.name,
|
|
27
|
-
connectorId,
|
|
28
|
-
workflowId,
|
|
29
|
-
prompt: workflow.prompt,
|
|
30
|
-
guidance: null,
|
|
31
|
-
agentId: null,
|
|
32
|
-
settings: {},
|
|
33
|
-
actionConnectorId: workflow.actionConnectorId ?? connectorId,
|
|
34
|
-
actionId: workflow.actionId,
|
|
35
|
-
actionTarget: workflow.actionTarget ?? {},
|
|
36
|
-
pollEveryMs: workflow.pollEveryMs ?? null,
|
|
37
|
-
enabled: true
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
function NewLoopPage() {
|
|
41
|
-
const { connector, workflow } = Route$9.useSearch();
|
|
42
|
-
const chosen = connector && workflow && connectorWorkflow(connector, workflow) ? {
|
|
43
|
-
connector,
|
|
44
|
-
workflow
|
|
45
|
-
} : null;
|
|
46
|
-
return chosen ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ShapeLoop, {
|
|
47
|
-
connectorId: chosen.connector,
|
|
48
|
-
workflowId: chosen.workflow
|
|
49
|
-
}) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChooseWorkflow, {});
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* The second step, and the one that makes a loop.
|
|
53
|
-
*
|
|
54
|
-
* Nothing has been written down yet: the draft is worked out here from the
|
|
55
|
-
* workflow the URL names, and the loop begins when the form is saved. Picking
|
|
56
|
-
* a workflow used to create it, which meant a look around this page left a
|
|
57
|
-
* live loop behind, enabled and with none of its questions answered.
|
|
58
|
-
*/
|
|
59
|
-
function ShapeLoop({ connectorId, workflowId }) {
|
|
60
|
-
const workflow = connectorWorkflow(connectorId, workflowId);
|
|
61
|
-
const connector = connectorManifest(connectorId);
|
|
62
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
63
|
-
className: "flex flex-col gap-6",
|
|
64
|
-
children: [
|
|
65
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Link, {
|
|
66
|
-
to: "/loops/new",
|
|
67
|
-
className: "flex items-center gap-1.5 text-sm text-muted-foreground hover:text-foreground",
|
|
68
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ArrowLeft, { className: "size-4" }), "New loop"]
|
|
69
|
-
}),
|
|
70
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", {
|
|
71
|
-
className: "flex items-start gap-4",
|
|
72
|
-
children: [connector ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ConnectorIcon, {
|
|
73
|
-
icon: connector.icon,
|
|
74
|
-
accent: connector.accent
|
|
75
|
-
}) : null, /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", {
|
|
76
|
-
className: "text-2xl font-semibold tracking-tight",
|
|
77
|
-
children: workflow.name
|
|
78
|
-
}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", {
|
|
79
|
-
className: "mt-1 text-sm text-muted-foreground",
|
|
80
|
-
children: ["Nothing is watched for until you add this. ", workflow.summary]
|
|
81
|
-
})] })]
|
|
82
|
-
}),
|
|
83
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(LoopForm, { loop: draftForWorkflow(connectorId, workflowId) })
|
|
84
|
-
]
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
/**
|
|
88
|
-
* There is no blank loop to write any more. Every loop is one of the
|
|
89
|
-
* workflows a connector offers, already knowing its own job, so choosing is
|
|
90
|
-
* the first half of making one and the settings are the second.
|
|
91
|
-
*/
|
|
92
|
-
function ChooseWorkflow() {
|
|
93
|
-
const { loops } = Route$9.useLoaderData();
|
|
94
|
-
const navigate = useNavigate();
|
|
95
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
96
|
-
className: "flex flex-col gap-6",
|
|
97
|
-
children: [
|
|
98
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Link, {
|
|
99
|
-
to: "/loops",
|
|
100
|
-
className: "flex items-center gap-1.5 text-sm text-muted-foreground hover:text-foreground",
|
|
101
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ArrowLeft, { className: "size-4" }), "Loops"]
|
|
102
|
-
}),
|
|
103
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", {
|
|
104
|
-
className: "text-2xl font-semibold tracking-tight",
|
|
105
|
-
children: "New loop"
|
|
106
|
-
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
107
|
-
className: "mt-1 text-sm text-muted-foreground",
|
|
108
|
-
children: "Pick what you want done. You can narrow it down to certain repositories, and add anything particular to your team, on the next screen."
|
|
109
|
-
})] }),
|
|
110
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
111
|
-
className: "flex flex-col gap-3",
|
|
112
|
-
children: allWorkflows().map(({ connector, workflow }) => {
|
|
113
|
-
const existing = loops.filter((loop) => loop.workflowId === workflow.id).length;
|
|
114
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Card, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(CardContent, {
|
|
115
|
-
className: "flex items-start gap-4 pt-6",
|
|
116
|
-
children: [
|
|
117
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ConnectorIcon, {
|
|
118
|
-
icon: connector.icon,
|
|
119
|
-
accent: connector.accent
|
|
120
|
-
}),
|
|
121
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
122
|
-
className: "flex min-w-0 flex-1 flex-col gap-1",
|
|
123
|
-
children: [
|
|
124
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
125
|
-
className: "flex flex-wrap items-center gap-2",
|
|
126
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
127
|
-
className: "font-medium",
|
|
128
|
-
children: workflow.name
|
|
129
|
-
}), existing > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Badge, {
|
|
130
|
-
variant: "secondary",
|
|
131
|
-
children: existing === 1 ? "Already have one" : `Already have ${existing}`
|
|
132
|
-
}) : null]
|
|
133
|
-
}),
|
|
134
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
135
|
-
className: "text-sm text-muted-foreground",
|
|
136
|
-
children: workflow.summary
|
|
137
|
-
}),
|
|
138
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", {
|
|
139
|
-
className: "mt-1 flex items-start gap-1.5 text-xs text-muted-foreground",
|
|
140
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ArrowRight, { className: "mt-0.5 size-3.5 shrink-0" }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
|
|
141
|
-
"When ",
|
|
142
|
-
workflow.trigger,
|
|
143
|
-
", it writes ",
|
|
144
|
-
workflow.writes,
|
|
145
|
-
"."
|
|
146
|
-
] })]
|
|
147
|
-
})
|
|
148
|
-
]
|
|
149
|
-
}),
|
|
150
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
|
|
151
|
-
variant: "outline",
|
|
152
|
-
onClick: () => void navigate({
|
|
153
|
-
to: "/loops/new",
|
|
154
|
-
search: {
|
|
155
|
-
connector: connector.id,
|
|
156
|
-
workflow: workflow.id
|
|
157
|
-
}
|
|
158
|
-
}),
|
|
159
|
-
children: "Set up"
|
|
160
|
-
})
|
|
161
|
-
]
|
|
162
|
-
}) }, workflow.id);
|
|
163
|
-
})
|
|
164
|
-
})
|
|
165
|
-
]
|
|
166
|
-
});
|
|
167
|
-
}
|
|
168
|
-
//#endregion
|
|
169
|
-
export { NewLoopPage as component };
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
import { f as eq } from "../_libs/drizzle-orm.mjs";
|
|
2
|
-
import { f as runners, g as tasks, s as db } from "./client-BtVRVhQ2.mjs";
|
|
3
|
-
import { a as runnerTokenKey, o as secretStore, r as joinTokenKey } from "./secrets-CRnpwGBh.mjs";
|
|
4
|
-
import { randomBytes, randomUUID } from "node:crypto";
|
|
5
|
-
import { hostname } from "node:os";
|
|
6
|
-
//#region node_modules/.nitro/vite/services/ssr/assets/runners-D2UAdLyj.js
|
|
7
|
-
var ONLINE_MS = 45e3;
|
|
8
|
-
function isRunnerOnline(row, now = Date.now()) {
|
|
9
|
-
if (row.status !== "online") return false;
|
|
10
|
-
if (!row.lastSeenAt) return false;
|
|
11
|
-
return now - row.lastSeenAt.getTime() < ONLINE_MS;
|
|
12
|
-
}
|
|
13
|
-
function toView(row, now = Date.now()) {
|
|
14
|
-
const online = isRunnerOnline(row, now);
|
|
15
|
-
return {
|
|
16
|
-
id: row.id,
|
|
17
|
-
name: row.name,
|
|
18
|
-
hostname: row.hostname,
|
|
19
|
-
status: online ? "online" : "offline",
|
|
20
|
-
inventory: row.inventory,
|
|
21
|
-
lastSeenAt: row.lastSeenAt?.toISOString() ?? null
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
async function listRunners() {
|
|
25
|
-
await db().delete(runners).where(eq(runners.id, "host")).run();
|
|
26
|
-
const now = Date.now();
|
|
27
|
-
return (await db().select().from(runners).all()).map((row) => toView(row, now));
|
|
28
|
-
}
|
|
29
|
-
async function getRunner(id) {
|
|
30
|
-
return await db().select().from(runners).where(eq(runners.id, id)).get();
|
|
31
|
-
}
|
|
32
|
-
async function onlineRunnerCount() {
|
|
33
|
-
return (await listRunners()).filter((row) => row.status === "online").length;
|
|
34
|
-
}
|
|
35
|
-
/** Agent ids signed in on an online runner. */
|
|
36
|
-
async function availableAgentIds(requiresHost = false) {
|
|
37
|
-
const local = hostname();
|
|
38
|
-
return [...new Set((await listRunners()).filter((row) => row.status === "online" && (!requiresHost || row.hostname === local)).flatMap((row) => row.inventory.filter((entry) => entry.installed && entry.signedIn).map((entry) => entry.agentId)))];
|
|
39
|
-
}
|
|
40
|
-
async function getJoinToken() {
|
|
41
|
-
const existing = await secretStore().get(joinTokenKey());
|
|
42
|
-
if (existing) return existing;
|
|
43
|
-
const token = randomBytes(24).toString("base64url");
|
|
44
|
-
await secretStore().set(joinTokenKey(), token);
|
|
45
|
-
return token;
|
|
46
|
-
}
|
|
47
|
-
async function rotateJoinToken() {
|
|
48
|
-
const token = randomBytes(24).toString("base64url");
|
|
49
|
-
await secretStore().set(joinTokenKey(), token);
|
|
50
|
-
return token;
|
|
51
|
-
}
|
|
52
|
-
async function heartbeatRunner(runnerId, inventory) {
|
|
53
|
-
if (!await getRunner(runnerId)) throw new Error("Unknown runner.");
|
|
54
|
-
await db().update(runners).set({
|
|
55
|
-
status: "online",
|
|
56
|
-
inventory,
|
|
57
|
-
lastSeenAt: /* @__PURE__ */ new Date()
|
|
58
|
-
}).where(eq(runners.id, runnerId)).run();
|
|
59
|
-
return toView(await getRunner(runnerId));
|
|
60
|
-
}
|
|
61
|
-
async function joinRunner(input) {
|
|
62
|
-
const expected = await getJoinToken();
|
|
63
|
-
if (!input.joinToken || input.joinToken !== expected) throw new Error("That join token is not valid.");
|
|
64
|
-
const existing = (await db().select().from(runners).all()).find((row) => row.hostname === input.hostname);
|
|
65
|
-
const id = existing?.id ?? randomUUID();
|
|
66
|
-
const name = existing?.name ?? input.hostname;
|
|
67
|
-
const now = /* @__PURE__ */ new Date();
|
|
68
|
-
if (existing) await db().update(runners).set({
|
|
69
|
-
status: "online",
|
|
70
|
-
inventory: input.inventory,
|
|
71
|
-
lastSeenAt: now,
|
|
72
|
-
hostname: input.hostname
|
|
73
|
-
}).where(eq(runners.id, id)).run();
|
|
74
|
-
else await db().insert(runners).values({
|
|
75
|
-
id,
|
|
76
|
-
name,
|
|
77
|
-
hostname: input.hostname,
|
|
78
|
-
status: "online",
|
|
79
|
-
inventory: input.inventory,
|
|
80
|
-
lastSeenAt: now
|
|
81
|
-
}).run();
|
|
82
|
-
const token = randomBytes(24).toString("base64url");
|
|
83
|
-
await secretStore().set(runnerTokenKey(id), token);
|
|
84
|
-
return {
|
|
85
|
-
runnerId: id,
|
|
86
|
-
runnerToken: token,
|
|
87
|
-
name
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
async function runnerIdForToken(token) {
|
|
91
|
-
if (!token) return null;
|
|
92
|
-
for (const row of await db().select().from(runners).all()) {
|
|
93
|
-
const stored = await secretStore().get(runnerTokenKey(row.id)) ?? await secretStore().get(`runners.machine.${row.id}`);
|
|
94
|
-
if (stored && stored === token) return row.id;
|
|
95
|
-
}
|
|
96
|
-
return null;
|
|
97
|
-
}
|
|
98
|
-
async function forgetRunner(id) {
|
|
99
|
-
if (!await getRunner(id)) throw new Error("Unknown runner.");
|
|
100
|
-
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.");
|
|
101
|
-
await db().delete(runners).where(eq(runners.id, id)).run();
|
|
102
|
-
await secretStore().delete(runnerTokenKey(id));
|
|
103
|
-
await secretStore().delete(`runners.machine.${id}`);
|
|
104
|
-
}
|
|
105
|
-
//#endregion
|
|
106
|
-
export { heartbeatRunner as a, listRunners as c, runnerIdForToken as d, getRunner as i, onlineRunnerCount as l, forgetRunner as n, isRunnerOnline as o, getJoinToken as r, joinRunner as s, availableAgentIds as t, rotateJoinToken as u };
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
import { U as require_jsx_runtime } from "../_libs/@base-ui/react+[...].mjs";
|
|
2
|
-
import { m as useRouter } from "../_libs/@tanstack/react-router+[...].mjs";
|
|
3
|
-
import { s as Server } from "../_libs/lucide-react.mjs";
|
|
4
|
-
import { n as toast } from "../_libs/sonner.mjs";
|
|
5
|
-
import { B as Card, H as CardHeader, R as Badge, V as CardContent, f as useLiveRefresh, i as rotateRunnerJoinToken, n as Route$7, r as removeRunner, z as Button$1 } from "./router-54IDWTVz.mjs";
|
|
6
|
-
import { n as AlertDescription, t as Alert } from "./alert-DQvzE7bu.mjs";
|
|
7
|
-
//#region node_modules/.nitro/vite/services/ssr/assets/runners-DGWaVvWi.js
|
|
8
|
-
var import_jsx_runtime = require_jsx_runtime();
|
|
9
|
-
function inventoryLines(runner) {
|
|
10
|
-
if (runner.inventory.length === 0) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
11
|
-
className: "mt-2 text-sm text-muted-foreground",
|
|
12
|
-
children: "No coding agents found yet."
|
|
13
|
-
});
|
|
14
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", {
|
|
15
|
-
className: "mt-2 text-sm text-muted-foreground",
|
|
16
|
-
children: runner.inventory.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { children: [entry.agentId, entry.signedIn ? " · signed in" : entry.installed ? " · not signed in" : " · missing"] }, entry.agentId))
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
function RunnersPage() {
|
|
20
|
-
const { runners, joinToken, origin } = Route$7.useLoaderData();
|
|
21
|
-
const router = useRouter();
|
|
22
|
-
useLiveRefresh();
|
|
23
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
24
|
-
className: "flex flex-col gap-8",
|
|
25
|
-
children: [
|
|
26
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", {
|
|
27
|
-
className: "text-2xl font-semibold tracking-tight",
|
|
28
|
-
children: "Runners"
|
|
29
|
-
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
30
|
-
className: "mt-1 text-sm text-muted-foreground",
|
|
31
|
-
children: "A runner is a process that runs agents. Join one on every host that should do work."
|
|
32
|
-
})] }),
|
|
33
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Card, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardHeader, {
|
|
34
|
-
className: "text-base font-medium",
|
|
35
|
-
children: "Join a runner"
|
|
36
|
-
}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(CardContent, {
|
|
37
|
-
className: "flex flex-col gap-3 text-sm",
|
|
38
|
-
children: [
|
|
39
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", {
|
|
40
|
-
className: "text-muted-foreground",
|
|
41
|
-
children: [
|
|
42
|
-
"Install the CLI once (",
|
|
43
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", {
|
|
44
|
-
className: "text-xs",
|
|
45
|
-
children: "npm install -g loopable-cli"
|
|
46
|
-
}),
|
|
47
|
-
"), sign in a coding agent on that host, then start a runner. Use this LAN IP so other machines can reach the App."
|
|
48
|
-
]
|
|
49
|
-
}),
|
|
50
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("pre", {
|
|
51
|
-
className: "overflow-x-auto rounded-md bg-muted p-3 text-xs",
|
|
52
|
-
children: `loopable runner --url ${origin} --token ${joinToken}`
|
|
53
|
-
}),
|
|
54
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
|
|
55
|
-
variant: "outline",
|
|
56
|
-
size: "sm",
|
|
57
|
-
onClick: async () => {
|
|
58
|
-
try {
|
|
59
|
-
await rotateRunnerJoinToken();
|
|
60
|
-
await router.invalidate();
|
|
61
|
-
toast.success("Join token rotated. Existing runners keep working.");
|
|
62
|
-
} catch (error) {
|
|
63
|
-
toast.error(error instanceof Error ? error.message : String(error));
|
|
64
|
-
}
|
|
65
|
-
},
|
|
66
|
-
children: "Rotate join token"
|
|
67
|
-
}) })
|
|
68
|
-
]
|
|
69
|
-
})] }),
|
|
70
|
-
runners.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Alert, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AlertDescription, { children: "No runner has joined yet. Loops will wait until one is online." }) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
71
|
-
className: "flex flex-col gap-3",
|
|
72
|
-
children: runners.map((runner) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Card, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(CardContent, {
|
|
73
|
-
className: "flex items-start gap-4 pt-6",
|
|
74
|
-
children: [
|
|
75
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Server, { className: "mt-0.5 size-5 shrink-0 text-muted-foreground" }),
|
|
76
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
77
|
-
className: "min-w-0 flex-1",
|
|
78
|
-
children: [
|
|
79
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
80
|
-
className: "flex flex-wrap items-center gap-2",
|
|
81
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
82
|
-
className: "font-medium",
|
|
83
|
-
children: runner.name
|
|
84
|
-
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Badge, {
|
|
85
|
-
variant: runner.status === "online" ? "default" : "outline",
|
|
86
|
-
children: runner.status
|
|
87
|
-
})]
|
|
88
|
-
}),
|
|
89
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
90
|
-
className: "mt-1 text-xs text-muted-foreground",
|
|
91
|
-
children: runner.hostname
|
|
92
|
-
}),
|
|
93
|
-
inventoryLines(runner)
|
|
94
|
-
]
|
|
95
|
-
}),
|
|
96
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
|
|
97
|
-
variant: "ghost",
|
|
98
|
-
size: "sm",
|
|
99
|
-
onClick: async () => {
|
|
100
|
-
try {
|
|
101
|
-
await removeRunner({ data: { id: runner.id } });
|
|
102
|
-
await router.invalidate();
|
|
103
|
-
} catch (error) {
|
|
104
|
-
toast.error(error instanceof Error ? error.message : String(error));
|
|
105
|
-
}
|
|
106
|
-
},
|
|
107
|
-
children: "Forget"
|
|
108
|
-
})
|
|
109
|
-
]
|
|
110
|
-
}) }, runner.id))
|
|
111
|
-
})
|
|
112
|
-
]
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
//#endregion
|
|
116
|
-
export { RunnersPage as component };
|
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
import { U as require_jsx_runtime } from "../_libs/@base-ui/react+[...].mjs";
|
|
2
|
-
import { f as Link } from "../_libs/@tanstack/react-router+[...].mjs";
|
|
3
|
-
import { o as connectorManifest } from "./connections-Df-aDpfJ.mjs";
|
|
4
|
-
import { n as agentManifest } from "./agents-DG643yoJ.mjs";
|
|
5
|
-
import { A as CircleMinus, D as CircleSlash, I as Bot, T as CircleX, b as FileText, d as Plug, g as Loader, j as CircleCheck, n as Workflow, w as Clock } from "../_libs/lucide-react.mjs";
|
|
6
|
-
import { B as Card, R as Badge, V as CardContent, W as cn, p as useLiveTasks } from "./router-54IDWTVz.mjs";
|
|
7
|
-
import { t as ConnectorIcon } from "./connector-icon-WvxwZ5TF.mjs";
|
|
8
|
-
//#region node_modules/.nitro/vite/services/ssr/assets/task-list-_qgBS9LU.js
|
|
9
|
-
var import_jsx_runtime = require_jsx_runtime();
|
|
10
|
-
var STATES = {
|
|
11
|
-
queued: {
|
|
12
|
-
label: "Queued",
|
|
13
|
-
icon: Clock,
|
|
14
|
-
tone: "text-muted-foreground"
|
|
15
|
-
},
|
|
16
|
-
preparing: {
|
|
17
|
-
label: "Working",
|
|
18
|
-
icon: Loader,
|
|
19
|
-
tone: "text-muted-foreground"
|
|
20
|
-
},
|
|
21
|
-
awaiting_agent: {
|
|
22
|
-
label: "On a runner",
|
|
23
|
-
icon: Bot,
|
|
24
|
-
tone: "text-muted-foreground"
|
|
25
|
-
},
|
|
26
|
-
applying: {
|
|
27
|
-
label: "Writing",
|
|
28
|
-
icon: Loader,
|
|
29
|
-
tone: "text-muted-foreground"
|
|
30
|
-
},
|
|
31
|
-
prepared: {
|
|
32
|
-
label: "Prepared",
|
|
33
|
-
icon: FileText,
|
|
34
|
-
tone: "text-muted-foreground"
|
|
35
|
-
},
|
|
36
|
-
done: {
|
|
37
|
-
label: "Written",
|
|
38
|
-
icon: CircleCheck,
|
|
39
|
-
tone: "text-emerald-600"
|
|
40
|
-
},
|
|
41
|
-
skipped: {
|
|
42
|
-
label: "Nothing to say",
|
|
43
|
-
icon: CircleMinus,
|
|
44
|
-
tone: "text-muted-foreground"
|
|
45
|
-
},
|
|
46
|
-
failed: {
|
|
47
|
-
label: "Failed",
|
|
48
|
-
icon: CircleX,
|
|
49
|
-
tone: "text-destructive"
|
|
50
|
-
},
|
|
51
|
-
cancelled: {
|
|
52
|
-
label: "Stopped",
|
|
53
|
-
icon: CircleSlash,
|
|
54
|
-
tone: "text-muted-foreground"
|
|
55
|
-
}
|
|
56
|
-
};
|
|
57
|
-
function TaskStateLabel({ state }) {
|
|
58
|
-
const meta = STATES[state];
|
|
59
|
-
const Icon = meta.icon;
|
|
60
|
-
const spinning = state === "preparing" || state === "awaiting_agent" || state === "applying";
|
|
61
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
|
|
62
|
-
className: cn("flex items-center gap-1.5 text-xs", meta.tone),
|
|
63
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { className: cn("size-3.5", spinning && "animate-spin") }), meta.label]
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
/** A queued task has no title yet: the connector has not fetched it. */
|
|
67
|
-
function taskTitle(task) {
|
|
68
|
-
return task.sourceTitle ?? task.sourceRef;
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* Kinds whose reference a person can read. A repository and a number say
|
|
72
|
-
* where the work is; a mail or a message id is a handle for fetching it
|
|
73
|
-
* again and means nothing on a page.
|
|
74
|
-
*/
|
|
75
|
-
var NAMED_REF = /* @__PURE__ */ new Set(["pull_request", "issue"]);
|
|
76
|
-
/** What to call the thing a task is about, where a sentence needs a noun. */
|
|
77
|
-
var KIND_LABEL = {
|
|
78
|
-
pull_request: "Pull request",
|
|
79
|
-
issue: "Issue",
|
|
80
|
-
message: "Message",
|
|
81
|
-
email: "Email",
|
|
82
|
-
occurrence: "Scheduled run",
|
|
83
|
-
prompt: "Test run"
|
|
84
|
-
};
|
|
85
|
-
function when(iso) {
|
|
86
|
-
const seconds = Math.round((Date.now() - new Date(iso).getTime()) / 1e3);
|
|
87
|
-
if (seconds < 60) return "just now";
|
|
88
|
-
const minutes = Math.round(seconds / 60);
|
|
89
|
-
if (minutes < 60) return `${minutes}m ago`;
|
|
90
|
-
const hours = Math.round(minutes / 60);
|
|
91
|
-
if (hours < 24) return `${hours}h ago`;
|
|
92
|
-
return new Date(iso).toLocaleDateString();
|
|
93
|
-
}
|
|
94
|
-
function TaskList({ tasks, showLoop = true, empty }) {
|
|
95
|
-
useLiveTasks(tasks);
|
|
96
|
-
if (tasks.length === 0) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Card, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardContent, {
|
|
97
|
-
className: "py-8 text-center text-sm text-muted-foreground",
|
|
98
|
-
children: empty
|
|
99
|
-
}) });
|
|
100
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
101
|
-
className: "flex flex-col gap-2",
|
|
102
|
-
children: tasks.map((task) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Link, {
|
|
103
|
-
to: "/inbox/$taskId",
|
|
104
|
-
params: { taskId: task.id },
|
|
105
|
-
className: "rounded-lg border bg-card p-4 transition-colors hover:bg-accent/40",
|
|
106
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
107
|
-
className: "flex items-start gap-3",
|
|
108
|
-
children: [
|
|
109
|
-
task.sourceKind === "prompt" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Bot, { className: "mt-0.5 size-7 shrink-0 text-muted-foreground" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ConnectorIcon, {
|
|
110
|
-
icon: connectorManifest(task.connectorId)?.icon ?? "Plug",
|
|
111
|
-
accent: connectorManifest(task.connectorId)?.accent ?? "bg-muted",
|
|
112
|
-
className: "mt-0.5 size-7 shrink-0"
|
|
113
|
-
}),
|
|
114
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
115
|
-
className: "min-w-0 flex-1",
|
|
116
|
-
children: [
|
|
117
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
118
|
-
className: "truncate text-sm font-medium",
|
|
119
|
-
children: taskTitle(task)
|
|
120
|
-
}),
|
|
121
|
-
NAMED_REF.has(task.sourceKind) && task.sourceTitle ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
122
|
-
className: "mt-0.5 truncate text-xs text-muted-foreground",
|
|
123
|
-
children: task.sourceRef
|
|
124
|
-
}) : null,
|
|
125
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TaskMeta, {
|
|
126
|
-
task,
|
|
127
|
-
showLoop
|
|
128
|
-
}),
|
|
129
|
-
task.output ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
130
|
-
className: "mt-2 line-clamp-2 text-xs text-muted-foreground",
|
|
131
|
-
children: task.output
|
|
132
|
-
}) : task.error ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
133
|
-
className: "mt-2 line-clamp-2 text-xs text-destructive",
|
|
134
|
-
children: task.error
|
|
135
|
-
}) : null
|
|
136
|
-
]
|
|
137
|
-
}),
|
|
138
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
139
|
-
className: "flex shrink-0 items-center gap-2",
|
|
140
|
-
children: [task.dryRun ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Badge, {
|
|
141
|
-
variant: "outline",
|
|
142
|
-
children: "Dry run"
|
|
143
|
-
}) : null, /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TaskStateLabel, { state: task.state })]
|
|
144
|
-
})
|
|
145
|
-
]
|
|
146
|
-
})
|
|
147
|
-
}, task.id))
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
/**
|
|
151
|
-
* What ran, and what it ran through. Which agent wrote the words matters when
|
|
152
|
-
* two loops disagree, and it is only known once the dispatcher has picked the task
|
|
153
|
-
* up, so it is absent rather than wrong while a task is queued.
|
|
154
|
-
*/
|
|
155
|
-
function TaskMeta({ task, showLoop }) {
|
|
156
|
-
const connector = connectorManifest(task.connectorId);
|
|
157
|
-
const agent = task.agentId ? agentManifest(task.agentId) : void 0;
|
|
158
|
-
const parts = [];
|
|
159
|
-
if (showLoop && task.loopName) parts.push({
|
|
160
|
-
icon: Workflow,
|
|
161
|
-
label: task.loopName
|
|
162
|
-
});
|
|
163
|
-
if (connector) parts.push({
|
|
164
|
-
icon: Plug,
|
|
165
|
-
label: connector.name
|
|
166
|
-
});
|
|
167
|
-
if (agent) parts.push({
|
|
168
|
-
icon: Bot,
|
|
169
|
-
label: agent.name
|
|
170
|
-
});
|
|
171
|
-
parts.push({
|
|
172
|
-
icon: Clock,
|
|
173
|
-
label: when(task.createdAt)
|
|
174
|
-
});
|
|
175
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
176
|
-
className: "mt-1.5 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted-foreground",
|
|
177
|
-
children: parts.map((part) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
|
|
178
|
-
className: "flex min-w-0 items-center gap-1",
|
|
179
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(part.icon, { className: "size-3 shrink-0" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
180
|
-
className: "truncate",
|
|
181
|
-
children: part.label
|
|
182
|
-
})]
|
|
183
|
-
}, part.label))
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
//#endregion
|
|
187
|
-
export { taskTitle as i, TaskList as n, TaskStateLabel as r, KIND_LABEL as t };
|