loopable-cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/.output/nitro.json +17 -0
  2. package/.output/public/apple-touch-icon.png +0 -0
  3. package/.output/public/assets/_connectorId-BWjYn5tm.js +1 -0
  4. package/.output/public/assets/_loopId-Ds4RkyEc.js +1 -0
  5. package/.output/public/assets/_taskId-BOzICXDf.js +1 -0
  6. package/.output/public/assets/alert-Bn9-Zm0l.js +1 -0
  7. package/.output/public/assets/arrow-left-BF9juV9T.js +1 -0
  8. package/.output/public/assets/arrow-right-CzqvnOY1.js +1 -0
  9. package/.output/public/assets/button-HCY6DCY-.js +1 -0
  10. package/.output/public/assets/connection-status-CYZB8kLy.js +1 -0
  11. package/.output/public/assets/connector-icon-BZ_MUJV_.js +1 -0
  12. package/.output/public/assets/connectors-BvzttfSu.js +1 -0
  13. package/.output/public/assets/connectors-DdJNwRN_.js +1 -0
  14. package/.output/public/assets/external-link-D46X-bXa.js +1 -0
  15. package/.output/public/assets/gaps-D-I6xiDc.js +1 -0
  16. package/.output/public/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
  17. package/.output/public/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
  18. package/.output/public/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
  19. package/.output/public/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
  20. package/.output/public/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
  21. package/.output/public/assets/inbox-BUUu9xxE.js +1 -0
  22. package/.output/public/assets/index-ztzex0FN.js +10 -0
  23. package/.output/public/assets/loader-CcBl2u8m.js +1 -0
  24. package/.output/public/assets/loop-form-dB3CLcZb.js +1 -0
  25. package/.output/public/assets/loops-DRunQrxL.js +1 -0
  26. package/.output/public/assets/loops-oSdqMd29.js +1 -0
  27. package/.output/public/assets/manifests-DNMqf7Vi.js +7 -0
  28. package/.output/public/assets/new-uyFxVjuB.js +1 -0
  29. package/.output/public/assets/preload-helper-ORbuV86A.js +3 -0
  30. package/.output/public/assets/runners-DSU3r5Ww.js +1 -0
  31. package/.output/public/assets/setting-fields-ixkpdBb-.js +3 -0
  32. package/.output/public/assets/styles-DZL5V1o7.css +2 -0
  33. package/.output/public/assets/switch-d5ummmHY.js +1 -0
  34. package/.output/public/assets/task-list-BpOStt9v.js +1 -0
  35. package/.output/public/assets/tasks-B8MsKgic.js +1 -0
  36. package/.output/public/assets/trash-2-D6u6NpzO.js +1 -0
  37. package/.output/public/favicon.svg +9 -0
  38. package/.output/server/_chunks/ssr-renderer.mjs +26 -0
  39. package/.output/server/_connectorId-8nb5yOzG.mjs +421 -0
  40. package/.output/server/_libs/@base-ui/react+[...].mjs +12841 -0
  41. package/.output/server/_libs/@tanstack/react-router+[...].mjs +14349 -0
  42. package/.output/server/_libs/class-variance-authority+clsx.mjs +69 -0
  43. package/.output/server/_libs/dijkstrajs.mjs +118 -0
  44. package/.output/server/_libs/drizzle-orm.mjs +4349 -0
  45. package/.output/server/_libs/h3+rou3+srvx.mjs +1361 -0
  46. package/.output/server/_libs/lucide-react.mjs +911 -0
  47. package/.output/server/_libs/next-themes.mjs +53 -0
  48. package/.output/server/_libs/pngjs.mjs +1680 -0
  49. package/.output/server/_libs/qrcode.mjs +2583 -0
  50. package/.output/server/_libs/sonner.mjs +955 -0
  51. package/.output/server/_libs/tailwind-merge.mjs +3380 -0
  52. package/.output/server/_libs/tanstack__history.mjs +341 -0
  53. package/.output/server/_libs/ufo.mjs +64 -0
  54. package/.output/server/_loopId-BsUL1SeL.mjs +272 -0
  55. package/.output/server/_runtime.mjs +35 -0
  56. package/.output/server/_ssr/access-zIl15o8y.mjs +42 -0
  57. package/.output/server/_ssr/agents-Btql102J.mjs +657 -0
  58. package/.output/server/_ssr/agents-DAOZPgO2.mjs +76 -0
  59. package/.output/server/_ssr/alert-NEiOCTOl.mjs +37 -0
  60. package/.output/server/_ssr/callback-CUINvl7M.mjs +62 -0
  61. package/.output/server/_ssr/client-CKkOfh-s.mjs +335 -0
  62. package/.output/server/_ssr/connection-status-Bi5peUAk.mjs +31 -0
  63. package/.output/server/_ssr/connections-qnw2GVlP.mjs +2542 -0
  64. package/.output/server/_ssr/connector-icon-DZXQLLFo.mjs +37 -0
  65. package/.output/server/_ssr/connectors-BHNwuIMj.mjs +106 -0
  66. package/.output/server/_ssr/connectors-C28g0i4c.mjs +83 -0
  67. package/.output/server/_ssr/createServerRpc-DPDrnFdH.mjs +12 -0
  68. package/.output/server/_ssr/empty-plugin-adapters-BJ4m9L8g.mjs +5 -0
  69. package/.output/server/_ssr/gaps-RAFSXLZf.mjs +53 -0
  70. package/.output/server/_ssr/inbox-k5zHergU.mjs +35 -0
  71. package/.output/server/_ssr/loop-form-DlObSewZ.mjs +412 -0
  72. package/.output/server/_ssr/loops-APFcP0y6.mjs +311 -0
  73. package/.output/server/_ssr/loops-xt2Bn7_q.mjs +222 -0
  74. package/.output/server/_ssr/new-DfKyd2Wa.mjs +169 -0
  75. package/.output/server/_ssr/router-3WmpE5b_.mjs +1553 -0
  76. package/.output/server/_ssr/runners-By-mwc3F.mjs +32 -0
  77. package/.output/server/_ssr/runners-DI_NxpHC.mjs +116 -0
  78. package/.output/server/_ssr/runners-DtXs4Q8a.mjs +106 -0
  79. package/.output/server/_ssr/secrets-Hqti-niS.mjs +199 -0
  80. package/.output/server/_ssr/setting-fields-8vKFj4df.mjs +92 -0
  81. package/.output/server/_ssr/ssr.mjs +6605 -0
  82. package/.output/server/_ssr/start-Bdc2GJUI.mjs +51 -0
  83. package/.output/server/_ssr/switch-ReNglgH6.mjs +18 -0
  84. package/.output/server/_ssr/task-list-CUITIqkb.mjs +186 -0
  85. package/.output/server/_ssr/tasks-SAjNovX6.mjs +110 -0
  86. package/.output/server/_ssr/tasks-vcoodp_D.mjs +533 -0
  87. package/.output/server/_tanstack-start-manifest_v-C0cP5F0M.mjs +125 -0
  88. package/.output/server/_taskId-CXS3lod0.mjs +249 -0
  89. package/.output/server/index.mjs +541 -0
  90. package/README.md +81 -0
  91. package/dist/cli.mjs +165 -0
  92. package/dist/dispatcher.mjs +9871 -0
  93. package/dist/runner.mjs +657 -0
  94. package/drizzle/0000_cold_demogoblin.sql +29 -0
  95. package/drizzle/0001_fancy_blonde_phantom.sql +13 -0
  96. package/drizzle/0002_spooky_smiling_tiger.sql +16 -0
  97. package/drizzle/0003_small_golden_guardian.sql +25 -0
  98. package/drizzle/0004_wonderful_thunderbolts.sql +39 -0
  99. package/drizzle/0005_named_workflows.sql +7 -0
  100. package/drizzle/0006_signals.sql +18 -0
  101. package/drizzle/0007_review_comments.sql +1 -0
  102. package/drizzle/0008_held_signals.sql +1 -0
  103. package/drizzle/0009_work_item_ref.sql +58 -0
  104. package/drizzle/0010_signal_payload.sql +2 -0
  105. package/drizzle/0011_rule_poll_cursor.sql +1 -0
  106. package/drizzle/0012_loops.sql +63 -0
  107. package/drizzle/0013_loop_owns_action_and_prompt.sql +154 -0
  108. package/drizzle/0014_loop_poll_every.sql +4 -0
  109. package/drizzle/0015_machines.sql +10 -0
  110. package/drizzle/0016_task_machine.sql +1 -0
  111. package/drizzle/0017_loop_machine.sql +1 -0
  112. package/drizzle/0018_runners.sql +1 -0
  113. package/drizzle/0019_task_runner.sql +1 -0
  114. package/drizzle/0020_loop_runner.sql +1 -0
  115. package/drizzle/0021_drop_runner_kind.sql +1 -0
  116. package/drizzle/0022_drop_loop_runner.sql +1 -0
  117. package/drizzle/0023_shared_host.sql +6 -0
  118. package/drizzle/meta/0000_snapshot.json +206 -0
  119. package/drizzle/meta/0001_snapshot.json +284 -0
  120. package/drizzle/meta/0002_snapshot.json +390 -0
  121. package/drizzle/meta/0003_snapshot.json +566 -0
  122. package/drizzle/meta/0004_snapshot.json +633 -0
  123. package/drizzle/meta/0005_snapshot.json +626 -0
  124. package/drizzle/meta/0006_snapshot.json +752 -0
  125. package/drizzle/meta/0007_snapshot.json +759 -0
  126. package/drizzle/meta/0008_snapshot.json +766 -0
  127. package/drizzle/meta/0009_snapshot.json +752 -0
  128. package/drizzle/meta/0010_snapshot.json +766 -0
  129. package/drizzle/meta/0011_snapshot.json +773 -0
  130. package/drizzle/meta/0012_snapshot.json +773 -0
  131. package/drizzle/meta/0013_snapshot.json +817 -0
  132. package/drizzle/meta/_journal.json +174 -0
  133. package/package.json +88 -0
@@ -0,0 +1,412 @@
1
+ import { i as __toESM } from "../_runtime.mjs";
2
+ import { U as require_jsx_runtime, W as require_react } from "../_libs/@base-ui/react+[...].mjs";
3
+ import { p as useNavigate } from "../_libs/@tanstack/react-router+[...].mjs";
4
+ import { a as connectorManifest, i as connectorAction, s as connectorWorkflow, t as CONNECTOR_MANIFESTS } from "./connections-qnw2GVlP.mjs";
5
+ import { t as AGENT_MANIFESTS } from "./agents-Btql102J.mjs";
6
+ import { L as Bell, z as ArrowRight } from "../_libs/lucide-react.mjs";
7
+ import { n as toast } from "../_libs/sonner.mjs";
8
+ import { C as Button$1, D as saveLoop, L as Card, R as CardContent, S as Input$1, _ as SelectItem, b as Label, g as SelectContent, h as Select$1, v as SelectTrigger, y as SelectValue } from "./router-3WmpE5b_.mjs";
9
+ import { t as Switch$1 } from "./switch-ReNglgH6.mjs";
10
+ import { n as Textarea, r as initialFieldValues, t as SettingFieldInputs } from "./setting-fields-8vKFj4df.mjs";
11
+ import { n as AlertDescription, r as AlertTitle, t as Alert } from "./alert-NEiOCTOl.mjs";
12
+ //#region node_modules/.nitro/vite/services/ssr/assets/loop-form-DlObSewZ.js
13
+ var import_react = /* @__PURE__ */ __toESM(require_react());
14
+ var import_jsx_runtime = require_jsx_runtime();
15
+ var DEFAULT_AGENT = "__default";
16
+ /** Zero is stored as null: "as often as the dispatcher does" is not a duration. */
17
+ var POLL_CHOICES = [
18
+ {
19
+ value: "0",
20
+ label: "Every time the dispatcher looks"
21
+ },
22
+ {
23
+ value: String(6e5),
24
+ label: "Every 10 minutes"
25
+ },
26
+ {
27
+ value: String(18e5),
28
+ label: "Every 30 minutes"
29
+ },
30
+ {
31
+ value: String(36e5),
32
+ label: "Every hour"
33
+ },
34
+ {
35
+ value: String(144e5),
36
+ label: "Every 4 hours"
37
+ }
38
+ ];
39
+ /** Connectors that can write at all, for the question of where the answer goes. */
40
+ var WRITERS = CONNECTOR_MANIFESTS.filter((entry) => entry.actions.length > 0);
41
+ /**
42
+ * Shown rather than hidden, and locked rather than editable.
43
+ *
44
+ * These are the parts of a workflow that only mean anything alongside the code
45
+ * that reads them: the query a connector sends, whether the agent gets a
46
+ * checkout, how its answer is parsed. Copying them onto a loop would freeze
47
+ * whatever was true the day it was made, and leaving them off the page would
48
+ * mean nobody could see what their loop actually does.
49
+ */
50
+ function Locked({ label, value, help, mono }) {
51
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
52
+ className: "flex flex-col gap-2",
53
+ children: [
54
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Label, {
55
+ className: "text-muted-foreground",
56
+ children: label
57
+ }),
58
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Input$1, {
59
+ readOnly: true,
60
+ disabled: true,
61
+ value,
62
+ className: mono ? "font-mono text-xs disabled:opacity-100" : "disabled:opacity-100"
63
+ }),
64
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
65
+ className: "text-xs text-muted-foreground",
66
+ children: help
67
+ })
68
+ ]
69
+ });
70
+ }
71
+ /**
72
+ * A loop is one of a connector's workflows with its own answers to the
73
+ * questions it asks. Which of those a loop owns is the whole shape of this
74
+ * page: what it looks for and how the answer is read belong to the workflow
75
+ * and are locked, while what narrows it, where it writes and which agent runs
76
+ * it belong to the loop.
77
+ *
78
+ * Serves a loop that exists and one that does not. A loop with no id has been
79
+ * chosen and not saved, so leaving this page is the end of it: nothing was
80
+ * written down, and nothing is watching for anything.
81
+ */
82
+ function LoopForm({ loop }) {
83
+ const navigate = useNavigate();
84
+ const fresh = loop.id === null;
85
+ const connector = connectorManifest(loop.connectorId);
86
+ const workflow = connectorWorkflow(loop.connectorId, loop.workflowId);
87
+ const [name, setName] = (0, import_react.useState)(loop.name);
88
+ const [prompt, setPrompt] = (0, import_react.useState)(loop.prompt);
89
+ const [guidance, setGuidance] = (0, import_react.useState)(loop.guidance ?? "");
90
+ const [agentId, setAgentId] = (0, import_react.useState)(loop.agentId ?? DEFAULT_AGENT);
91
+ const [enabled, setEnabled] = (0, import_react.useState)(loop.enabled);
92
+ const [settings, setSettings] = (0, import_react.useState)(() => initialFieldValues(workflow?.settings ?? [], loop.settings));
93
+ const [pollEveryMs, setPollEveryMs] = (0, import_react.useState)(String(loop.pollEveryMs ?? 0));
94
+ const [actionConnectorId, setActionConnectorId] = (0, import_react.useState)(loop.actionConnectorId);
95
+ const [actionId, setActionId] = (0, import_react.useState)(loop.actionId);
96
+ const [actionTarget, setActionTarget] = (0, import_react.useState)(() => initialFieldValues(connectorAction(loop.actionConnectorId, loop.actionId)?.target ?? [], loop.actionTarget));
97
+ const [saving, setSaving] = (0, import_react.useState)(false);
98
+ const action = connectorAction(actionConnectorId, actionId);
99
+ const writerName = connectorManifest(actionConnectorId)?.name ?? actionConnectorId;
100
+ /** Changing where the answer goes changes what has to be said about it. */
101
+ const chooseAction = (connectorId, id) => {
102
+ setActionConnectorId(connectorId);
103
+ setActionId(id);
104
+ setActionTarget(initialFieldValues(connectorAction(connectorId, id)?.target ?? [], {}));
105
+ };
106
+ if (!workflow) return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Alert, {
107
+ variant: "destructive",
108
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(AlertTitle, { children: "This workflow is no longer offered" }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(AlertDescription, { children: [
109
+ connector?.name ?? loop.connectorId,
110
+ " used to have ",
111
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { children: loop.workflowId }),
112
+ " and no longer does, so this loop cannot run or be edited. Deleting it is the only thing left to do with it."
113
+ ] })]
114
+ });
115
+ const save = async () => {
116
+ setSaving(true);
117
+ try {
118
+ const saved = await saveLoop({ data: {
119
+ id: loop.id,
120
+ name,
121
+ connectorId: loop.connectorId,
122
+ workflowId: loop.workflowId,
123
+ prompt,
124
+ guidance: guidance.trim() || null,
125
+ agentId: agentId === DEFAULT_AGENT ? null : agentId,
126
+ settings,
127
+ actionConnectorId,
128
+ actionId,
129
+ actionTarget,
130
+ pollEveryMs: Number(pollEveryMs) || null,
131
+ enabled
132
+ } });
133
+ toast.success(fresh ? `Added "${saved.name}"` : `Saved ${saved.name}`);
134
+ await navigate({ to: "/loops" });
135
+ } catch (error) {
136
+ toast.error(error instanceof Error ? error.message : String(error));
137
+ } finally {
138
+ setSaving(false);
139
+ }
140
+ };
141
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
142
+ className: "flex flex-col gap-6",
143
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Card, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(CardContent, {
144
+ className: "flex flex-col gap-5 pt-6",
145
+ children: [
146
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
147
+ className: "flex items-start gap-3 rounded-lg bg-muted/50 p-4 text-sm",
148
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Bell, { className: "mt-0.5 size-4 shrink-0 text-muted-foreground" }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
149
+ className: "flex flex-col gap-1",
150
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { children: [
151
+ "When ",
152
+ workflow.trigger,
153
+ ", ",
154
+ connector?.name ?? loop.connectorId,
155
+ " hands it to an agent."
156
+ ] }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", {
157
+ className: "flex items-center gap-1.5 text-muted-foreground",
158
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ArrowRight, { className: "size-3.5 shrink-0" }), action ? `Then, on ${writerName}: ${action.name.toLowerCase()}.` : "Where the answer goes is no longer offered; choose again below."]
159
+ })]
160
+ })]
161
+ }),
162
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
163
+ className: "flex flex-col gap-2",
164
+ children: [
165
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Label, {
166
+ htmlFor: "loop-name",
167
+ children: "Name"
168
+ }),
169
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Input$1, {
170
+ id: "loop-name",
171
+ value: name,
172
+ onChange: (event) => setName(event.target.value)
173
+ }),
174
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
175
+ className: "text-xs text-muted-foreground",
176
+ children: "Only for your benefit, and worth changing if you run this workflow more than once."
177
+ })
178
+ ]
179
+ }),
180
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Locked, {
181
+ label: "What it looks for",
182
+ mono: true,
183
+ value: workflow.watches ?? "Nothing is asked for; it arrives as it happens",
184
+ help: workflow.watches ? `Word for word what Loopable asks ${connector?.name ?? loop.connectorId} every couple of minutes${workflow.settings.length > 0 ? ", before anything below narrows it further." : "."}` : `${connector?.name ?? loop.connectorId} sends this over as it happens, and Loopable picks it up within a couple of minutes.`
185
+ }),
186
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Locked, {
187
+ label: "Where the agent runs",
188
+ value: workflow.runsIn === "checkout" ? "In a scratch directory; the prompt tells the agent to clone" : workflow.runsIn === "folder" ? "In the folder this loop names" : "In a scratch directory holding only what it was given",
189
+ help: workflow.runsIn === "checkout" ? "The agent clones the repository with that machine's git. Loopable's GitHub account is not used to clone, and is only used afterwards to write the review or open the pull request." : workflow.runsIn === "folder" ? "This job reads real code to do its work, so it runs in a real checkout. It does not write there: anything it changed would be sitting in your working copy." : "The agent gets the files it was given and nothing else."
190
+ }),
191
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Locked, {
192
+ label: "How its answer is read",
193
+ value: workflow.answer === "review" ? "A summary, plus points attached to lines" : workflow.answer === "code" ? "A change to the code, described in a sentence or two" : "One block of text",
194
+ help: "Part of how this job works rather than a preference, so it is fixed here and cannot drift from the code that parses it."
195
+ }),
196
+ workflow.settings.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
197
+ className: "flex flex-col gap-5 rounded-lg border p-4",
198
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
199
+ className: "text-sm font-medium",
200
+ children: "Only when"
201
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SettingFieldInputs, {
202
+ fields: workflow.settings,
203
+ values: settings,
204
+ idPrefix: "setting-",
205
+ onChange: (key, value) => setSettings((prev) => ({
206
+ ...prev,
207
+ [key]: value
208
+ }))
209
+ })]
210
+ }) : null,
211
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
212
+ className: "flex flex-col gap-5 rounded-lg border p-4",
213
+ children: [
214
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
215
+ className: "flex flex-col gap-2",
216
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
217
+ className: "text-sm font-medium",
218
+ children: "Where the answer goes"
219
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
220
+ className: "text-xs text-muted-foreground",
221
+ children: "Usually back to whatever triggered the loop, which is what this started as. It does not have to be, and it does not have to be the same service."
222
+ })]
223
+ }),
224
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
225
+ className: "flex flex-col gap-4 sm:flex-row",
226
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
227
+ className: "flex flex-1 flex-col gap-2",
228
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Label, {
229
+ htmlFor: "loop-action-connector",
230
+ children: "Service"
231
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Select$1, {
232
+ value: actionConnectorId,
233
+ onValueChange: (value) => value && chooseAction(value, connectorManifest(value).actions[0].id),
234
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectTrigger, {
235
+ id: "loop-action-connector",
236
+ className: "w-full",
237
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectValue, { children: (value) => connectorManifest(value)?.name ?? value })
238
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectContent, { children: WRITERS.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
239
+ value: entry.id,
240
+ children: entry.name
241
+ }, entry.id)) })]
242
+ })]
243
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
244
+ className: "flex flex-1 flex-col gap-2",
245
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Label, {
246
+ htmlFor: "loop-action",
247
+ children: "What it does there"
248
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Select$1, {
249
+ value: actionId,
250
+ onValueChange: (value) => value && chooseAction(actionConnectorId, value),
251
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectTrigger, {
252
+ id: "loop-action",
253
+ className: "w-full",
254
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectValue, { children: (value) => connectorAction(actionConnectorId, value)?.name ?? value })
255
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectContent, { children: (connectorManifest(actionConnectorId)?.actions ?? []).map((entry) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
256
+ value: entry.id,
257
+ children: entry.name
258
+ }, entry.id)) })]
259
+ })]
260
+ })]
261
+ }),
262
+ action ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
263
+ className: "text-xs text-muted-foreground",
264
+ children: action.summary
265
+ }) : null,
266
+ action && actionConnectorId !== loop.connectorId ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Alert, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(AlertTitle, { children: "This writes somewhere it did not read" }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(AlertDescription, { children: [
267
+ writerName,
268
+ " has no way to answer something on",
269
+ " ",
270
+ connector?.name ?? loop.connectorId,
271
+ ", so choose below where this should land. Both accounts have to be connected for the loop to run."
272
+ ] })] }) : null,
273
+ action && workflow.answer === "review" && !action.accepts.includes("review") ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
274
+ className: "text-xs text-muted-foreground",
275
+ children: "This cannot attach a comment to a line, so the review arrives as prose with the file and line of each point written into it. Nothing is dropped."
276
+ }) : null,
277
+ action && action.target.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SettingFieldInputs, {
278
+ fields: action.target,
279
+ values: actionTarget,
280
+ idPrefix: "target-",
281
+ onChange: (key, value) => setActionTarget((prev) => ({
282
+ ...prev,
283
+ [key]: value
284
+ }))
285
+ }) : null
286
+ ]
287
+ }),
288
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
289
+ className: "flex flex-col gap-2",
290
+ children: [
291
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Label, {
292
+ htmlFor: "loop-prompt",
293
+ children: "What the agent is asked to do"
294
+ }),
295
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
296
+ className: "text-xs text-muted-foreground",
297
+ children: fresh ? `Copied from “${workflow.name}”, and yours once you save. Editing it here changes nothing anywhere else, and a later version of that workflow will not change it back.` : `Copied from “${workflow.name}” when this loop was made, and yours now. Editing it here changes nothing anywhere else, and a later version of that workflow will not change it back.`
298
+ }),
299
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Textarea, {
300
+ id: "loop-prompt",
301
+ rows: 12,
302
+ className: "font-mono text-xs",
303
+ value: prompt,
304
+ onChange: (event) => setPrompt(event.target.value)
305
+ })
306
+ ]
307
+ }),
308
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
309
+ className: "flex flex-col gap-2",
310
+ children: [
311
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Label, {
312
+ htmlFor: "loop-guidance",
313
+ children: "Anything else the agent should know"
314
+ }),
315
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
316
+ className: "text-xs text-muted-foreground",
317
+ children: "Optional, and added after what you asked above. For a standing note about your team rather than about the job, so the two do not have to be untangled later."
318
+ }),
319
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Textarea, {
320
+ id: "loop-guidance",
321
+ rows: 4,
322
+ value: guidance,
323
+ placeholder: workflow.guidancePlaceholder,
324
+ onChange: (event) => setGuidance(event.target.value)
325
+ })
326
+ ]
327
+ }),
328
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
329
+ className: "flex flex-col gap-2",
330
+ children: [
331
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Label, {
332
+ htmlFor: "loop-every",
333
+ children: "How often to look"
334
+ }),
335
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
336
+ className: "text-xs text-muted-foreground",
337
+ children: "Waiting longer is not only about being polite to the service. Anything that decides what deserves an agent by weighing a batch needs a batch to weigh, and looking constantly means finding one thing at a time and running all of them."
338
+ }),
339
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Select$1, {
340
+ value: pollEveryMs,
341
+ onValueChange: (value) => value && setPollEveryMs(value),
342
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectTrigger, {
343
+ id: "loop-every",
344
+ className: "w-full sm:max-w-xs",
345
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectValue, { children: (value) => POLL_CHOICES.find((choice) => choice.value === value)?.label ?? value })
346
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectContent, { children: POLL_CHOICES.map((choice) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
347
+ value: choice.value,
348
+ children: choice.label
349
+ }, choice.value)) })]
350
+ })
351
+ ]
352
+ }),
353
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
354
+ className: "flex flex-col gap-2 sm:max-w-xs",
355
+ children: [
356
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Label, {
357
+ htmlFor: "loop-agent",
358
+ children: "Run it with"
359
+ }),
360
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Select$1, {
361
+ value: agentId,
362
+ onValueChange: (value) => value && setAgentId(value),
363
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectTrigger, {
364
+ id: "loop-agent",
365
+ className: "w-full",
366
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectValue, { children: (value) => value === DEFAULT_AGENT ? "The default agent" : AGENT_MANIFESTS.find((entry) => entry.id === value)?.name ?? value })
367
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectContent, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
368
+ value: DEFAULT_AGENT,
369
+ children: "The default agent"
370
+ }), AGENT_MANIFESTS.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
371
+ value: entry.id,
372
+ children: entry.name
373
+ }, entry.id))] })]
374
+ }),
375
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
376
+ className: "text-xs text-muted-foreground",
377
+ children: "The dispatcher picks an online runner that has this agent signed in."
378
+ })
379
+ ]
380
+ }),
381
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
382
+ className: "flex items-center justify-between gap-4 border-t pt-5",
383
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Label, {
384
+ htmlFor: "loop-enabled",
385
+ children: "Enabled"
386
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
387
+ className: "mt-1 text-xs text-muted-foreground",
388
+ children: "A disabled loop is kept but never matches."
389
+ })] }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Switch$1, {
390
+ id: "loop-enabled",
391
+ checked: enabled,
392
+ onCheckedChange: setEnabled
393
+ })]
394
+ })
395
+ ]
396
+ }) }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
397
+ className: "flex items-center gap-2",
398
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
399
+ onClick: save,
400
+ disabled: saving,
401
+ children: saving ? fresh ? "Adding..." : "Saving..." : fresh ? "Add loop" : "Save loop"
402
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
403
+ variant: "ghost",
404
+ onClick: () => navigate({ to: fresh ? "/loops/new" : "/loops" }),
405
+ disabled: saving,
406
+ children: "Cancel"
407
+ })]
408
+ })]
409
+ });
410
+ }
411
+ //#endregion
412
+ export { LoopForm as t };