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.
Files changed (134) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/_connectorId-CDCuSgpN.js +1 -0
  3. package/.output/public/assets/_loopId-DOnIsVTW.js +1 -0
  4. package/.output/public/assets/_taskId-BO5MZnlZ.js +1 -0
  5. package/.output/public/assets/agents-CBozi7oi.js +1 -0
  6. package/.output/public/assets/alert-Bn_wjgTX.js +1 -0
  7. package/.output/public/assets/button-K3ypIhAZ.js +1 -0
  8. package/.output/public/assets/card-DiVnaTIE.js +1 -0
  9. package/.output/public/assets/chevron-right-BrctLtFK.js +1 -0
  10. package/.output/public/assets/connection-status-DKWelvLO.js +1 -0
  11. package/.output/public/assets/connector-icon-CEbdE71G.js +1 -0
  12. package/.output/public/assets/connectors-JyX3IzJj.js +1 -0
  13. package/.output/public/assets/gaps-PjmcbaWo.js +1 -0
  14. package/.output/public/assets/inbox-CujRR0Cz.js +1 -0
  15. package/.output/public/assets/index-C9SQ_Baq.js +16 -0
  16. package/.output/public/assets/loop-form-Dw3cuTzw.js +1 -0
  17. package/.output/public/assets/loops-DcEfZa2_.js +1 -0
  18. package/.output/public/assets/manifests-yJpevyye.js +1 -0
  19. package/.output/public/assets/new-CwsnNuRW.js +1 -0
  20. package/.output/public/assets/page-bvnfRMum.js +1 -0
  21. package/.output/public/assets/play-yHLs4DjK.js +1 -0
  22. package/.output/public/assets/plus-Ber25lrU.js +1 -0
  23. package/.output/public/assets/preload-helper-BoU_7EQC.js +3 -0
  24. package/.output/public/assets/{refresh-cw-C3xBnKfy.js → refresh-cw-BzSY7M8I.js} +1 -1
  25. package/.output/public/assets/rotate-ccw-DLv9EfCX.js +1 -0
  26. package/.output/public/assets/runners-DzX-RU3i.js +2 -0
  27. package/.output/public/assets/select-C9grIFwG.js +1 -0
  28. package/.output/public/assets/setting-fields-DUc3npgh.js +3 -0
  29. package/.output/public/assets/styles-B9B98zLk.css +2 -0
  30. package/.output/public/assets/switch-DwABVI6d.js +1 -0
  31. package/.output/public/assets/task-list-Ddk8-qt7.js +1 -0
  32. package/.output/public/assets/textarea-Ds-gIYUQ.js +1 -0
  33. package/.output/public/assets/time-goBWd-5J.js +1 -0
  34. package/.output/public/assets/use-live-tasks-DZNNuECy.js +1 -0
  35. package/.output/public/assets/useCompositeListItem-P4Nmmn7j.js +1 -0
  36. package/.output/public/assets/usePositioner-B7i9Ejzh.js +1 -0
  37. package/.output/server/_chunks/ssr-renderer.mjs +1 -1
  38. package/.output/server/{_connectorId-C9yydJrC.mjs → _connectorId-COuQN02a.mjs} +198 -188
  39. package/.output/server/_libs/@base-ui/react+[...].mjs +1410 -804
  40. package/.output/server/_libs/@tanstack/react-router+[...].mjs +1 -1
  41. package/.output/server/_libs/lucide-react.mjs +93 -84
  42. package/.output/server/_libs/next-themes.mjs +1 -1
  43. package/.output/server/_libs/sonner.mjs +1 -1
  44. package/.output/server/_loopId-CZh6_Yhr.mjs +310 -0
  45. package/.output/server/_ssr/{agents-DG643yoJ.mjs → agents-B_qo1gpL.mjs} +195 -6
  46. package/.output/server/_ssr/{agents-C7B6RJ6-.mjs → agents-Bz10gz_m.mjs} +8 -8
  47. package/.output/server/_ssr/agents-CjbOUp3Z.mjs +206 -0
  48. package/.output/server/_ssr/{alert-DQvzE7bu.mjs → alert-C2WBWfzo.mjs} +2 -2
  49. package/.output/server/_ssr/card-r1cV-PCN.mjs +36 -0
  50. package/.output/server/_ssr/{client-BtVRVhQ2.mjs → client-Dk3SD8Il.mjs} +5 -1
  51. package/.output/server/_ssr/connection-status-BogAL-Fj.mjs +32 -0
  52. package/.output/server/_ssr/{connections-Df-aDpfJ.mjs → connections-Cqvb6Gw2.mjs} +372 -325
  53. package/.output/server/_ssr/connector-icon-DrhVbhs6.mjs +190 -0
  54. package/.output/server/_ssr/connectors-BfS5tQKr.mjs +81 -0
  55. package/.output/server/_ssr/{connectors-e0mf2pMs.mjs → connectors-DJI-rzPU.mjs} +10 -10
  56. package/.output/server/_ssr/{createServerRpc-DPDrnFdH.mjs → createServerRpc-DDrp2nLT.mjs} +2 -2
  57. package/.output/server/_ssr/{gaps-Cz76ZicM.mjs → gaps-CcvHiLcj.mjs} +15 -7
  58. package/.output/server/_ssr/inbox-D1ef761v.mjs +178 -0
  59. package/.output/server/_ssr/loop-form-DWwOR05R.mjs +346 -0
  60. package/.output/server/_ssr/{loops-D8WuK7Ke.mjs → loops-6WhUe_lc.mjs} +37 -27
  61. package/.output/server/_ssr/loops-DHPjzG-t.mjs +217 -0
  62. package/.output/server/_ssr/new-ChKBdcXJ.mjs +245 -0
  63. package/.output/server/_ssr/page-DZVxKvdn.mjs +120 -0
  64. package/.output/server/_ssr/{router-54IDWTVz.mjs → router-DdwREiOf.mjs} +162 -489
  65. package/.output/server/_ssr/runners-CK9Cx9fS.mjs +217 -0
  66. package/.output/server/_ssr/runners-CrznFPHt.mjs +140 -0
  67. package/.output/server/_ssr/{runners-Cj6z9Mqh.mjs → runners-W-pgo5yo.mjs} +5 -5
  68. package/.output/server/_ssr/{secrets-CRnpwGBh.mjs → secrets-CV5scFlt.mjs} +2 -2
  69. package/.output/server/_ssr/select-BtndHhHb.mjs +76 -0
  70. package/.output/server/_ssr/{setting-fields-DzBlslan.mjs → setting-fields-LNgywAW7.mjs} +25 -20
  71. package/.output/server/_ssr/ssr.mjs +43 -42
  72. package/.output/server/_ssr/{start-Vq3Oan3v.mjs → start-C_j-e4vF.mjs} +9 -4
  73. package/.output/server/_ssr/{switch-1pnd_bPJ.mjs → switch-odxNO_S4.mjs} +3 -3
  74. package/.output/server/_ssr/task-list-Dnmq_WeE.mjs +150 -0
  75. package/.output/server/_ssr/{tasks-Qtml5TlK.mjs → tasks-D-kXLgVP.mjs} +13 -13
  76. package/.output/server/_ssr/{tasks-BDqznm57.mjs → tasks-D7aUvA33.mjs} +44 -17
  77. package/.output/server/_ssr/{textarea-CHJTKtsJ.mjs → textarea-D2c_vTIC.mjs} +2 -2
  78. package/.output/server/_ssr/time-Cy8TZP0Z.mjs +13 -0
  79. package/.output/server/_ssr/use-live-tasks-BzhTkEOL.mjs +40 -0
  80. package/.output/server/_tanstack-start-manifest_v-2HUdn-2E.mjs +166 -0
  81. package/.output/server/_taskId-D3KkNJW1.mjs +263 -0
  82. package/.output/server/index.mjs +199 -164
  83. package/README.md +36 -6
  84. package/dist/cli.mjs +161 -8
  85. package/dist/dispatcher.mjs +671 -285
  86. package/dist/runner.mjs +333 -46
  87. package/drizzle/0025_prompt_tasks_done.sql +2 -0
  88. package/drizzle/0026_guidance_into_prompt.sql +3 -0
  89. package/drizzle/0027_relative_folders.sql +5 -0
  90. package/drizzle/0028_runner_version.sql +5 -0
  91. package/drizzle/meta/_journal.json +29 -1
  92. package/package.json +4 -2
  93. package/.output/public/assets/_connectorId-CkygNLny.js +0 -1
  94. package/.output/public/assets/_loopId-Cshlc5Ja.js +0 -1
  95. package/.output/public/assets/_taskId-BKDIJ_Fg.js +0 -1
  96. package/.output/public/assets/alert-BT2EVMJP.js +0 -1
  97. package/.output/public/assets/arrow-left-CWUZt6k2.js +0 -1
  98. package/.output/public/assets/arrow-right-dy_oOg_c.js +0 -1
  99. package/.output/public/assets/connection-status-DCqqmY_6.js +0 -1
  100. package/.output/public/assets/connector-icon-BUoK6Cru.js +0 -1
  101. package/.output/public/assets/connectors-CeLBtEtg.js +0 -1
  102. package/.output/public/assets/external-link-DC11orFA.js +0 -1
  103. package/.output/public/assets/gaps-BtFoD7mF.js +0 -1
  104. package/.output/public/assets/inbox-BYNjkNDJ.js +0 -1
  105. package/.output/public/assets/index-0yP3Jn_G.js +0 -18
  106. package/.output/public/assets/loader-B4Epchm_.js +0 -1
  107. package/.output/public/assets/loop-form-DnSJNc1k.js +0 -1
  108. package/.output/public/assets/loops-1CvbqO6m.js +0 -1
  109. package/.output/public/assets/new-m5NlqYQT.js +0 -1
  110. package/.output/public/assets/play-DD4xQgvi.js +0 -1
  111. package/.output/public/assets/preload-helper-CfdAYKD7.js +0 -3
  112. package/.output/public/assets/rolldown-runtime-CbXtAM7H.js +0 -1
  113. package/.output/public/assets/runners-D8QVl-Y6.js +0 -1
  114. package/.output/public/assets/select-Cppu-hti.js +0 -1
  115. package/.output/public/assets/setting-fields-bJ_gTG7Q.js +0 -3
  116. package/.output/public/assets/styles-ClTGaXU0.css +0 -2
  117. package/.output/public/assets/switch-hRIbNGOj.js +0 -1
  118. package/.output/public/assets/task-list-BAxjcVgr.js +0 -1
  119. package/.output/public/assets/textarea-Fyuzi6Vp.js +0 -1
  120. package/.output/public/assets/trash-2-BXZzBsoV.js +0 -1
  121. package/.output/public/assets/useValueChanged-VVyq4hb9.js +0 -1
  122. package/.output/server/_loopId-B0VG-eBC.mjs +0 -301
  123. package/.output/server/_ssr/connection-status-BZr3Y-IC.mjs +0 -31
  124. package/.output/server/_ssr/connector-icon-WvxwZ5TF.mjs +0 -56
  125. package/.output/server/_ssr/connectors-BU0IiRge.mjs +0 -107
  126. package/.output/server/_ssr/inbox-CAUBa1I3.mjs +0 -114
  127. package/.output/server/_ssr/loop-form-FqSaXiHl.mjs +0 -413
  128. package/.output/server/_ssr/loops-Dqoebe8q.mjs +0 -222
  129. package/.output/server/_ssr/new-bBsVaWD6.mjs +0 -169
  130. package/.output/server/_ssr/runners-D2UAdLyj.mjs +0 -106
  131. package/.output/server/_ssr/runners-DGWaVvWi.mjs +0 -116
  132. package/.output/server/_ssr/task-list-_qgBS9LU.mjs +0 -187
  133. package/.output/server/_tanstack-start-manifest_v-CvheE7ZF.mjs +0 -131
  134. package/.output/server/_taskId-V4QLBO2N.mjs +0 -256
@@ -1,413 +0,0 @@
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 connectorAction, c as connectorWorkflow, o as connectorManifest, t as CONNECTOR_MANIFESTS } from "./connections-Df-aDpfJ.mjs";
5
- import { t as AGENT_MANIFESTS } from "./agents-DG643yoJ.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 { B as Card, D as saveLoop, S as Input$1, V as CardContent, _ as SelectItem, b as Label, g as SelectContent, h as Select$1, v as SelectTrigger, y as SelectValue, z as Button$1 } from "./router-54IDWTVz.mjs";
9
- import { t as Switch$1 } from "./switch-1pnd_bPJ.mjs";
10
- import { t as Textarea } from "./textarea-CHJTKtsJ.mjs";
11
- import { n as initialFieldValues, t as SettingFieldInputs } from "./setting-fields-DzBlslan.mjs";
12
- import { n as AlertDescription, r as AlertTitle, t as Alert } from "./alert-DQvzE7bu.mjs";
13
- //#region node_modules/.nitro/vite/services/ssr/assets/loop-form-FqSaXiHl.js
14
- var import_react = /* @__PURE__ */ __toESM(require_react());
15
- var import_jsx_runtime = require_jsx_runtime();
16
- var DEFAULT_AGENT = "__default";
17
- /** Zero is stored as null: "as often as the dispatcher does" is not a duration. */
18
- var POLL_CHOICES = [
19
- {
20
- value: "0",
21
- label: "Every time the dispatcher looks"
22
- },
23
- {
24
- value: String(6e5),
25
- label: "Every 10 minutes"
26
- },
27
- {
28
- value: String(18e5),
29
- label: "Every 30 minutes"
30
- },
31
- {
32
- value: String(36e5),
33
- label: "Every hour"
34
- },
35
- {
36
- value: String(144e5),
37
- label: "Every 4 hours"
38
- }
39
- ];
40
- /** Connectors that can write at all, for the question of where the answer goes. */
41
- var WRITERS = CONNECTOR_MANIFESTS.filter((entry) => entry.actions.length > 0);
42
- /**
43
- * Shown rather than hidden, and locked rather than editable.
44
- *
45
- * These are the parts of a workflow that only mean anything alongside the code
46
- * that reads them: the query a connector sends, whether the agent gets a
47
- * checkout, how its answer is parsed. Copying them onto a loop would freeze
48
- * whatever was true the day it was made, and leaving them off the page would
49
- * mean nobody could see what their loop actually does.
50
- */
51
- function Locked({ label, value, help, mono }) {
52
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
53
- className: "flex flex-col gap-2",
54
- children: [
55
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Label, {
56
- className: "text-muted-foreground",
57
- children: label
58
- }),
59
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Input$1, {
60
- readOnly: true,
61
- disabled: true,
62
- value,
63
- className: mono ? "font-mono text-xs disabled:opacity-100" : "disabled:opacity-100"
64
- }),
65
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
66
- className: "text-xs text-muted-foreground",
67
- children: help
68
- })
69
- ]
70
- });
71
- }
72
- /**
73
- * A loop is one of a connector's workflows with its own answers to the
74
- * questions it asks. Which of those a loop owns is the whole shape of this
75
- * page: what it looks for and how the answer is read belong to the workflow
76
- * and are locked, while what narrows it, where it writes and which agent runs
77
- * it belong to the loop.
78
- *
79
- * Serves a loop that exists and one that does not. A loop with no id has been
80
- * chosen and not saved, so leaving this page is the end of it: nothing was
81
- * written down, and nothing is watching for anything.
82
- */
83
- function LoopForm({ loop }) {
84
- const navigate = useNavigate();
85
- const fresh = loop.id === null;
86
- const connector = connectorManifest(loop.connectorId);
87
- const workflow = connectorWorkflow(loop.connectorId, loop.workflowId);
88
- const [name, setName] = (0, import_react.useState)(loop.name);
89
- const [prompt, setPrompt] = (0, import_react.useState)(loop.prompt);
90
- const [guidance, setGuidance] = (0, import_react.useState)(loop.guidance ?? "");
91
- const [agentId, setAgentId] = (0, import_react.useState)(loop.agentId ?? DEFAULT_AGENT);
92
- const [enabled, setEnabled] = (0, import_react.useState)(loop.enabled);
93
- const [settings, setSettings] = (0, import_react.useState)(() => initialFieldValues(workflow?.settings ?? [], loop.settings));
94
- const [pollEveryMs, setPollEveryMs] = (0, import_react.useState)(String(loop.pollEveryMs ?? 0));
95
- const [actionConnectorId, setActionConnectorId] = (0, import_react.useState)(loop.actionConnectorId);
96
- const [actionId, setActionId] = (0, import_react.useState)(loop.actionId);
97
- const [actionTarget, setActionTarget] = (0, import_react.useState)(() => initialFieldValues(connectorAction(loop.actionConnectorId, loop.actionId)?.target ?? [], loop.actionTarget));
98
- const [saving, setSaving] = (0, import_react.useState)(false);
99
- const action = connectorAction(actionConnectorId, actionId);
100
- const writerName = connectorManifest(actionConnectorId)?.name ?? actionConnectorId;
101
- /** Changing where the answer goes changes what has to be said about it. */
102
- const chooseAction = (connectorId, id) => {
103
- setActionConnectorId(connectorId);
104
- setActionId(id);
105
- setActionTarget(initialFieldValues(connectorAction(connectorId, id)?.target ?? [], {}));
106
- };
107
- if (!workflow) return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Alert, {
108
- variant: "destructive",
109
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(AlertTitle, { children: "This workflow is no longer offered" }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(AlertDescription, { children: [
110
- connector?.name ?? loop.connectorId,
111
- " used to have ",
112
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { children: loop.workflowId }),
113
- " and no longer does, so this loop cannot run or be edited. Deleting it is the only thing left to do with it."
114
- ] })]
115
- });
116
- const save = async () => {
117
- setSaving(true);
118
- try {
119
- const saved = await saveLoop({ data: {
120
- id: loop.id,
121
- name,
122
- connectorId: loop.connectorId,
123
- workflowId: loop.workflowId,
124
- prompt,
125
- guidance: guidance.trim() || null,
126
- agentId: agentId === DEFAULT_AGENT ? null : agentId,
127
- settings,
128
- actionConnectorId,
129
- actionId,
130
- actionTarget,
131
- pollEveryMs: Number(pollEveryMs) || null,
132
- enabled
133
- } });
134
- toast.success(fresh ? `Added "${saved.name}"` : `Saved ${saved.name}`);
135
- await navigate({ to: "/loops" });
136
- } catch (error) {
137
- toast.error(error instanceof Error ? error.message : String(error));
138
- } finally {
139
- setSaving(false);
140
- }
141
- };
142
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
143
- className: "flex flex-col gap-6",
144
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Card, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(CardContent, {
145
- className: "flex flex-col gap-5 pt-6",
146
- children: [
147
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
148
- className: "flex items-start gap-3 rounded-lg bg-muted/50 p-4 text-sm",
149
- 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", {
150
- className: "flex flex-col gap-1",
151
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { children: [
152
- "When ",
153
- workflow.trigger,
154
- ", ",
155
- connector?.name ?? loop.connectorId,
156
- " hands it to an agent."
157
- ] }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", {
158
- className: "flex items-center gap-1.5 text-muted-foreground",
159
- 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."]
160
- })]
161
- })]
162
- }),
163
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
164
- className: "flex flex-col gap-2",
165
- children: [
166
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Label, {
167
- htmlFor: "loop-name",
168
- children: "Name"
169
- }),
170
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Input$1, {
171
- id: "loop-name",
172
- value: name,
173
- onChange: (event) => setName(event.target.value)
174
- }),
175
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
176
- className: "text-xs text-muted-foreground",
177
- children: "Only for your benefit, and worth changing if you run this workflow more than once."
178
- })
179
- ]
180
- }),
181
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Locked, {
182
- label: "What it looks for",
183
- mono: true,
184
- value: workflow.watches ?? "Nothing is asked for; it arrives as it happens",
185
- 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.`
186
- }),
187
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Locked, {
188
- label: "Where the agent runs",
189
- 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",
190
- 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."
191
- }),
192
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Locked, {
193
- label: "How its answer is read",
194
- 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",
195
- 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."
196
- }),
197
- workflow.settings.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
198
- className: "flex flex-col gap-5 rounded-lg border p-4",
199
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
200
- className: "text-sm font-medium",
201
- children: "Only when"
202
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SettingFieldInputs, {
203
- fields: workflow.settings,
204
- values: settings,
205
- idPrefix: "setting-",
206
- onChange: (key, value) => setSettings((prev) => ({
207
- ...prev,
208
- [key]: value
209
- }))
210
- })]
211
- }) : null,
212
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
213
- className: "flex flex-col gap-5 rounded-lg border p-4",
214
- children: [
215
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
216
- className: "flex flex-col gap-2",
217
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
218
- className: "text-sm font-medium",
219
- children: "Where the answer goes"
220
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
221
- className: "text-xs text-muted-foreground",
222
- 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."
223
- })]
224
- }),
225
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
226
- className: "flex flex-col gap-4 sm:flex-row",
227
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
228
- className: "flex flex-1 flex-col gap-2",
229
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Label, {
230
- htmlFor: "loop-action-connector",
231
- children: "Service"
232
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Select$1, {
233
- value: actionConnectorId,
234
- onValueChange: (value) => value && chooseAction(value, connectorManifest(value).actions[0].id),
235
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectTrigger, {
236
- id: "loop-action-connector",
237
- className: "w-full",
238
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectValue, { children: (value) => connectorManifest(value)?.name ?? value })
239
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectContent, { children: WRITERS.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
240
- value: entry.id,
241
- children: entry.name
242
- }, entry.id)) })]
243
- })]
244
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
245
- className: "flex flex-1 flex-col gap-2",
246
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Label, {
247
- htmlFor: "loop-action",
248
- children: "What it does there"
249
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Select$1, {
250
- value: actionId,
251
- onValueChange: (value) => value && chooseAction(actionConnectorId, value),
252
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectTrigger, {
253
- id: "loop-action",
254
- className: "w-full",
255
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectValue, { children: (value) => connectorAction(actionConnectorId, value)?.name ?? value })
256
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectContent, { children: (connectorManifest(actionConnectorId)?.actions ?? []).map((entry) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
257
- value: entry.id,
258
- children: entry.name
259
- }, entry.id)) })]
260
- })]
261
- })]
262
- }),
263
- action ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
264
- className: "text-xs text-muted-foreground",
265
- children: action.summary
266
- }) : null,
267
- 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: [
268
- writerName,
269
- " has no way to answer something on",
270
- " ",
271
- connector?.name ?? loop.connectorId,
272
- ", so choose below where this should land. Both accounts have to be connected for the loop to run."
273
- ] })] }) : null,
274
- action && workflow.answer === "review" && !action.accepts.includes("review") ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
275
- className: "text-xs text-muted-foreground",
276
- 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."
277
- }) : null,
278
- action && action.target.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SettingFieldInputs, {
279
- fields: action.target,
280
- values: actionTarget,
281
- idPrefix: "target-",
282
- onChange: (key, value) => setActionTarget((prev) => ({
283
- ...prev,
284
- [key]: value
285
- }))
286
- }) : null
287
- ]
288
- }),
289
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
290
- className: "flex flex-col gap-2",
291
- children: [
292
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Label, {
293
- htmlFor: "loop-prompt",
294
- children: "What the agent is asked to do"
295
- }),
296
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
297
- className: "text-xs text-muted-foreground",
298
- 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.`
299
- }),
300
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Textarea, {
301
- id: "loop-prompt",
302
- rows: 12,
303
- className: "font-mono text-xs",
304
- value: prompt,
305
- onChange: (event) => setPrompt(event.target.value)
306
- })
307
- ]
308
- }),
309
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
310
- className: "flex flex-col gap-2",
311
- children: [
312
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Label, {
313
- htmlFor: "loop-guidance",
314
- children: "Anything else the agent should know"
315
- }),
316
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
317
- className: "text-xs text-muted-foreground",
318
- 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."
319
- }),
320
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Textarea, {
321
- id: "loop-guidance",
322
- rows: 4,
323
- value: guidance,
324
- placeholder: workflow.guidancePlaceholder,
325
- onChange: (event) => setGuidance(event.target.value)
326
- })
327
- ]
328
- }),
329
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
330
- className: "flex flex-col gap-2",
331
- children: [
332
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Label, {
333
- htmlFor: "loop-every",
334
- children: "How often to look"
335
- }),
336
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
337
- className: "text-xs text-muted-foreground",
338
- 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."
339
- }),
340
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Select$1, {
341
- value: pollEveryMs,
342
- onValueChange: (value) => value && setPollEveryMs(value),
343
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectTrigger, {
344
- id: "loop-every",
345
- className: "w-full sm:max-w-xs",
346
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectValue, { children: (value) => POLL_CHOICES.find((choice) => choice.value === value)?.label ?? value })
347
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectContent, { children: POLL_CHOICES.map((choice) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
348
- value: choice.value,
349
- children: choice.label
350
- }, choice.value)) })]
351
- })
352
- ]
353
- }),
354
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
355
- className: "flex flex-col gap-2 sm:max-w-xs",
356
- children: [
357
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Label, {
358
- htmlFor: "loop-agent",
359
- children: "Run it with"
360
- }),
361
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Select$1, {
362
- value: agentId,
363
- onValueChange: (value) => value && setAgentId(value),
364
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectTrigger, {
365
- id: "loop-agent",
366
- className: "w-full",
367
- 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 })
368
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectContent, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
369
- value: DEFAULT_AGENT,
370
- children: "The default agent"
371
- }), AGENT_MANIFESTS.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
372
- value: entry.id,
373
- children: entry.name
374
- }, entry.id))] })]
375
- }),
376
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
377
- className: "text-xs text-muted-foreground",
378
- children: "The dispatcher picks an online runner that has this agent signed in."
379
- })
380
- ]
381
- }),
382
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
383
- className: "flex items-center justify-between gap-4 border-t pt-5",
384
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Label, {
385
- htmlFor: "loop-enabled",
386
- children: "Enabled"
387
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
388
- className: "mt-1 text-xs text-muted-foreground",
389
- children: "A disabled loop is kept but never matches."
390
- })] }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Switch$1, {
391
- id: "loop-enabled",
392
- checked: enabled,
393
- onCheckedChange: setEnabled
394
- })]
395
- })
396
- ]
397
- }) }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
398
- className: "flex items-center gap-2",
399
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
400
- onClick: save,
401
- disabled: saving,
402
- children: saving ? fresh ? "Adding..." : "Saving..." : fresh ? "Add loop" : "Save loop"
403
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
404
- variant: "ghost",
405
- onClick: () => navigate({ to: fresh ? "/loops/new" : "/loops" }),
406
- disabled: saving,
407
- children: "Cancel"
408
- })]
409
- })]
410
- });
411
- }
412
- //#endregion
413
- export { LoopForm as t };
@@ -1,222 +0,0 @@
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 { f as Link, m as useRouter } from "../_libs/@tanstack/react-router+[...].mjs";
4
- import { a as connectorAction, c as connectorWorkflow, o as connectorManifest } from "./connections-Df-aDpfJ.mjs";
5
- import { n as agentManifest } from "./agents-DG643yoJ.mjs";
6
- import { M as CircleAlert, R as ArrowUp, V as ArrowDown, i as Trash2, u as Plus } from "../_libs/lucide-react.mjs";
7
- import { n as toast } from "../_libs/sonner.mjs";
8
- import { B as Card, H as CardHeader, O as toggleLoop, R as Badge, T as reorderLoop, V as CardContent, f as useLiveRefresh, s as Route$11, w as removeLoop, z as Button$1 } from "./router-54IDWTVz.mjs";
9
- import { t as Switch$1 } from "./switch-1pnd_bPJ.mjs";
10
- import { t as ConnectorIcon } from "./connector-icon-WvxwZ5TF.mjs";
11
- import { n as AlertDescription, r as AlertTitle, t as Alert } from "./alert-DQvzE7bu.mjs";
12
- import { n as issuesFor, t as gapsFor } from "./gaps-Cz76ZicM.mjs";
13
- //#region node_modules/.nitro/vite/services/ssr/assets/loops-Dqoebe8q.js
14
- var import_react = /* @__PURE__ */ __toESM(require_react());
15
- var import_jsx_runtime = require_jsx_runtime();
16
- function LoopsPage() {
17
- const { loops, readiness } = Route$11.useLoaderData();
18
- useLiveRefresh();
19
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
20
- className: "flex flex-col gap-6",
21
- children: [
22
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", {
23
- className: "flex items-start gap-4",
24
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
25
- className: "flex-1",
26
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", {
27
- className: "text-2xl font-semibold tracking-tight",
28
- children: "Loops"
29
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
30
- className: "mt-1 text-sm text-muted-foreground",
31
- children: "Each loop runs one workflow on its own and writes the result back. The first loop that matches something is the one that runs, so order matters."
32
- })]
33
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button$1, {
34
- render: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Link, { to: "/loops/new" }),
35
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Plus, {}), "New loop"]
36
- })]
37
- }),
38
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Gaps, {
39
- readiness,
40
- loops
41
- }),
42
- loops.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Card, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(CardContent, {
43
- className: "flex flex-col items-center gap-3 py-10 text-center",
44
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
45
- className: "text-sm text-muted-foreground",
46
- children: "No loops yet, so nothing is being watched for."
47
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
48
- variant: "outline",
49
- render: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Link, { to: "/loops/new" }),
50
- children: "See what Loopable can do"
51
- })]
52
- }) }) : null,
53
- loops.map((loop, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LoopCard, {
54
- loop,
55
- readiness,
56
- position: index + 1,
57
- first: index === 0,
58
- last: index === loops.length - 1
59
- }, loop.id))
60
- ]
61
- });
62
- }
63
- /** Says what a loop still needs, without standing in the way of writing one. */
64
- function Gaps({ readiness, loops }) {
65
- const missing = gapsFor(readiness, loops);
66
- if (missing.length === 0) return null;
67
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Alert, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(AlertTitle, { children: "Loops can be written, but nothing will run yet" }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(AlertDescription, {
68
- className: "flex flex-col gap-1",
69
- children: [missing.map((line) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: line }, line)), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
70
- className: "mt-1 flex gap-3 text-xs",
71
- children: [
72
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Link, {
73
- to: "/connectors",
74
- className: "underline",
75
- children: "Connectors"
76
- }),
77
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Link, {
78
- to: "/agents",
79
- className: "underline",
80
- children: "Agents"
81
- }),
82
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Link, {
83
- to: "/runners",
84
- className: "underline",
85
- children: "Runners"
86
- })
87
- ]
88
- })]
89
- })] });
90
- }
91
- /**
92
- * Where this loop writes, in its own words rather than the workflow's. A loop
93
- * may have been pointed somewhere else since it was made, and the card is the
94
- * one place someone scanning a list would notice.
95
- */
96
- function writesOf(loop) {
97
- const action = connectorAction(loop.actionConnectorId, loop.actionId);
98
- if (!action) return `it writes with ${loop.actionId}, which is no longer offered`;
99
- const writer = connectorManifest(loop.actionConnectorId);
100
- const where = loop.actionConnectorId === loop.connectorId ? "" : ` on ${writer?.name ?? loop.actionConnectorId}`;
101
- return `it will ${action.name.toLowerCase()}${where}`;
102
- }
103
- function LoopCard({ loop, readiness, position, first, last }) {
104
- const router = useRouter();
105
- const manifest = connectorManifest(loop.connectorId);
106
- const workflow = connectorWorkflow(loop.connectorId, loop.workflowId);
107
- const agent = loop.agentId ? agentManifest(loop.agentId) : void 0;
108
- const issues = issuesFor(readiness, loop);
109
- const [busy, setBusy] = (0, import_react.useState)(false);
110
- const run = async (work, message) => {
111
- setBusy(true);
112
- try {
113
- await work();
114
- await router.invalidate();
115
- if (message) toast.success(message);
116
- } catch (error) {
117
- toast.error(error instanceof Error ? error.message : String(error));
118
- } finally {
119
- setBusy(false);
120
- }
121
- };
122
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Card, {
123
- className: loop.enabled ? void 0 : "opacity-70",
124
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(CardHeader, {
125
- className: "flex flex-row items-start gap-3",
126
- children: [
127
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
128
- className: "mt-0.5 w-5 text-sm text-muted-foreground",
129
- children: position
130
- }),
131
- manifest ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ConnectorIcon, {
132
- icon: manifest.icon,
133
- accent: manifest.accent
134
- }) : null,
135
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
136
- className: "flex-1",
137
- children: [
138
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
139
- className: "flex flex-wrap items-center gap-2",
140
- children: [
141
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Link, {
142
- to: "/loops/$loopId",
143
- params: { loopId: loop.id },
144
- className: "font-medium hover:underline",
145
- children: loop.name
146
- }),
147
- loop.enabled ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Badge, {
148
- variant: "secondary",
149
- children: "Off"
150
- }),
151
- issues.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Badge, {
152
- variant: "outline",
153
- children: "Cannot run"
154
- }) : null
155
- ]
156
- }),
157
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
158
- className: "mt-1 text-sm text-muted-foreground",
159
- children: workflow ? `When ${workflow.trigger}, ${writesOf(loop)}${agent ? `, using ${agent.name}` : ""}.` : `Built on ${loop.workflowId}, which is no longer offered.`
160
- }),
161
- issues.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", {
162
- className: "mt-2 flex items-start gap-1.5 text-xs text-amber-700 dark:text-amber-500",
163
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CircleAlert, { className: "mt-0.5 size-3.5 shrink-0" }), issues[0]]
164
- }) : null
165
- ]
166
- }),
167
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
168
- className: "flex items-center gap-1",
169
- children: [
170
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
171
- variant: "ghost",
172
- size: "icon",
173
- "aria-label": "Move up",
174
- disabled: busy || first,
175
- onClick: () => run(() => reorderLoop({ data: {
176
- id: loop.id,
177
- direction: "up"
178
- } })),
179
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ArrowUp, {})
180
- }),
181
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
182
- variant: "ghost",
183
- size: "icon",
184
- "aria-label": "Move down",
185
- disabled: busy || last,
186
- onClick: () => run(() => reorderLoop({ data: {
187
- id: loop.id,
188
- direction: "down"
189
- } })),
190
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ArrowDown, {})
191
- }),
192
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Switch$1, {
193
- checked: loop.enabled,
194
- "aria-label": "Enabled",
195
- disabled: busy,
196
- onCheckedChange: (enabled) => run(() => toggleLoop({ data: {
197
- id: loop.id,
198
- enabled
199
- } }), enabled ? `${loop.name} is on` : `${loop.name} is off`)
200
- }),
201
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
202
- variant: "ghost",
203
- size: "icon",
204
- "aria-label": "Delete",
205
- disabled: busy,
206
- onClick: () => run(() => removeLoop({ data: { id: loop.id } }), `Deleted ${loop.name}`),
207
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Trash2, {})
208
- })
209
- ]
210
- })
211
- ]
212
- }), loop.guidance ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardContent, {
213
- className: "border-t pt-4",
214
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
215
- className: "line-clamp-2 text-sm text-muted-foreground",
216
- children: loop.guidance
217
- })
218
- }) : null]
219
- });
220
- }
221
- //#endregion
222
- export { LoopsPage as component };