loopable-cli 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) 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-BzSY7M8I.js +1 -0
  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-COuQN02a.mjs +512 -0
  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/{callback-D20cVD2w.mjs → access-DTttr563.mjs} +37 -20
  46. package/.output/server/_ssr/{agents-CrUmIQpi.mjs → agents-B_qo1gpL.mjs} +196 -7
  47. package/.output/server/_ssr/{agents-Dms_6TBF.mjs → agents-Bz10gz_m.mjs} +8 -8
  48. package/.output/server/_ssr/agents-CjbOUp3Z.mjs +206 -0
  49. package/.output/server/_ssr/{alert-DQvzE7bu.mjs → alert-C2WBWfzo.mjs} +2 -2
  50. package/.output/server/_ssr/card-r1cV-PCN.mjs +36 -0
  51. package/.output/server/_ssr/{client-BtVRVhQ2.mjs → client-Dk3SD8Il.mjs} +5 -1
  52. package/.output/server/_ssr/connection-status-BogAL-Fj.mjs +32 -0
  53. package/.output/server/_ssr/{connections-ypex3qUd.mjs → connections-Cqvb6Gw2.mjs} +1109 -263
  54. package/.output/server/_ssr/connector-icon-DrhVbhs6.mjs +190 -0
  55. package/.output/server/_ssr/connectors-BfS5tQKr.mjs +81 -0
  56. package/.output/server/_ssr/{connectors-BZly0yGF.mjs → connectors-DJI-rzPU.mjs} +21 -10
  57. package/.output/server/_ssr/{createServerRpc-DPDrnFdH.mjs → createServerRpc-DDrp2nLT.mjs} +2 -2
  58. package/.output/server/_ssr/{gaps-D2Zh8sEo.mjs → gaps-CcvHiLcj.mjs} +18 -10
  59. package/.output/server/_ssr/inbox-D1ef761v.mjs +178 -0
  60. package/.output/server/_ssr/loop-form-DWwOR05R.mjs +346 -0
  61. package/.output/server/_ssr/{loops-Og_G3KyZ.mjs → loops-6WhUe_lc.mjs} +228 -25
  62. package/.output/server/_ssr/loops-DHPjzG-t.mjs +217 -0
  63. package/.output/server/_ssr/new-ChKBdcXJ.mjs +245 -0
  64. package/.output/server/_ssr/page-DZVxKvdn.mjs +120 -0
  65. package/.output/server/_ssr/{router-BbrBz36P.mjs → router-DdwREiOf.mjs} +167 -489
  66. package/.output/server/_ssr/runners-CK9Cx9fS.mjs +217 -0
  67. package/.output/server/_ssr/runners-CrznFPHt.mjs +140 -0
  68. package/.output/server/_ssr/runners-W-pgo5yo.mjs +55 -0
  69. package/.output/server/_ssr/{secrets-CRnpwGBh.mjs → secrets-CV5scFlt.mjs} +2 -2
  70. package/.output/server/_ssr/select-BtndHhHb.mjs +76 -0
  71. package/.output/server/_ssr/{setting-fields-mpXfGftI.mjs → setting-fields-LNgywAW7.mjs} +25 -20
  72. package/.output/server/_ssr/ssr.mjs +49 -40
  73. package/.output/server/_ssr/{start-BgTj-h_C.mjs → start-C_j-e4vF.mjs} +10 -6
  74. package/.output/server/_ssr/{switch-1pnd_bPJ.mjs → switch-odxNO_S4.mjs} +3 -3
  75. package/.output/server/_ssr/task-list-Dnmq_WeE.mjs +150 -0
  76. package/.output/server/_ssr/{tasks-LIi-W4cy.mjs → tasks-D-kXLgVP.mjs} +13 -13
  77. package/.output/server/_ssr/{tasks-B_QmDFVs.mjs → tasks-D7aUvA33.mjs} +55 -17
  78. package/.output/server/_ssr/{textarea-CHJTKtsJ.mjs → textarea-D2c_vTIC.mjs} +2 -2
  79. package/.output/server/_ssr/time-Cy8TZP0Z.mjs +13 -0
  80. package/.output/server/_ssr/use-live-tasks-BzhTkEOL.mjs +40 -0
  81. package/.output/server/_tanstack-start-manifest_v-2HUdn-2E.mjs +166 -0
  82. package/.output/server/_taskId-D3KkNJW1.mjs +263 -0
  83. package/.output/server/index.mjs +204 -162
  84. package/README.md +54 -19
  85. package/dist/cli.mjs +185 -67
  86. package/dist/dispatcher.mjs +1443 -277
  87. package/dist/runner.mjs +333 -46
  88. package/drizzle/0025_prompt_tasks_done.sql +2 -0
  89. package/drizzle/0026_guidance_into_prompt.sql +3 -0
  90. package/drizzle/0027_relative_folders.sql +5 -0
  91. package/drizzle/0028_runner_version.sql +5 -0
  92. package/drizzle/meta/_journal.json +29 -1
  93. package/package.json +23 -17
  94. package/.output/public/assets/_connectorId-m67WbiJ8.js +0 -1
  95. package/.output/public/assets/_loopId-Bf4K5sdd.js +0 -1
  96. package/.output/public/assets/_taskId-eCKkcVOx.js +0 -1
  97. package/.output/public/assets/alert-BT2EVMJP.js +0 -1
  98. package/.output/public/assets/arrow-left-CWUZt6k2.js +0 -1
  99. package/.output/public/assets/arrow-right-dy_oOg_c.js +0 -1
  100. package/.output/public/assets/connection-status-DCqqmY_6.js +0 -1
  101. package/.output/public/assets/connector-icon-qGpNsPgH.js +0 -1
  102. package/.output/public/assets/connectors-DxXlc9zF.js +0 -1
  103. package/.output/public/assets/external-link-DC11orFA.js +0 -1
  104. package/.output/public/assets/gaps-CoTA-PzA.js +0 -1
  105. package/.output/public/assets/inbox-CLEXUXCI.js +0 -1
  106. package/.output/public/assets/index-Cndsw84p.js +0 -16
  107. package/.output/public/assets/loader-B4Epchm_.js +0 -1
  108. package/.output/public/assets/loop-form-BvizlZdR.js +0 -1
  109. package/.output/public/assets/loops-Dbe_mf5T.js +0 -1
  110. package/.output/public/assets/new-DPEaXKAY.js +0 -1
  111. package/.output/public/assets/play-DD4xQgvi.js +0 -1
  112. package/.output/public/assets/preload-helper-CfdAYKD7.js +0 -3
  113. package/.output/public/assets/rolldown-runtime-CbXtAM7H.js +0 -1
  114. package/.output/public/assets/runners-BVBggS5B.js +0 -1
  115. package/.output/public/assets/select-Cppu-hti.js +0 -1
  116. package/.output/public/assets/setting-fields-BT1naVJX.js +0 -3
  117. package/.output/public/assets/styles-D3sonCfX.css +0 -2
  118. package/.output/public/assets/switch-hRIbNGOj.js +0 -1
  119. package/.output/public/assets/task-list-CrptnI1N.js +0 -1
  120. package/.output/public/assets/textarea-Fyuzi6Vp.js +0 -1
  121. package/.output/public/assets/trash-2-BXZzBsoV.js +0 -1
  122. package/.output/public/assets/useValueChanged-VVyq4hb9.js +0 -1
  123. package/.output/server/_connectorId-DcSnoRjk.mjs +0 -421
  124. package/.output/server/_loopId-G_TAkYtS.mjs +0 -272
  125. package/.output/server/_ssr/access-B7TcOZ7F.mjs +0 -42
  126. package/.output/server/_ssr/connection-status-BZr3Y-IC.mjs +0 -31
  127. package/.output/server/_ssr/connector-icon-CFrrHjzR.mjs +0 -37
  128. package/.output/server/_ssr/connectors-4yzKb0KG.mjs +0 -106
  129. package/.output/server/_ssr/inbox-C2VBGLa5.mjs +0 -114
  130. package/.output/server/_ssr/loop-form-D-U-kU4k.mjs +0 -413
  131. package/.output/server/_ssr/loops-D0XJ8eBb.mjs +0 -222
  132. package/.output/server/_ssr/new-CajvgIog.mjs +0 -169
  133. package/.output/server/_ssr/runners-B_WuF8UJ.mjs +0 -32
  134. package/.output/server/_ssr/runners-D-0_lfiq.mjs +0 -106
  135. package/.output/server/_ssr/runners-zqF_zbEc.mjs +0 -116
  136. package/.output/server/_ssr/task-list-BnQJ94Dk.mjs +0 -187
  137. package/.output/server/_tanstack-start-manifest_v-BCHAazE8.mjs +0 -129
  138. package/.output/server/_taskId-CIv0ajIx.mjs +0 -256
@@ -1,24 +1,42 @@
1
1
  import { f as eq } from "../_libs/drizzle-orm.mjs";
2
- import { n as agentSettings, r as appSettings, s as db } from "./client-BtVRVhQ2.mjs";
3
- import { n as isHosted } from "./secrets-CRnpwGBh.mjs";
2
+ import { n as agentSettings, r as appSettings, s as db } from "./client-Dk3SD8Il.mjs";
3
+ import { n as isHosted } from "./secrets-CV5scFlt.mjs";
4
4
  import { accessSync, constants, createWriteStream, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
5
5
  import { join } from "node:path";
6
6
  import { homedir, tmpdir } from "node:os";
7
7
  import { execFile, spawn } from "node:child_process";
8
8
  import { promisify } from "node:util";
9
- //#region node_modules/.nitro/vite/services/ssr/assets/agents-CrUmIQpi.js
9
+ //#region node_modules/.nitro/vite/services/ssr/assets/agents-B_qo1gpL.js
10
10
  function defineAgentManifest(manifest) {
11
11
  return manifest;
12
12
  }
13
13
  function defineAgentRuntime(runtime) {
14
14
  return runtime;
15
15
  }
16
+ var claudeManifest = defineAgentManifest({
17
+ id: "claude",
18
+ name: "Claude Code",
19
+ tagline: "Anthropic's coding agent, driven through its print mode.",
20
+ docsUrl: "https://docs.claude.com/en/docs/claude-code/cli-reference",
21
+ icon: "ClaudeCode",
22
+ accent: "bg-[#d97757] text-white",
23
+ binaries: ["claude"],
24
+ installHint: "Install with curl -fsSL https://claude.ai/install.sh | bash, then run claude auth login.",
25
+ permissionModes: ["read_only", "workspace_write"],
26
+ supportsModel: true,
27
+ modelPlaceholder: "e.g. sonnet or opus; leave empty for the default",
28
+ defaults: {
29
+ permissionMode: "read_only",
30
+ model: null,
31
+ timeoutMs: 3e5
32
+ }
33
+ });
16
34
  var codexManifest = defineAgentManifest({
17
35
  id: "codex",
18
36
  name: "Codex",
19
37
  tagline: "OpenAI's coding agent, driven non-interactively through codex exec.",
20
38
  docsUrl: "https://developers.openai.com/codex/cli",
21
- icon: "Terminal",
39
+ icon: "Codex",
22
40
  accent: "bg-neutral-900 text-white",
23
41
  binaries: ["codex"],
24
42
  installHint: "Install with npm i -g @openai/codex, then run codex login.",
@@ -36,7 +54,7 @@ var cursorAgentManifest = defineAgentManifest({
36
54
  name: "Cursor Agent",
37
55
  tagline: "Cursor's CLI agent, driven through its print mode.",
38
56
  docsUrl: "https://cursor.com/docs/cli",
39
- icon: "MousePointerClick",
57
+ icon: "Cursor",
40
58
  accent: "bg-blue-600 text-white",
41
59
  binaries: ["cursor-agent"],
42
60
  installHint: "Install with curl https://cursor.com/install -fsS | bash, then run cursor-agent login.",
@@ -50,7 +68,11 @@ var cursorAgentManifest = defineAgentManifest({
50
68
  }
51
69
  });
52
70
  /** Client-safe registry, listing only agents whose invocation we have verified. */
53
- var AGENT_MANIFESTS = [codexManifest, cursorAgentManifest];
71
+ var AGENT_MANIFESTS = [
72
+ codexManifest,
73
+ claudeManifest,
74
+ cursorAgentManifest
75
+ ];
54
76
  function agentManifest(id) {
55
77
  return AGENT_MANIFESTS.find((manifest) => manifest.id === id);
56
78
  }
@@ -268,6 +290,172 @@ function runProcess(input) {
268
290
  child.on("close", (code) => finish(code));
269
291
  });
270
292
  }
293
+ function parse$1(line) {
294
+ const trimmed = line.trim();
295
+ if (!trimmed.startsWith("{")) return null;
296
+ try {
297
+ return JSON.parse(trimmed);
298
+ } catch {
299
+ return null;
300
+ }
301
+ }
302
+ function blocks(event) {
303
+ return event?.type === "assistant" ? event.message?.content ?? [] : [];
304
+ }
305
+ /** `Read` with a file_path becomes `read <path>`. */
306
+ function describeTool$1(block, cwd) {
307
+ const verb = (block.name ?? "a tool").toLowerCase();
308
+ const input = block.input ?? {};
309
+ const subject = input.file_path ?? input.path ?? input.command ?? input.pattern ?? input.url ?? input.query;
310
+ if (typeof subject !== "string") return verb;
311
+ return `${verb} ${cwd && subject.startsWith(`${cwd}/`) ? subject.slice(cwd.length + 1) : subject}`;
312
+ }
313
+ /**
314
+ * One readable line for the log, or nothing when the event says nothing a
315
+ * person would want to read. Speech is left out: the answer is shown in full
316
+ * elsewhere, and the narration in between is what the tool lines already say.
317
+ */
318
+ function describeEvent$1(line, cwd) {
319
+ const event = parse$1(line);
320
+ if (!event) return line.trim() === "" ? null : line;
321
+ switch (event.type) {
322
+ case "system":
323
+ if (event.subtype !== "init") return null;
324
+ return event.model ? `Started with ${event.model}.` : "Started.";
325
+ case "assistant": {
326
+ const tools = blocks(event).filter((block) => block.type === "tool_use").map((block) => `Running ${describeTool$1(block, cwd)}`);
327
+ return tools.length > 0 ? tools.join("\n") : null;
328
+ }
329
+ case "result": {
330
+ const seconds = event.duration_ms !== void 0 ? (event.duration_ms / 1e3).toFixed(0) : "?";
331
+ const tokens = event.usage?.output_tokens;
332
+ const spent = tokens === void 0 ? "" : `, ${tokens} tokens out`;
333
+ return event.is_error ? `Failed after ${seconds}s${spent}: ${event.result ?? event.subtype ?? "unknown error"}` : `Answered after ${seconds}s${spent}.`;
334
+ }
335
+ default: return null;
336
+ }
337
+ }
338
+ /**
339
+ * The answer, which is the agent's last message and not everything it said.
340
+ *
341
+ * The result event carries exactly that, so it wins when present. A stopped
342
+ * run never gets one; then the text after the last tool use is the block it
343
+ * was in the middle of, and earlier text is it thinking out loud.
344
+ */
345
+ function answerFrom$1(stdout) {
346
+ const events = stdout.split("\n").map(parse$1);
347
+ const result = events.filter((event) => event?.type === "result").at(-1);
348
+ const failed = result?.is_error === true;
349
+ if (typeof result?.result === "string" && result.result.trim() !== "") return {
350
+ text: result.result.trim(),
351
+ failed
352
+ };
353
+ const said = events.flatMap(blocks);
354
+ const lastTool = said.findLastIndex((block) => block.type === "tool_use");
355
+ return {
356
+ text: said.slice(lastTool + 1).filter((block) => block.type === "text").map((block) => block.text ?? "").join("\n").trim(),
357
+ failed
358
+ };
359
+ }
360
+ async function binary$2() {
361
+ const found = await resolveBinary(claudeManifest.binaries);
362
+ if (!found) throw new Error("The claude binary was not found on this host.");
363
+ return found;
364
+ }
365
+ function args$2(input) {
366
+ const list = [
367
+ "-p",
368
+ "--output-format",
369
+ "stream-json",
370
+ "--verbose",
371
+ "--no-session-persistence"
372
+ ];
373
+ if (input.settings.permissionMode === "read_only") list.push("--permission-mode", "dontAsk", "--tools", "Read,Grep,Glob");
374
+ else list.push("--permission-mode", "acceptEdits", "--allowedTools", "Bash");
375
+ if (input.settings.model) list.push("--model", input.settings.model);
376
+ list.push("--", input.prompt);
377
+ return list;
378
+ }
379
+ var claudeRuntime = defineAgentRuntime({
380
+ async detect() {
381
+ const found = await resolveBinary(claudeManifest.binaries);
382
+ if (!found) return { installed: false };
383
+ return {
384
+ installed: true,
385
+ binaryPath: found,
386
+ version: firstLine((await probe(found, ["--version"])).text)
387
+ };
388
+ },
389
+ async authStatus() {
390
+ const found = await resolveBinary(claudeManifest.binaries);
391
+ if (!found) return {
392
+ signedIn: false,
393
+ detail: "Not installed."
394
+ };
395
+ const result = await probe(found, [
396
+ "auth",
397
+ "status",
398
+ "--json"
399
+ ]);
400
+ try {
401
+ const status = JSON.parse(result.text);
402
+ return status.loggedIn ? {
403
+ signedIn: true,
404
+ detail: `Signed in (${status.authMethod ?? "unknown method"}).`
405
+ } : {
406
+ signedIn: false,
407
+ detail: "Run claude auth login."
408
+ };
409
+ } catch {
410
+ return {
411
+ signedIn: result.ok,
412
+ detail: firstLine(result.text) || (result.ok ? "Signed in." : "Run claude auth login.")
413
+ };
414
+ }
415
+ },
416
+ invocation(input) {
417
+ return {
418
+ bin: claudeManifest.binaries[0],
419
+ args: args$2(input)
420
+ };
421
+ },
422
+ async run(input) {
423
+ const bin = await binary$2();
424
+ const argv = args$2(input);
425
+ const outcome = await runProcess({
426
+ bin,
427
+ args: argv,
428
+ cwd: input.cwd,
429
+ timeoutMs: input.settings.timeoutMs,
430
+ signal: input.signal,
431
+ logPath: input.logFile,
432
+ logLine: (line) => describeEvent$1(line, input.cwd)
433
+ });
434
+ const command = `${bin} ${argv.join(" ")}`;
435
+ const answer = answerFrom$1(outcome.stdout);
436
+ if (outcome.aborted) return {
437
+ ok: false,
438
+ aborted: true,
439
+ output: answer.text,
440
+ durationMs: outcome.durationMs,
441
+ command
442
+ };
443
+ if (outcome.timedOut) return {
444
+ ok: false,
445
+ output: answer.text,
446
+ detail: `Claude Code did not finish within ${Math.round(input.settings.timeoutMs / 1e3)}s.`,
447
+ durationMs: outcome.durationMs,
448
+ command
449
+ };
450
+ return {
451
+ ok: outcome.code === 0 && !answer.failed,
452
+ output: answer.text,
453
+ detail: outcome.code === 0 && !answer.failed ? void 0 : answer.failed ? answer.text || "The agent reported an error." : outcome.stderr || `Exited with code ${outcome.code}.`,
454
+ durationMs: outcome.durationMs,
455
+ command
456
+ };
457
+ }
458
+ });
271
459
  async function binary$1() {
272
460
  const found = await resolveBinary(codexManifest.binaries);
273
461
  if (!found) throw new Error("The codex binary was not found on this host.");
@@ -458,6 +646,7 @@ function args(input) {
458
646
  /** Server-only registry. Never import this from a component. */
459
647
  var RUNTIMES = {
460
648
  codex: codexRuntime,
649
+ claude: claudeRuntime,
461
650
  "cursor-agent": defineAgentRuntime({
462
651
  async detect() {
463
652
  const found = await resolveBinary(cursorAgentManifest.binaries);
@@ -654,4 +843,4 @@ async function testAgent(agentId) {
654
843
  };
655
844
  }
656
845
  //#endregion
657
- export { setDefaultAgent as a, saveAgentSettings as i, agentManifest as n, settingsFor as o, listAgents as r, testAgent as s, AGENT_MANIFESTS as t };
846
+ export { saveAgentSettings as a, testAgent as c, listAgents as i, agentManifest as n, setDefaultAgent as o, agentRuntime as r, settingsFor as s, AGENT_MANIFESTS as t };
@@ -1,8 +1,8 @@
1
- import { n as createServerFn } from "./ssr.mjs";
2
- import { a as setDefaultAgent, i as saveAgentSettings, r as listAgents, s as testAgent } from "./agents-CrUmIQpi.mjs";
3
- import { c as listRunners, t as availableAgentIds } from "./runners-D-0_lfiq.mjs";
4
- import { t as createServerRpc } from "./createServerRpc-DPDrnFdH.mjs";
5
- //#region node_modules/.nitro/vite/services/ssr/assets/agents-Dms_6TBF.js
1
+ import { r as createServerFn } from "./ssr.mjs";
2
+ import { l as listRunners, n as availableAgentIds } from "./runners-CK9Cx9fS.mjs";
3
+ import { a as saveAgentSettings, c as testAgent, i as listAgents, o as setDefaultAgent } from "./agents-B_qo1gpL.mjs";
4
+ import { t as createServerRpc } from "./createServerRpc-DDrp2nLT.mjs";
5
+ //#region node_modules/.nitro/vite/services/ssr/assets/agents-Bz10gz_m.js
6
6
  async function withRunners(agents) {
7
7
  const runners = await listRunners();
8
8
  const available = await availableAgentIds();
@@ -40,7 +40,7 @@ var saveAgent_createServerFn_handler = createServerRpc({
40
40
  name: "saveAgent",
41
41
  filename: "src/server/functions/agents.ts"
42
42
  }, (opts) => saveAgent.__executeServer(opts));
43
- var saveAgent = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(saveAgent_createServerFn_handler, async ({ data }) => {
43
+ var saveAgent = createServerFn({ method: "POST" }).validator((data) => data).handler(saveAgent_createServerFn_handler, async ({ data }) => {
44
44
  await saveAgentSettings(data.agentId, {
45
45
  permissionMode: data.permissionMode,
46
46
  model: data.model,
@@ -53,7 +53,7 @@ var chooseDefaultAgent_createServerFn_handler = createServerRpc({
53
53
  name: "chooseDefaultAgent",
54
54
  filename: "src/server/functions/agents.ts"
55
55
  }, (opts) => chooseDefaultAgent.__executeServer(opts));
56
- var chooseDefaultAgent = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(chooseDefaultAgent_createServerFn_handler, async ({ data }) => {
56
+ var chooseDefaultAgent = createServerFn({ method: "POST" }).validator((data) => data).handler(chooseDefaultAgent_createServerFn_handler, async ({ data }) => {
57
57
  await setDefaultAgent(data.agentId);
58
58
  return await withRunners(await listAgents());
59
59
  });
@@ -62,7 +62,7 @@ var runAgentTest_createServerFn_handler = createServerRpc({
62
62
  name: "runAgentTest",
63
63
  filename: "src/server/functions/agents.ts"
64
64
  }, (opts) => runAgentTest.__executeServer(opts));
65
- var runAgentTest = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(runAgentTest_createServerFn_handler, async ({ data }) => {
65
+ var runAgentTest = createServerFn({ method: "POST" }).validator((data) => data).handler(runAgentTest_createServerFn_handler, async ({ data }) => {
66
66
  const result = await testAgent(data.agentId);
67
67
  return {
68
68
  passed: result.passed,
@@ -0,0 +1,206 @@
1
+ import { i as __toESM } from "../_runtime.mjs";
2
+ import { K as require_jsx_runtime, q as require_react } from "../_libs/@base-ui/react+[...].mjs";
3
+ import { m as useRouter } from "../_libs/@tanstack/react-router+[...].mjs";
4
+ import { t as AGENT_MANIFESTS } from "./agents-B_qo1gpL.mjs";
5
+ import { L as ChevronDown, k as CirclePlay } from "../_libs/lucide-react.mjs";
6
+ import { n as toast } from "../_libs/sonner.mjs";
7
+ import { P as cn, _ as Badge, f as Route$16, g as Input$1, h as saveAgent, m as runAgentTest, p as chooseDefaultAgent, v as Button$1 } from "./router-DdwREiOf.mjs";
8
+ import { n as ProductIcon } from "./connector-icon-DrhVbhs6.mjs";
9
+ import { a as List, o as PageHeader, r as Label } from "./page-DZVxKvdn.mjs";
10
+ import { a as SelectValue, i as SelectTrigger, n as SelectContent, r as SelectItem, t as Select$1 } from "./select-BtndHhHb.mjs";
11
+ import { n as useLiveRefresh } from "./use-live-tasks-BzhTkEOL.mjs";
12
+ //#region node_modules/.nitro/vite/services/ssr/assets/agents-CjbOUp3Z.js
13
+ var import_react = /* @__PURE__ */ __toESM(require_react());
14
+ var import_jsx_runtime = require_jsx_runtime();
15
+ var MODE_LABELS = {
16
+ read_only: "Read only",
17
+ workspace_write: "Can write in the workspace"
18
+ };
19
+ function AgentsPage() {
20
+ const { agents } = Route$16.useLoaderData();
21
+ useLiveRefresh();
22
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
23
+ className: "flex flex-col gap-6",
24
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(PageHeader, {
25
+ title: "Agents",
26
+ description: "The coding CLIs runners can use. Settings apply on every runner."
27
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(List, { children: AGENT_MANIFESTS.map((manifest) => {
28
+ const view = agents.find((agent) => agent.agentId === manifest.id);
29
+ return view ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AgentRow, {
30
+ manifest,
31
+ view
32
+ }, manifest.id) : null;
33
+ }) })]
34
+ });
35
+ }
36
+ function presence(view) {
37
+ const signedIn = view.onRunners.filter((runner) => runner.signedIn);
38
+ const online = signedIn.filter((runner) => runner.status === "online");
39
+ if (online.length > 0) return online.length === 1 ? `Signed in on ${online[0].name}` : `Signed in on ${online.length} runners`;
40
+ if (signedIn.length > 0) return "Signed in, but every runner is offline";
41
+ if (view.onRunners.length > 0) return "Installed, not signed in";
42
+ return "Not on any runner";
43
+ }
44
+ function AgentRow({ manifest, view }) {
45
+ const router = useRouter();
46
+ const [open, setOpen] = (0, import_react.useState)(view.isDefault && !view.defaultIsImplicit);
47
+ const [mode, setMode] = (0, import_react.useState)(view.settings.permissionMode);
48
+ const [model, setModel] = (0, import_react.useState)(view.settings.model ?? "");
49
+ const [seconds, setSeconds] = (0, import_react.useState)(String(Math.round(view.settings.timeoutMs / 1e3)));
50
+ const [busy, setBusy] = (0, import_react.useState)(null);
51
+ const [testOutput, setTestOutput] = (0, import_react.useState)(null);
52
+ const online = view.onRunners.some((runner) => runner.signedIn && runner.status === "online");
53
+ const act = async (kind, run) => {
54
+ setBusy(kind);
55
+ try {
56
+ await run();
57
+ } catch (error) {
58
+ toast.error(error instanceof Error ? error.message : String(error));
59
+ } finally {
60
+ setBusy(null);
61
+ }
62
+ };
63
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
64
+ className: "flex items-center gap-3 px-4 py-3",
65
+ children: [
66
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ProductIcon, {
67
+ icon: manifest.icon,
68
+ className: cn("size-8", online ? void 0 : "opacity-50 grayscale")
69
+ }),
70
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", {
71
+ type: "button",
72
+ className: "min-w-0 flex-1 text-left",
73
+ onClick: () => setOpen((value) => !value),
74
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
75
+ className: "flex flex-wrap items-center gap-2",
76
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
77
+ className: "text-sm font-medium",
78
+ children: manifest.name
79
+ }), view.isDefault ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Badge, {
80
+ variant: "secondary",
81
+ children: "Default"
82
+ }) : null]
83
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
84
+ className: "mt-0.5 text-xs text-muted-foreground",
85
+ children: presence(view)
86
+ })]
87
+ }),
88
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
89
+ className: "flex shrink-0 items-center gap-1",
90
+ children: [!view.isDefault ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
91
+ variant: "ghost",
92
+ size: "sm",
93
+ disabled: busy !== null,
94
+ onClick: () => act("default", async () => {
95
+ await chooseDefaultAgent({ data: { agentId: manifest.id } });
96
+ await router.invalidate();
97
+ toast.success(`${manifest.name} will run the loops`);
98
+ }),
99
+ children: "Make default"
100
+ }) : null, /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
101
+ variant: "ghost",
102
+ size: "icon-sm",
103
+ "aria-expanded": open,
104
+ "aria-label": "Settings",
105
+ onClick: () => setOpen((value) => !value),
106
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronDown, { className: cn("size-4 transition-transform", open && "rotate-180") })
107
+ })]
108
+ })
109
+ ]
110
+ }), open ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
111
+ className: "flex flex-col gap-4 border-t bg-muted/30 px-4 py-4 sm:pl-15",
112
+ children: [
113
+ !online ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
114
+ className: "text-xs text-muted-foreground",
115
+ children: manifest.installHint
116
+ }) : null,
117
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
118
+ className: "grid gap-4 sm:grid-cols-3",
119
+ children: [
120
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
121
+ className: "flex flex-col gap-2",
122
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Label, {
123
+ htmlFor: `${manifest.id}-mode`,
124
+ children: "Permission"
125
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Select$1, {
126
+ value: mode,
127
+ onValueChange: (value) => setMode(value),
128
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectTrigger, {
129
+ id: `${manifest.id}-mode`,
130
+ className: "w-full",
131
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectValue, { children: (value) => MODE_LABELS[value] ?? value })
132
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectContent, { children: manifest.permissionModes.map((value) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
133
+ value,
134
+ children: MODE_LABELS[value]
135
+ }, value)) })]
136
+ })]
137
+ }),
138
+ manifest.supportsModel ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
139
+ className: "flex flex-col gap-2",
140
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Label, {
141
+ htmlFor: `${manifest.id}-model`,
142
+ children: "Model"
143
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Input$1, {
144
+ id: `${manifest.id}-model`,
145
+ value: model,
146
+ placeholder: manifest.modelPlaceholder,
147
+ onChange: (event) => setModel(event.target.value)
148
+ })]
149
+ }) : null,
150
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
151
+ className: "flex flex-col gap-2",
152
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Label, {
153
+ htmlFor: `${manifest.id}-timeout`,
154
+ children: "Timeout (seconds)"
155
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Input$1, {
156
+ id: `${manifest.id}-timeout`,
157
+ value: seconds,
158
+ inputMode: "numeric",
159
+ onChange: (event) => setSeconds(event.target.value.replace(/\D/g, ""))
160
+ })]
161
+ })
162
+ ]
163
+ }),
164
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("pre", {
165
+ className: "overflow-x-auto rounded-md bg-muted p-2.5 text-xs",
166
+ children: view.commandPreview
167
+ }),
168
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
169
+ className: "flex flex-wrap items-center gap-2",
170
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
171
+ size: "sm",
172
+ disabled: busy !== null,
173
+ onClick: () => act("save", async () => {
174
+ await saveAgent({ data: {
175
+ agentId: manifest.id,
176
+ permissionMode: mode,
177
+ model: model.trim() ? model.trim() : null,
178
+ timeoutMs: Number(seconds) * 1e3
179
+ } });
180
+ await router.invalidate();
181
+ toast.success(`Saved ${manifest.name} settings`);
182
+ }),
183
+ children: busy === "save" ? "Saving..." : "Save"
184
+ }), view.installed ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button$1, {
185
+ variant: "outline",
186
+ size: "sm",
187
+ disabled: busy !== null,
188
+ onClick: () => act("test", async () => {
189
+ setTestOutput(null);
190
+ const result = await runAgentTest({ data: { agentId: manifest.id } });
191
+ setTestOutput(result.passed ? `Answered correctly in ${(result.durationMs / 1e3).toFixed(1)}s.` : `${result.detail ?? "The agent did not answer as expected."}\n${result.output}`.trim());
192
+ if (result.passed) toast.success(`${manifest.name} is working`);
193
+ else toast.error(`${manifest.name} did not pass`);
194
+ }),
195
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CirclePlay, {}), busy === "test" ? "Running..." : "Test"]
196
+ }) : null]
197
+ }),
198
+ testOutput ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
199
+ className: "whitespace-pre-wrap text-xs text-muted-foreground",
200
+ children: testOutput
201
+ }) : null
202
+ ]
203
+ }) : null] });
204
+ }
205
+ //#endregion
206
+ export { AgentsPage as component };
@@ -1,7 +1,7 @@
1
1
  import "../_runtime.mjs";
2
- import { U as require_jsx_runtime, W as require_react } from "../_libs/@base-ui/react+[...].mjs";
2
+ import { K as require_jsx_runtime, q as require_react } from "../_libs/@base-ui/react+[...].mjs";
3
3
  import { t as cva } from "../_libs/class-variance-authority+clsx.mjs";
4
- import { H as cn } from "./router-BbrBz36P.mjs";
4
+ import { P as cn } from "./router-DdwREiOf.mjs";
5
5
  require_react();
6
6
  var import_jsx_runtime = require_jsx_runtime();
7
7
  var alertVariants = cva("group/alert relative grid w-full gap-0.5 rounded-lg border px-2.5 py-2 text-left text-sm has-data-[slot=alert-action]:relative has-data-[slot=alert-action]:pr-18 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-2 *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*='size-'])]:size-4", {
@@ -0,0 +1,36 @@
1
+ import "../_runtime.mjs";
2
+ import { K as require_jsx_runtime, q as require_react } from "../_libs/@base-ui/react+[...].mjs";
3
+ import { P as cn } from "./router-DdwREiOf.mjs";
4
+ require_react();
5
+ var import_jsx_runtime = require_jsx_runtime();
6
+ function Card({ className, size = "default", ...props }) {
7
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
8
+ "data-slot": "card",
9
+ "data-size": size,
10
+ className: cn("group/card flex flex-col gap-(--card-spacing) overflow-hidden rounded-xl bg-card py-(--card-spacing) text-sm text-card-foreground ring-1 ring-foreground/10 [--card-spacing:--spacing(4)] has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:[--card-spacing:--spacing(3)] data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl", className),
11
+ ...props
12
+ });
13
+ }
14
+ function CardHeader({ className, ...props }) {
15
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
16
+ "data-slot": "card-header",
17
+ className: cn("group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-t-xl px-(--card-spacing) has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] [.border-b]:pb-(--card-spacing)", className),
18
+ ...props
19
+ });
20
+ }
21
+ function CardTitle({ className, ...props }) {
22
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
23
+ "data-slot": "card-title",
24
+ className: cn("font-heading text-base leading-snug font-medium group-data-[size=sm]/card:text-sm", className),
25
+ ...props
26
+ });
27
+ }
28
+ function CardContent({ className, ...props }) {
29
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
30
+ "data-slot": "card-content",
31
+ className: cn("px-(--card-spacing)", className),
32
+ ...props
33
+ });
34
+ }
35
+ //#endregion
36
+ export { CardTitle as i, CardContent as n, CardHeader as r, Card as t };
@@ -4,7 +4,7 @@ import { fileURLToPath, pathToFileURL } from "node:url";
4
4
  import { join } from "node:path";
5
5
  import { homedir } from "node:os";
6
6
  import { createClient } from "@libsql/client";
7
- //#region node_modules/.nitro/vite/services/ssr/assets/client-BtVRVhQ2.js
7
+ //#region node_modules/.nitro/vite/services/ssr/assets/client-Dk3SD8Il.js
8
8
  var __defProp = Object.defineProperty;
9
9
  var __exportAll = (all, no_symbols) => {
10
10
  let target = {};
@@ -294,6 +294,10 @@ var runners = sqliteTable("runners", {
294
294
  status: text("status").$type().notNull().default("offline"),
295
295
  inventory: text("inventory", { mode: "json" }).$type().notNull().default([]),
296
296
  lastSeenAt: integer("last_seen_at", { mode: "timestamp_ms" }),
297
+ /** The runner's loopable-cli version. Null for runners before 0.3. */
298
+ version: text("version"),
299
+ /** What jobs it understands; see RUNNER_PROTOCOL. Null means the first. */
300
+ protocol: integer("protocol"),
297
301
  createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull().default(sql`(unixepoch() * 1000)`)
298
302
  });
299
303
  function databaseUrl() {
@@ -0,0 +1,32 @@
1
+ import { K as require_jsx_runtime } from "../_libs/@base-ui/react+[...].mjs";
2
+ import { P as cn } from "./router-DdwREiOf.mjs";
3
+ //#region node_modules/.nitro/vite/services/ssr/assets/connection-status-BogAL-Fj.js
4
+ var import_jsx_runtime = require_jsx_runtime();
5
+ var LABELS = {
6
+ connected: {
7
+ text: "Connected",
8
+ dot: "bg-emerald-500"
9
+ },
10
+ needs_reauth: {
11
+ text: "Needs reconnecting",
12
+ dot: "bg-destructive"
13
+ },
14
+ error: {
15
+ text: "Error",
16
+ dot: "bg-destructive"
17
+ },
18
+ paused: {
19
+ text: "Paused",
20
+ dot: "bg-muted-foreground"
21
+ }
22
+ };
23
+ /** A dot and a word: quiet when all is well, red when it is not. */
24
+ function ConnectionStatusBadge({ status }) {
25
+ const label = LABELS[status] ?? LABELS.error;
26
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
27
+ className: cn("inline-flex items-center gap-1.5 text-xs", status === "connected" || status === "paused" ? "text-muted-foreground" : "text-destructive"),
28
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: cn("size-1.5 rounded-full", label.dot) }), label.text]
29
+ });
30
+ }
31
+ //#endregion
32
+ export { ConnectionStatusBadge as t };