codebay 0.14.0 → 0.15.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 (78) hide show
  1. package/.mochi/manifest.json +526 -455
  2. package/.mochi/svelte-client/HydratableIsland-smgsf0ma.js +1 -0
  3. package/.mochi/svelte-client/_hydrate-AppShell_2rtnxn6pgp2rh-9vc087ep.js +2 -0
  4. package/.mochi/svelte-client/_hydrate-AvatarsView_257s9yq1wwr4f-a48xq47a.js +14 -0
  5. package/.mochi/svelte-client/_hydrate-InstanceView_2ma45l9bybfhl-d4zehrqz.js +3 -0
  6. package/.mochi/svelte-client/_hydrate-SettingsView_8hmarwmf8onq-t5175pf5.js +44 -0
  7. package/.mochi/svelte-client/chunk-0mstgbtr.js +2 -0
  8. package/.mochi/svelte-client/chunk-0q9g0kpg.js +1 -0
  9. package/.mochi/svelte-client/chunk-3fwjzw1r.js +1 -0
  10. package/.mochi/svelte-client/{chunk-spk1b2r4.js → chunk-5pg0be5f.js} +1 -1
  11. package/.mochi/svelte-client/{chunk-ccfh8hxz.js → chunk-a2hty99b.js} +1 -1
  12. package/.mochi/svelte-client/{chunk-0mc09cyb.js → chunk-ds9yn9cq.js} +1 -1
  13. package/.mochi/svelte-client/{chunk-phf7v5jj.js → chunk-fn03gxer.js} +1 -1
  14. package/.mochi/svelte-client/chunk-jhgq7f65.js +2 -0
  15. package/.mochi/svelte-client/chunk-p5a07kx1.js +1 -0
  16. package/.mochi/svelte-client/chunk-p5wpyhsf.js +2 -0
  17. package/.mochi/svelte-client/chunk-t2kfvtb6.js +2 -0
  18. package/.mochi/svelte-client/{chunk-x0m3k0rn.js → chunk-v3wg9mdy.js} +1 -1
  19. package/.mochi/svelte-compile/{App-94xsdn0w.server.js → App-68f9a9fq.server.js} +64 -59
  20. package/.mochi/svelte-compile/{Avatars-fnccw47h.server.js → Avatars-vyh3rwxx.server.js} +14 -29
  21. package/.mochi/svelte-compile/Instance-yfw7s5mp.server.js +627 -0
  22. package/.mochi/svelte-compile/{Settings-a7b11v90.server.js → Settings-0b5t9kw9.server.js} +145 -25
  23. package/.mochi/svelte-compile/{chunk-w0hca7va.js → chunk-0y3wrae9.js} +13 -1
  24. package/.mochi/svelte-compile/{chunk-ed46m58e.js → chunk-2qzc07gt.js} +13 -1
  25. package/.mochi/svelte-compile/chunk-ef3hvegw.js +21 -0
  26. package/.mochi/svelte-compile/chunk-f9td8ej2.js +24 -0
  27. package/.mochi/svelte-compile/{chunk-pqb403fh.js → chunk-g0eravvj.js} +5 -16
  28. package/.mochi/svelte-css/AgentLogBox-38adby5iygny7.css +1 -0
  29. package/.mochi/svelte-css/{InstanceCard-25z7nas7y98zk.css → InstanceCard-3vxozrp9uoads.css} +1 -1
  30. package/.mochi/svelte-css/{SettingsView-bccw34agd2bj.css → SettingsView-2400myxb9ot56.css} +1 -1
  31. package/README.md +32 -1
  32. package/migrations/0009_agent_runs.sql +24 -0
  33. package/migrations/0010_agent_run_model.sql +2 -0
  34. package/package.json +6 -2
  35. package/src/components/AgentLogBox.svelte +458 -0
  36. package/src/components/AppShell.svelte +11 -0
  37. package/src/components/FolderBrowser.svelte +13 -5
  38. package/src/components/InstanceCard.svelte +38 -0
  39. package/src/components/InstanceView.svelte +8 -0
  40. package/src/components/SettingsView.svelte +186 -0
  41. package/src/components/UiShowcase.svelte +4 -2
  42. package/src/container-injections/claude-statusline.ts +4 -1
  43. package/src/container-injections/github-credentials.ts +12 -1
  44. package/src/index.ts +32 -7
  45. package/src/lib/agent-run-stream.ts +246 -0
  46. package/src/lib/agent-runs.server.ts +724 -0
  47. package/src/lib/auth.server.ts +11 -2
  48. package/src/lib/build-freshness.server.ts +71 -0
  49. package/src/lib/config.server.ts +29 -6
  50. package/src/lib/container-files.server.ts +1 -1
  51. package/src/lib/data-dir-lock.server.ts +78 -0
  52. package/src/lib/db.server.ts +120 -1
  53. package/src/lib/devcontainer.server.ts +60 -14
  54. package/src/lib/docker-client.server.ts +11 -5
  55. package/src/lib/drives.ts +11 -0
  56. package/src/lib/exec.server.ts +66 -5
  57. package/src/lib/instances.server.ts +45 -3
  58. package/src/lib/log-capture.server.ts +17 -5
  59. package/src/lib/mcp-auth.server.ts +52 -0
  60. package/src/lib/picker.server.ts +23 -2
  61. package/src/lib/sandbox-ops.server.ts +263 -0
  62. package/src/mcp/routes.server.ts +25 -0
  63. package/src/mcp/server.server.ts +52 -0
  64. package/src/mcp/tools.server.ts +494 -0
  65. package/src/pages/Settings.svelte +12 -0
  66. package/src/routes.ts +63 -1
  67. package/src/types.ts +15 -0
  68. package/.mochi/svelte-client/HydratableIsland-zrdsrsmg.js +0 -1
  69. package/.mochi/svelte-client/_hydrate-AppShell_2rtnxn6pgp2rh-v7y7dh53.js +0 -2
  70. package/.mochi/svelte-client/_hydrate-AvatarsView_257s9yq1wwr4f-tmwd6fpb.js +0 -14
  71. package/.mochi/svelte-client/_hydrate-InstanceView_2ma45l9bybfhl-m7t64dbh.js +0 -1
  72. package/.mochi/svelte-client/_hydrate-SettingsView_8hmarwmf8onq-4a7ann1f.js +0 -40
  73. package/.mochi/svelte-client/chunk-5jwfs4p9.js +0 -2
  74. package/.mochi/svelte-client/chunk-6qg4q98a.js +0 -2
  75. package/.mochi/svelte-client/chunk-tmeh70wg.js +0 -2
  76. package/.mochi/svelte-client/chunk-vs1yjbqt.js +0 -2
  77. package/.mochi/svelte-client/chunk-y7f4jbdm.js +0 -2
  78. package/.mochi/svelte-compile/Instance-knnkcn2p.server.js +0 -377
@@ -2,23 +2,26 @@
2
2
  import {
3
3
  Layout_template,
4
4
  Plus,
5
- Terminal,
6
5
  playChime,
7
6
  setSoundEnabled,
8
7
  setTabShortcutsEnabled,
9
8
  soundEnabled,
10
9
  tabShortcutsEnabled,
11
10
  unlockAudio
12
- } from "./chunk-pqb403fh.js";
11
+ } from "./chunk-g0eravvj.js";
12
+ import {
13
+ Message_square
14
+ } from "./chunk-ef3hvegw.js";
13
15
  import {
14
16
  CLAUDE_EFFORT_LEVELS,
15
17
  CLAUDE_OUTPUT_STYLES,
16
18
  CLAUDE_PERMISSION_MODES,
19
+ Terminal,
17
20
  apiPost,
18
21
  applyTheme,
19
22
  getTheme,
20
23
  setTheme
21
- } from "./chunk-w0hca7va.js";
24
+ } from "./chunk-0y3wrae9.js";
22
25
  import {
23
26
  Rotate_ccw
24
27
  } from "./chunk-jx6h1xzp.js";
@@ -551,12 +554,30 @@ function Puzzle($$renderer, $$props) {
551
554
  ];
552
555
  Icon($$renderer, $24.spread_props([{ name: "puzzle" }, props, { iconNode }]));
553
556
  }
557
+ // node_modules/@lucide/svelte/dist/icons/plug.svelte
558
+ import"svelte/internal/flags/async";
559
+ import * as $25 from "svelte/internal/server";
560
+ function Plug($$renderer, $$props) {
561
+ let { $$slots, $$events, ...props } = $$props;
562
+ const iconNode = [
563
+ ["path", { d: "M12 22v-5" }],
564
+ ["path", { d: "M15 8V2" }],
565
+ [
566
+ "path",
567
+ {
568
+ d: "M17 8a1 1 0 0 1 1 1v4a4 4 0 0 1-4 4h-4a4 4 0 0 1-4-4V9a1 1 0 0 1 1-1z"
569
+ }
570
+ ],
571
+ ["path", { d: "M9 8V2" }]
572
+ ];
573
+ Icon($$renderer, $25.spread_props([{ name: "plug" }, props, { iconNode }]));
574
+ }
554
575
  // src/components/SettingsView.svelte
555
576
  import { flushSync } from "svelte";
556
577
 
557
578
  // src/components/CoinButton.svelte
558
579
  import"svelte/internal/flags/async";
559
- import * as $25 from "svelte/internal/server";
580
+ import * as $26 from "svelte/internal/server";
560
581
  function CoinButton($$renderer, $$props) {
561
582
  let { href = "/avatars" } = $$props;
562
583
  const coinArt = {
@@ -572,14 +593,14 @@ function CoinButton($$renderer, $$props) {
572
593
  "..####.."
573
594
  ]
574
595
  };
575
- $$renderer.push(`<div class="coin-row svelte-1ioj7vz"><span class="pixel-line svelte-1ioj7vz" aria-hidden="true"></span> <a class="coin-btn svelte-1ioj7vz"${$25.attr("href", href)} aria-label="Draw your own avatar" title="Draw your own avatar"><span class="new svelte-1ioj7vz" aria-hidden="true">New</span> <span class="coin svelte-1ioj7vz" aria-hidden="true">`);
596
+ $$renderer.push(`<div class="coin-row svelte-1ioj7vz"><span class="pixel-line svelte-1ioj7vz" aria-hidden="true"></span> <a class="coin-btn svelte-1ioj7vz"${$26.attr("href", href)} aria-label="Draw your own avatar" title="Draw your own avatar"><span class="new svelte-1ioj7vz" aria-hidden="true">New</span> <span class="coin svelte-1ioj7vz" aria-hidden="true">`);
576
597
  Avatar($$renderer, { art: coinArt, name: "coin", scale: 3 });
577
598
  $$renderer.push(`<!----></span></a> <span class="pixel-line svelte-1ioj7vz" aria-hidden="true"></span></div>`);
578
599
  }
579
600
 
580
601
  // node_modules/@lucide/svelte/dist/icons/shield-check.svelte
581
602
  import"svelte/internal/flags/async";
582
- import * as $26 from "svelte/internal/server";
603
+ import * as $27 from "svelte/internal/server";
583
604
  function Shield_check($$renderer, $$props) {
584
605
  let { $$slots, $$events, ...props } = $$props;
585
606
  const iconNode = [
@@ -591,33 +612,18 @@ function Shield_check($$renderer, $$props) {
591
612
  ],
592
613
  ["path", { d: "m9 12 2 2 4-4" }]
593
614
  ];
594
- Icon($$renderer, $26.spread_props([{ name: "shield-check" }, props, { iconNode }]));
615
+ Icon($$renderer, $27.spread_props([{ name: "shield-check" }, props, { iconNode }]));
595
616
  }
596
617
  // node_modules/@lucide/svelte/dist/icons/gauge.svelte
597
618
  import"svelte/internal/flags/async";
598
- import * as $27 from "svelte/internal/server";
619
+ import * as $28 from "svelte/internal/server";
599
620
  function Gauge($$renderer, $$props) {
600
621
  let { $$slots, $$events, ...props } = $$props;
601
622
  const iconNode = [
602
623
  ["path", { d: "m12 14 4-4" }],
603
624
  ["path", { d: "M3.34 19a10 10 0 1 1 17.32 0" }]
604
625
  ];
605
- Icon($$renderer, $27.spread_props([{ name: "gauge" }, props, { iconNode }]));
606
- }
607
- // node_modules/@lucide/svelte/dist/icons/message-square.svelte
608
- import"svelte/internal/flags/async";
609
- import * as $28 from "svelte/internal/server";
610
- function Message_square($$renderer, $$props) {
611
- let { $$slots, $$events, ...props } = $$props;
612
- const iconNode = [
613
- [
614
- "path",
615
- {
616
- d: "M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z"
617
- }
618
- ]
619
- ];
620
- Icon($$renderer, $28.spread_props([{ name: "message-square" }, props, { iconNode }]));
626
+ Icon($$renderer, $28.spread_props([{ name: "gauge" }, props, { iconNode }]));
621
627
  }
622
628
  // src/components/SettingsView.svelte
623
629
  function SettingsView($$renderer, $$props) {
@@ -634,6 +640,10 @@ function SettingsView($$renderer, $$props) {
634
640
  copyIgnorePatterns,
635
641
  builtinCopyIgnore,
636
642
  claudeConfigDir,
643
+ mcpEnabled,
644
+ mcpToken,
645
+ mcpUrl,
646
+ mcpPrAttribution,
637
647
  gitIdentityEnabled,
638
648
  gitIdentityName,
639
649
  gitIdentityEmail,
@@ -854,6 +864,43 @@ function SettingsView($$renderer, $$props) {
854
864
  flushSync(() => claudeDir = "");
855
865
  claudeDirFormEl?.requestSubmit();
856
866
  }
867
+ let mcp = mcpEnabled;
868
+ let mcpTokenValue = mcpToken;
869
+ let savingMcpToggle = false;
870
+ let mcpToggleError = null;
871
+ let savingMcpToken = false;
872
+ let mcpTokenError = null;
873
+ let mcpCopied = null;
874
+ const mcpToggleOpts = toggleOpts({
875
+ set: (v) => mcp = v,
876
+ setSaving: (v) => savingMcpToggle = v,
877
+ setError: (v) => mcpToggleError = v,
878
+ onSuccess: (data) => mcpTokenValue = data?.token ?? ""
879
+ });
880
+ const mcpTokenOpts = saveOpts({
881
+ setSaving: (v) => savingMcpToken = v,
882
+ setError: (v) => mcpTokenError = v,
883
+ onSuccess: (data) => mcpTokenValue = data?.token ?? "",
884
+ confirmMessage: "Regenerate the MCP token? Any agent already configured with the current token will stop working."
885
+ });
886
+ let mcpAttribution = mcpPrAttribution;
887
+ let savingMcpAttribution = false;
888
+ let mcpAttributionError = null;
889
+ const mcpAttributionOpts = toggleOpts({
890
+ set: (v) => mcpAttribution = v,
891
+ setSaving: (v) => savingMcpAttribution = v,
892
+ setError: (v) => mcpAttributionError = v
893
+ });
894
+ const mcpCommand = $29.derived(() => `claude mcp add --transport http codebay ${mcpUrl} --header "Authorization: Bearer ${mcpTokenValue}"`);
895
+ async function copyMcp(label, text) {
896
+ try {
897
+ await navigator.clipboard.writeText(text);
898
+ mcpCopied = label;
899
+ setTimeout(() => mcpCopied = null, 1500);
900
+ } catch {
901
+ mcpTokenError = "Could not copy \u2014 select the text and copy it manually.";
902
+ }
903
+ }
857
904
  let gitIdentity = gitIdentityEnabled;
858
905
  let savingGitToggle = false;
859
906
  let gitToggleError = null;
@@ -1418,7 +1465,68 @@ function SettingsView($$renderer, $$props) {
1418
1465
  } else {
1419
1466
  $$renderer2.push("<!--[-1-->");
1420
1467
  }
1421
- $$renderer2.push(`<!--]--></form></section> <section class="card svelte-1n6ht58"><form class="row svelte-1n6ht58" method="POST" action="?/gitIdentityToggle"><div class="label svelte-1n6ht58">`);
1468
+ $$renderer2.push(`<!--]--></form></section> <section class="card svelte-1n6ht58"><form class="row svelte-1n6ht58" method="POST" action="?/mcpToggle"><div class="label svelte-1n6ht58">`);
1469
+ Plug($$renderer2, { size: 20 });
1470
+ $$renderer2.push(`<!----> <div class="text svelte-1n6ht58"><div class="name svelte-1n6ht58">MCP server</div> <div class="desc svelte-1n6ht58">Expose Codebay to other AI agents over MCP, so they can create sandboxes, run Claude
1471
+ Code in them non-interactively and read back the result. Off by default; the endpoint
1472
+ 404s until you turn it on, then authenticates with the bearer token below.</div></div></div> <label class="switch svelte-1n6ht58"><input type="checkbox" name="enabled"${$29.attr("checked", mcp, true)}${$29.attr("disabled", savingMcpToggle, true)} class="svelte-1n6ht58"/> <span class="track svelte-1n6ht58"><span class="thumb svelte-1n6ht58"></span></span></label></form> `);
1473
+ if (mcpToggleError) {
1474
+ $$renderer2.push("<!--[0-->");
1475
+ $$renderer2.push(`<div class="sub svelte-1n6ht58"><div class="msg error svelte-1n6ht58">${$29.escape(mcpToggleError)}</div></div>`);
1476
+ } else {
1477
+ $$renderer2.push("<!--[-1-->");
1478
+ }
1479
+ $$renderer2.push(`<!--]--> `);
1480
+ if (mcp) {
1481
+ $$renderer2.push("<!--[0-->");
1482
+ $$renderer2.push(`<div class="row divided svelte-1n6ht58"><div class="label svelte-1n6ht58"><div class="text svelte-1n6ht58"><div class="name svelte-1n6ht58">Token</div> <div class="desc svelte-1n6ht58">Anything holding this token can create containers and run agents with your GitHub
1483
+ and Claude credentials. Treat it like a password.</div></div></div> <div class="model-fields svelte-1n6ht58"><label class="model-row svelte-1n6ht58"><span class="model-label svelte-1n6ht58">Bearer</span> <input type="text" class="image-input svelte-1n6ht58"${$29.attr("value", mcpTokenValue)} readonly="" spellcheck="false"/></label> <div class="mcp-actions svelte-1n6ht58">`);
1484
+ Button($$renderer2, {
1485
+ type: "button",
1486
+ onclick: () => copyMcp("token", mcpTokenValue),
1487
+ children: ($$renderer3) => {
1488
+ $$renderer3.push(`<!---->${$29.escape(mcpCopied === "token" ? "Copied" : "Copy token")}`);
1489
+ },
1490
+ $$slots: { default: true }
1491
+ });
1492
+ $$renderer2.push(`<!----> `);
1493
+ Button($$renderer2, {
1494
+ type: "button",
1495
+ onclick: () => copyMcp("command", mcpCommand()),
1496
+ children: ($$renderer3) => {
1497
+ $$renderer3.push(`<!---->${$29.escape(mcpCopied === "command" ? "Copied" : "Copy claude mcp add")}`);
1498
+ },
1499
+ $$slots: { default: true }
1500
+ });
1501
+ $$renderer2.push(`<!----> <form method="POST" action="?/mcpRegenerateToken">`);
1502
+ Button($$renderer2, {
1503
+ type: "submit",
1504
+ disabled: savingMcpToken,
1505
+ children: ($$renderer3) => {
1506
+ $$renderer3.push(`<!---->${$29.escape(savingMcpToken ? "Regenerating\u2026" : "Regenerate")}`);
1507
+ },
1508
+ $$slots: { default: true }
1509
+ });
1510
+ $$renderer2.push(`<!----></form></div> <code class="mcp-command svelte-1n6ht58">${$29.escape(mcpCommand())}</code> `);
1511
+ if (mcpTokenError) {
1512
+ $$renderer2.push("<!--[0-->");
1513
+ $$renderer2.push(`<div class="msg error svelte-1n6ht58">${$29.escape(mcpTokenError)}</div>`);
1514
+ } else {
1515
+ $$renderer2.push("<!--[-1-->");
1516
+ }
1517
+ $$renderer2.push(`<!--]--></div></div> <form class="row divided svelte-1n6ht58" method="POST" action="?/mcpPrAttributionToggle"><div class="label svelte-1n6ht58"><div class="text svelte-1n6ht58"><div class="name svelte-1n6ht58">Credit Codebay on pull requests</div> <div class="desc svelte-1n6ht58">Append a one-line footer to the body of every pull request an agent opens through <code class="svelte-1n6ht58">create_pr</code>. Off by default \u2014 an attribution line on your PRs is yours to
1518
+ opt into.</div></div></div> <label class="switch svelte-1n6ht58"><input type="checkbox" name="enabled"${$29.attr("checked", mcpAttribution, true)}${$29.attr("disabled", savingMcpAttribution, true)} class="svelte-1n6ht58"/> <span class="track svelte-1n6ht58"><span class="thumb svelte-1n6ht58"></span></span></label></form> `);
1519
+ if (mcpAttributionError) {
1520
+ $$renderer2.push("<!--[0-->");
1521
+ $$renderer2.push(`<div class="sub svelte-1n6ht58"><div class="msg error svelte-1n6ht58">${$29.escape(mcpAttributionError)}</div></div>`);
1522
+ } else {
1523
+ $$renderer2.push("<!--[-1-->");
1524
+ }
1525
+ $$renderer2.push(`<!--]-->`);
1526
+ } else {
1527
+ $$renderer2.push("<!--[-1-->");
1528
+ }
1529
+ $$renderer2.push(`<!--]--></section> <section class="card svelte-1n6ht58"><form class="row svelte-1n6ht58" method="POST" action="?/gitIdentityToggle"><div class="label svelte-1n6ht58">`);
1422
1530
  User_cog($$renderer2, { size: 20 });
1423
1531
  $$renderer2.push(`<!----> <div class="text svelte-1n6ht58"><div class="name svelte-1n6ht58">Override git identity</div> <div class="desc svelte-1n6ht58">Inject a name and email as <code class="svelte-1n6ht58">git config --global user.name/user.email</code> in every
1424
1532
  new container, taking precedence over the host's own git config. When off, each container
@@ -1979,6 +2087,10 @@ function Settings($$renderer, $$props) {
1979
2087
  copyIgnorePatterns,
1980
2088
  builtinCopyIgnore,
1981
2089
  claudeConfigDir,
2090
+ mcpEnabled,
2091
+ mcpToken,
2092
+ mcpUrl,
2093
+ mcpPrAttribution,
1982
2094
  gitIdentityEnabled,
1983
2095
  gitIdentityName,
1984
2096
  gitIdentityEmail,
@@ -2032,6 +2144,10 @@ function Settings($$renderer, $$props) {
2032
2144
  copyIgnorePatterns,
2033
2145
  builtinCopyIgnore,
2034
2146
  claudeConfigDir,
2147
+ mcpEnabled,
2148
+ mcpToken,
2149
+ mcpUrl,
2150
+ mcpPrAttribution,
2035
2151
  gitIdentityEnabled,
2036
2152
  gitIdentityName,
2037
2153
  gitIdentityEmail,
@@ -2077,6 +2193,10 @@ function Settings($$renderer, $$props) {
2077
2193
  copyIgnorePatterns,
2078
2194
  builtinCopyIgnore,
2079
2195
  claudeConfigDir,
2196
+ mcpEnabled,
2197
+ mcpToken,
2198
+ mcpUrl,
2199
+ mcpPrAttribution,
2080
2200
  gitIdentityEnabled,
2081
2201
  gitIdentityName,
2082
2202
  gitIdentityEmail,
@@ -1,5 +1,6 @@
1
1
  // @bun
2
2
  import {
3
+ Icon,
3
4
  cookies
4
5
  } from "./chunk-60mpagr9.js";
5
6
 
@@ -17,6 +18,17 @@ function ideUrl(instance) {
17
18
  return instance.remote_workspace_folder ? `${base}?folder=${encodeURIComponent(instance.remote_workspace_folder)}` : base;
18
19
  }
19
20
 
21
+ // node_modules/@lucide/svelte/dist/icons/terminal.svelte
22
+ import"svelte/internal/flags/async";
23
+ import * as $ from "svelte/internal/server";
24
+ function Terminal($$renderer, $$props) {
25
+ let { $$slots, $$events, ...props } = $$props;
26
+ const iconNode = [
27
+ ["path", { d: "M12 19h8" }],
28
+ ["path", { d: "m4 17 6-6-6-6" }]
29
+ ];
30
+ Icon($$renderer, $.spread_props([{ name: "terminal" }, props, { iconNode }]));
31
+ }
20
32
  // src/api.ts
21
33
  var APP_REQUEST_HEADER = "X-Codebay-Request";
22
34
  async function apiFetch(url, init, fallbackMessage = "Request failed") {
@@ -63,4 +75,4 @@ function applyTheme(theme) {
63
75
  document.documentElement.dataset.theme = theme;
64
76
  }
65
77
 
66
- export { CLAUDE_PERMISSION_MODES, CLAUDE_EFFORT_LEVELS, CLAUDE_OUTPUT_STYLES, ideUrl, apiFetch, apiPost, apiDelete, getTheme, setTheme, applyTheme };
78
+ export { CLAUDE_PERMISSION_MODES, CLAUDE_EFFORT_LEVELS, CLAUDE_OUTPUT_STYLES, ideUrl, Terminal, apiFetch, apiPost, apiDelete, getTheme, setTheme, applyTheme };
@@ -1,4 +1,8 @@
1
1
  // @bun
2
+ import {
3
+ Icon
4
+ } from "./chunk-60mpagr9.js";
5
+
2
6
  // src/lib/links.ts
3
7
  function forwardedPortUrl(hostPort) {
4
8
  const host = typeof window === "undefined" ? "localhost" : window.location.hostname;
@@ -19,4 +23,12 @@ function StatusBadge($$renderer, $$props) {
19
23
  $$renderer.push(`<span${$.attr_class(`status ${$.stringify(status)}`, "svelte-1hqr9xd")}>${$.escape(label[status])}</span>`);
20
24
  }
21
25
 
22
- export { forwardedPortUrl, StatusBadge };
26
+ // node_modules/@lucide/svelte/dist/icons/chevron-right.svelte
27
+ import"svelte/internal/flags/async";
28
+ import * as $2 from "svelte/internal/server";
29
+ function Chevron_right($$renderer, $$props) {
30
+ let { $$slots, $$events, ...props } = $$props;
31
+ const iconNode = [["path", { d: "m9 18 6-6-6-6" }]];
32
+ Icon($$renderer, $2.spread_props([{ name: "chevron-right" }, props, { iconNode }]));
33
+ }
34
+ export { forwardedPortUrl, StatusBadge, Chevron_right };
@@ -0,0 +1,21 @@
1
+ // @bun
2
+ import {
3
+ Icon
4
+ } from "./chunk-60mpagr9.js";
5
+
6
+ // node_modules/@lucide/svelte/dist/icons/message-square.svelte
7
+ import"svelte/internal/flags/async";
8
+ import * as $ from "svelte/internal/server";
9
+ function Message_square($$renderer, $$props) {
10
+ let { $$slots, $$events, ...props } = $$props;
11
+ const iconNode = [
12
+ [
13
+ "path",
14
+ {
15
+ d: "M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z"
16
+ }
17
+ ]
18
+ ];
19
+ Icon($$renderer, $.spread_props([{ name: "message-square" }, props, { iconNode }]));
20
+ }
21
+ export { Message_square };
@@ -0,0 +1,24 @@
1
+ // @bun
2
+ import {
3
+ Icon
4
+ } from "./chunk-60mpagr9.js";
5
+
6
+ // node_modules/@lucide/svelte/dist/icons/bot.svelte
7
+ import"svelte/internal/flags/async";
8
+ import * as $ from "svelte/internal/server";
9
+ function Bot($$renderer, $$props) {
10
+ let { $$slots, $$events, ...props } = $$props;
11
+ const iconNode = [
12
+ ["path", { d: "M12 8V4H8" }],
13
+ [
14
+ "rect",
15
+ { width: "16", height: "12", x: "4", y: "8", rx: "2" }
16
+ ],
17
+ ["path", { d: "M2 14h2" }],
18
+ ["path", { d: "M20 14h2" }],
19
+ ["path", { d: "M15 13v2" }],
20
+ ["path", { d: "M9 13v2" }]
21
+ ];
22
+ Icon($$renderer, $.spread_props([{ name: "bot" }, props, { iconNode }]));
23
+ }
24
+ export { Bot };
@@ -3,20 +3,9 @@ import {
3
3
  Icon
4
4
  } from "./chunk-60mpagr9.js";
5
5
 
6
- // node_modules/@lucide/svelte/dist/icons/terminal.svelte
7
- import"svelte/internal/flags/async";
8
- import * as $ from "svelte/internal/server";
9
- function Terminal($$renderer, $$props) {
10
- let { $$slots, $$events, ...props } = $$props;
11
- const iconNode = [
12
- ["path", { d: "M12 19h8" }],
13
- ["path", { d: "m4 17 6-6-6-6" }]
14
- ];
15
- Icon($$renderer, $.spread_props([{ name: "terminal" }, props, { iconNode }]));
16
- }
17
6
  // node_modules/@lucide/svelte/dist/icons/layout-template.svelte
18
7
  import"svelte/internal/flags/async";
19
- import * as $2 from "svelte/internal/server";
8
+ import * as $ from "svelte/internal/server";
20
9
  function Layout_template($$renderer, $$props) {
21
10
  let { $$slots, $$events, ...props } = $$props;
22
11
  const iconNode = [
@@ -33,15 +22,15 @@ function Layout_template($$renderer, $$props) {
33
22
  { width: "5", height: "7", x: "16", y: "14", rx: "1" }
34
23
  ]
35
24
  ];
36
- Icon($$renderer, $2.spread_props([{ name: "layout-template" }, props, { iconNode }]));
25
+ Icon($$renderer, $.spread_props([{ name: "layout-template" }, props, { iconNode }]));
37
26
  }
38
27
  // node_modules/@lucide/svelte/dist/icons/plus.svelte
39
28
  import"svelte/internal/flags/async";
40
- import * as $3 from "svelte/internal/server";
29
+ import * as $2 from "svelte/internal/server";
41
30
  function Plus($$renderer, $$props) {
42
31
  let { $$slots, $$events, ...props } = $$props;
43
32
  const iconNode = [["path", { d: "M5 12h14" }], ["path", { d: "M12 5v14" }]];
44
- Icon($$renderer, $3.spread_props([{ name: "plus" }, props, { iconNode }]));
33
+ Icon($$renderer, $2.spread_props([{ name: "plus" }, props, { iconNode }]));
45
34
  }
46
35
  // src/settings.ts
47
36
  var SOUND_KEY = "codebay.sound";
@@ -121,4 +110,4 @@ function playChime(state) {
121
110
  el.play().then(() => console.log(`[chime] playChime(${state}) \u2014 playing`)).catch((err) => console.warn(`[chime] playChime(${state}) \u2014 play rejected:`, err?.message ?? err));
122
111
  }
123
112
 
124
- export { Terminal, Layout_template, Plus, soundEnabled, setSoundEnabled, tabShortcutsEnabled, setTabShortcutsEnabled, unlockAudio, playChime };
113
+ export { Layout_template, Plus, soundEnabled, setSoundEnabled, tabShortcutsEnabled, setTabShortcutsEnabled, unlockAudio, playChime };
@@ -0,0 +1 @@
1
+ .agentwrap.svelte-rfwj7e{overflow:hidden;margin-bottom:18px}.agent-bar.svelte-rfwj7e{display:flex;justify-content:space-between;align-items: center;gap:12px}.agent-bar.svelte-rfwj7e span:where(.svelte-rfwj7e){display:inline-flex;align-items: center;gap:6px}.count.svelte-rfwj7e{opacity:.8;font-size:11px}.run.svelte-rfwj7e{background:var(--bg-card);border-top:1px solid var(--rule-soft)}.run.svelte-rfwj7e:first-of-type{border-top:none}.run-head.svelte-rfwj7e{display:flex;color:inherit;font:inherit;text-align:left;cursor:pointer;background:0 0;border:none;align-items: center;gap:10px;width:100%;min-width:0;padding:9px 14px}.run-head.svelte-rfwj7e:hover{background:color-mix(in srgb,var(--ink)6%,transparent)}.chev.svelte-rfwj7e{display:inline-flex;color:var(--ink-soft);flex:none;transition:transform .12s}.chev.down.svelte-rfwj7e{transform:rotate(90deg)}.when.svelte-rfwj7e,.status.svelte-rfwj7e,.model.svelte-rfwj7e,.session.svelte-rfwj7e,.run-meta.svelte-rfwj7e,.peek.svelte-rfwj7e{font-family:var(--font-mono);font-size:11px}.session.svelte-rfwj7e{display:inline-flex;color:var(--ink-faint);flex:none;align-items: center;gap:4px}.session.resumed.svelte-rfwj7e{color:var(--ink-soft)}.model.svelte-rfwj7e{color:var(--ink-soft);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:none;max-width:14em}.when.svelte-rfwj7e{color:var(--ink-soft);flex:none}.status.svelte-rfwj7e{display:inline-flex;border:1px solid var(--ink-faint);flex:none;align-items: center;gap:5px;padding:0 5px}.status.live.svelte-rfwj7e{border-color:var(--fill)}.status.bad.svelte-rfwj7e{color:var(--attn-waiting);border-color:var(--attn-waiting)}.peek.svelte-rfwj7e{color:var(--ink-soft);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0}.run-meta.svelte-rfwj7e{color:var(--ink-faint);flex:none;margin-left:auto}.prompt.svelte-rfwj7e{border-top:1px solid var(--rule-soft);padding:10px 14px}.ptitle.svelte-rfwj7e{font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.08em;color:var(--ink-soft);margin-bottom:4px;font-size:10px}.ptext.svelte-rfwj7e{white-space:pre-wrap;overflow-y:auto;max-height:7.5em;font-size:12px;line-height:1.5}.steps.svelte-rfwj7e{list-style:none;overflow-y:auto;max-height:420px;margin:0;padding:0}.step.svelte-rfwj7e{display:flex;border-top:1px solid var(--rule-soft);align-items: flex-start;gap:10px;padding:8px 14px}.step.sub.svelte-rfwj7e{opacity:.75;padding-left:34px}.ico.svelte-rfwj7e{display:inline-flex;color:var(--ink-soft);flex:none;justify-content:center;align-items: center;width:16px;height:16px;margin-top:1px}.ico.bad.svelte-rfwj7e{color:var(--attn-waiting)}.body.svelte-rfwj7e{display:flex;flex-wrap:wrap;align-items:baseline;gap:6px;min-width:0}.tool.svelte-rfwj7e{font-family:var(--font-mono);border:1px solid var(--ink-faint);flex:none;padding:0 4px;font-size:11px}.text.svelte-rfwj7e{font-family:var(--font-mono);word-break:break-word;min-width:0;font-size:12px;line-height:1.5}.pulse.svelte-rfwj7e{background:var(--fill);animation:svelte-rfwj7e-agent-pulse 1.6s ease-in-out infinite;border-radius:50%;flex:none;width:7px;height:7px}@keyframes svelte-rfwj7e-agent-pulse{50%{opacity:.25}}.err.svelte-rfwj7e{font-family:var(--font-mono);border-top:1px solid var(--rule-soft);color:var(--attn-waiting);white-space:pre-wrap;padding:8px 14px;font-size:11px}
@@ -1 +1 @@
1
- .card.svelte-13338y7{position:relative;background:var(--bg-card);padding:16px 16px 14px;transition:transform 80ms steps(2,end),box-shadow 80ms steps(2,end)}.card.svelte-13338y7:hover:not(.busy){box-shadow:6px 6px 0 var(--shadow);transform:translate(-1px,-1px)}.busy-overlay.svelte-13338y7{position:absolute;z-index:1;display:flex;background:color-mix(in srgb,var(--bg-card)78%,transparent);justify-content:center;align-items: center;inset:0}.busy-label.svelte-13338y7{font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.1em;border:1px solid var(--fill);background:var(--fill);color:var(--fill-ink);animation:svelte-13338y7-lcd-blink 1.1s steps(1)infinite;padding:4px 9px;font-size:12px;font-weight:600}@keyframes svelte-13338y7-lcd-blink{50%{background:0 0;color:var(--fill)}}@media (prefers-reduced-motion:reduce){.busy-label.svelte-13338y7{animation:none}}.card-head.svelte-13338y7{display:flex;align-items: center;gap:11px}.attn.svelte-13338y7{border:1px solid var(--edge);flex:none;width:14px;height:14px}.attn.attn-done.svelte-13338y7{background:var(--attn-done);animation:svelte-13338y7-card-attn-pulse 1.4s ease-in-out infinite}.attn.attn-waiting.svelte-13338y7{background:var(--attn-waiting);animation:svelte-13338y7-card-attn-pulse 1.4s ease-in-out infinite}@keyframes svelte-13338y7-card-attn-pulse{0%,to{box-shadow:0 0 #0000;filter:brightness(.85)}50%{box-shadow:0 0 7px 2px color-mix(in srgb,currentColor 60%,transparent);filter:brightness(1.15)}}.attn.attn-done.svelte-13338y7{color:var(--attn-done)}.attn.attn-waiting.svelte-13338y7{color:var(--attn-waiting)}@media (prefers-reduced-motion:reduce){.attn.svelte-13338y7{animation:none;filter:none}}.name.svelte-13338y7{font-family:var(--font-mono);overflow-wrap:anywhere;color:var(--ink);text-align:left;cursor:pointer;background:0 0;border:1px solid #0000;flex:1;min-width:0;margin:0;padding:2px 4px;font-size:15px;font-weight:600}.name.svelte-13338y7:hover{border-color:var(--ink-faint)}.name-edit.svelte-13338y7{font-family:var(--font-mono);border:1px solid var(--ink);background:var(--bg);color:var(--ink);outline:none;flex:1;min-width:0;margin:0;padding:2px 4px;font-size:15px;font-weight:600}.path.svelte-13338y7{font-family:var(--font-mono);color:var(--ink-soft);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin-top:10px;font-size:12px}.card-error.svelte-13338y7{font-family:var(--font-mono);color:var(--ink);background:var(--bg);border:1px solid var(--edge);overflow:auto;max-height:80px;margin-top:10px;padding:8px 10px;font-size:12px}.actions.svelte-13338y7{display:flex;flex-wrap:wrap;gap:8px;margin-top:16px}
1
+ .card.svelte-13338y7{position:relative;background:var(--bg-card);padding:16px 16px 14px;transition:transform 80ms steps(2,end),box-shadow 80ms steps(2,end)}.card.svelte-13338y7:hover:not(.busy){box-shadow:6px 6px 0 var(--shadow);transform:translate(-1px,-1px)}.busy-overlay.svelte-13338y7{position:absolute;z-index:1;display:flex;background:color-mix(in srgb,var(--bg-card)78%,transparent);justify-content:center;align-items: center;inset:0}.busy-label.svelte-13338y7{font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.1em;border:1px solid var(--fill);background:var(--fill);color:var(--fill-ink);animation:svelte-13338y7-lcd-blink 1.1s steps(1)infinite;padding:4px 9px;font-size:12px;font-weight:600}@keyframes svelte-13338y7-lcd-blink{50%{background:0 0;color:var(--fill)}}@media (prefers-reduced-motion:reduce){.busy-label.svelte-13338y7{animation:none}}.card-head.svelte-13338y7{display:flex;align-items: center;gap:11px}.attn.svelte-13338y7{border:1px solid var(--edge);flex:none;width:14px;height:14px}.attn.attn-done.svelte-13338y7{background:var(--attn-done);animation:svelte-13338y7-card-attn-pulse 1.4s ease-in-out infinite}.attn.attn-waiting.svelte-13338y7{background:var(--attn-waiting);animation:svelte-13338y7-card-attn-pulse 1.4s ease-in-out infinite}@keyframes svelte-13338y7-card-attn-pulse{0%,to{box-shadow:0 0 #0000;filter:brightness(.85)}50%{box-shadow:0 0 7px 2px color-mix(in srgb,currentColor 60%,transparent);filter:brightness(1.15)}}.attn.attn-done.svelte-13338y7{color:var(--attn-done)}.attn.attn-waiting.svelte-13338y7{color:var(--attn-waiting)}@media (prefers-reduced-motion:reduce){.attn.svelte-13338y7{animation:none;filter:none}}.name.svelte-13338y7{font-family:var(--font-mono);overflow-wrap:anywhere;color:var(--ink);text-align:left;cursor:pointer;background:0 0;border:1px solid #0000;flex:1;min-width:0;margin:0;padding:2px 4px;font-size:15px;font-weight:600}.name.svelte-13338y7:hover{border-color:var(--ink-faint)}.name-edit.svelte-13338y7{font-family:var(--font-mono);border:1px solid var(--ink);background:var(--bg);color:var(--ink);outline:none;flex:1;min-width:0;margin:0;padding:2px 4px;font-size:15px;font-weight:600}.agent-run.svelte-13338y7{display:flex;font-family:var(--font-mono);color:var(--ink-soft);align-items: center;gap:6px;min-width:0;margin-top:8px;font-size:11px}.agent-dot.svelte-13338y7{background:var(--fill);animation:svelte-13338y7-agent-pulse 1.6s ease-in-out infinite;border-radius:50%;flex:none;width:6px;height:6px}.agent-text.svelte-13338y7{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}@keyframes svelte-13338y7-agent-pulse{50%{opacity:.25}}.path.svelte-13338y7{font-family:var(--font-mono);color:var(--ink-soft);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin-top:10px;font-size:12px}.card-error.svelte-13338y7{font-family:var(--font-mono);color:var(--ink);background:var(--bg);border:1px solid var(--edge);overflow:auto;max-height:80px;margin-top:10px;padding:8px 10px;font-size:12px}.actions.svelte-13338y7{display:flex;flex-wrap:wrap;gap:8px;margin-top:16px}
@@ -1 +1 @@
1
- .page.svelte-1n6ht58{display:flex;flex-direction:column;min-height:100vh}.title.svelte-1n6ht58{display:inline-flex;font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.08em;color:var(--ink-soft);align-items: center;padding:0 14px;font-size:12px}.content.svelte-1n6ht58{display:flex;flex-direction:column;flex:1;align-items: center;gap:16px;padding:32px 20px}.card.svelte-1n6ht58{background:var(--bg-card);border:1px solid var(--rule);width:100%;max-width:560px;height:max-content}.danger-card.svelte-1n6ht58{border-color:var(--danger)}details.advanced.svelte-1n6ht58>summary:where(.svelte-1n6ht58){display:flex;cursor:pointer;list-style:none;font-family:var(--font-mono);align-items: center;gap:10px;padding:14px 16px}details.advanced.svelte-1n6ht58>summary:where(.svelte-1n6ht58)::-webkit-details-marker{display:none}details.advanced.svelte-1n6ht58>summary:where(.svelte-1n6ht58) .name:where(.svelte-1n6ht58){font-size:13px}details.advanced.svelte-1n6ht58>summary:where(.svelte-1n6ht58) .hint:where(.svelte-1n6ht58){color:var(--ink-soft);font-size:12px}details.advanced.svelte-1n6ht58>summary:where(.svelte-1n6ht58):after{content:"▸";color:var(--ink-soft);margin-left:auto}details.advanced[open].svelte-1n6ht58>summary:where(.svelte-1n6ht58):after{content:"▾"}.disabled-card.svelte-1n6ht58{opacity:.55;pointer-events:none}.danger-card.svelte-1n6ht58 .btn{white-space:nowrap;flex:none}.version.svelte-1n6ht58{font-family:var(--font-mono);letter-spacing:.08em;color:var(--ink-soft);font-size:11px}.shutting.svelte-1n6ht58{font-family:var(--font-mono);color:var(--danger);text-align:right;flex:none;max-width:200px;font-size:12px;line-height:1.4}.row.svelte-1n6ht58{display:flex;justify-content:space-between;align-items: center;gap:16px;padding:18px}.row.divided.svelte-1n6ht58{border-top:1px solid var(--rule);align-items: flex-start}.row.divided.svelte-1n6ht58 .btn{white-space:nowrap;flex:none}.sub.svelte-1n6ht58{padding:0 18px 14px}code.svelte-1n6ht58{font-family:var(--font-mono);background:var(--bg);border:1px solid var(--rule);border-radius:3px;padding:1px 4px;font-size:.92em}.label.svelte-1n6ht58{display:flex;color:var(--ink);align-items: flex-start;gap:12px;min-width:0}.label.svelte-1n6ht58>svg{flex:none}.text.svelte-1n6ht58{min-width:0}.name.svelte-1n6ht58{font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.06em;font-size:13px}.arch.svelte-1n6ht58{letter-spacing:.04em;color:var(--ink-soft);border:1px solid var(--rule);vertical-align:middle;border-radius:3px;margin-left:8px;padding:2px 6px;font-size:11px}.tip.svelte-1n6ht58{color:var(--ink-soft);background:color-mix(in srgb,var(--info)10%,transparent);border:1px solid color-mix(in srgb,var(--info)35%,transparent);border-radius:4px;width:100%;margin-top:4px;padding:8px 10px}.desc.svelte-1n6ht58{font-family:var(--font-mono);color:var(--ink-faint);margin-top:4px;font-size:12px;line-height:1.4}.image-row.svelte-1n6ht58,.token-row.svelte-1n6ht58{flex-wrap:wrap}.image-controls.svelte-1n6ht58{display:flex;flex:1;justify-content:flex-end;align-items: center;gap:8px;min-width:220px}.image-input.svelte-1n6ht58{font-family:var(--font-mono);color:var(--ink);background:var(--bg);border:1px solid var(--rule);flex:1;min-width:0;padding:8px 10px;font-size:12px}.image-input.svelte-1n6ht58:focus-visible{outline:2px solid var(--ink);outline-offset:1px}.image-controls.svelte-1n6ht58 .btn{flex:none}.msg.svelte-1n6ht58{font-family:var(--font-mono);width:100%;margin-top:2px;font-size:12px;line-height:1.4}.msg.error.svelte-1n6ht58{color:var(--danger)}.msg.ok.svelte-1n6ht58{color:var(--ink-soft)}.pets.svelte-1n6ht58{display:grid;grid-template-columns:repeat(auto-fill,minmax(44px,1fr));gap:6px}.pets.svelte-1n6ht58 form:where(.svelte-1n6ht58){display:contents}.pet.svelte-1n6ht58:disabled{cursor:progress}.pet.svelte-1n6ht58{display:flex;appearance:none;cursor:pointer;background:0 0;border:1px solid #0000;justify-content:center;align-items: center;padding:4px}.pet.svelte-1n6ht58:hover{border-color:var(--rule)}.pet.selected.svelte-1n6ht58{border-color:var(--attn-done);background:var(--switch-on-bg)}.pet.svelte-1n6ht58:focus-visible{outline:2px solid var(--ink);outline-offset:1px}.mode-toggle.svelte-1n6ht58{display:flex;border:1px solid var(--edge);flex:none}.mode-btn.svelte-1n6ht58{display:inline-flex;font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.05em;background:var(--bg);color:var(--ink);cursor:pointer;border:none;align-items: center;gap:6px;padding:7px 14px;font-size:12px;font-weight:700}.mode-btn.svelte-1n6ht58+.mode-btn:where(.svelte-1n6ht58){border-left:1px solid var(--edge)}.mode-btn.active.svelte-1n6ht58{background:var(--fill);color:var(--fill-ink)}.mode-btn.svelte-1n6ht58:disabled{cursor:not-allowed}.switch.svelte-1n6ht58{position:relative;cursor:pointer;flex:none}.switch.svelte-1n6ht58 input:where(.svelte-1n6ht58){position:absolute;opacity:0;cursor:pointer;margin:0;inset:0}.track.svelte-1n6ht58{display:block;border:1px solid var(--rule);background:var(--bg);border-radius:999px;width:44px;height:24px;transition:background .15s}.thumb.svelte-1n6ht58{display:block;background:var(--ink-faint);border-radius:999px;width:18px;height:18px;margin:2px;transition:transform .15s,background .15s}.switch.svelte-1n6ht58 input:where(.svelte-1n6ht58):checked+.track:where(.svelte-1n6ht58){background:var(--switch-on-bg)}.switch.svelte-1n6ht58 input:where(.svelte-1n6ht58):checked+.track:where(.svelte-1n6ht58) .thumb:where(.svelte-1n6ht58){background:var(--ink);transform:translate(20px)}.switch.svelte-1n6ht58 input:where(.svelte-1n6ht58):focus-visible+.track:where(.svelte-1n6ht58){outline:2px solid var(--ink);outline-offset:2px}.model-fields.svelte-1n6ht58{display:flex;flex-direction:column;flex:1;gap:6px;min-width:220px}.model-row.svelte-1n6ht58{display:flex;align-items: center;gap:8px}.model-label.svelte-1n6ht58{font-family:var(--font-mono);color:var(--ink-soft);text-align:right;flex:none;width:72px;font-size:11px}.model-row.svelte-1n6ht58 .image-input:where(.svelte-1n6ht58){flex:1}.model-save-row.svelte-1n6ht58{display:flex;justify-content:flex-end;margin-top:4px}.var-row.svelte-1n6ht58{flex-direction:column;align-items:stretch;gap:10px}.var-list.svelte-1n6ht58{display:flex;flex-direction:column;gap:6px}.var-item.svelte-1n6ht58{display:flex;background:var(--bg);border:1px solid var(--rule);align-items: center;gap:8px;padding:6px 8px}.var-name.svelte-1n6ht58{font-family:var(--font-mono);color:var(--ink);word-break:break-all;flex:1;min-width:0;font-size:12px}.var-status.svelte-1n6ht58{font-family:var(--font-mono);letter-spacing:.04em;color:var(--danger);flex:none;font-size:11px}.var-status.present.svelte-1n6ht58{color:var(--ink-soft)}.var-remove.svelte-1n6ht58{display:inline-flex;color:var(--ink-soft);border:1px solid var(--rule);cursor:pointer;background:0 0;flex:none;justify-content:center;align-items: center;width:22px;height:22px;padding:0}.var-remove.svelte-1n6ht58:hover:not(:disabled){color:var(--danger);border-color:var(--danger)}.var-remove.svelte-1n6ht58:disabled{opacity:var(--dim);cursor:not-allowed}.var-empty.svelte-1n6ht58{font-family:var(--font-mono);color:var(--ink-faint);font-size:12px}.var-add.svelte-1n6ht58{display:flex;gap:8px}.var-add.svelte-1n6ht58 .btn{flex:none}
1
+ .page.svelte-1n6ht58{display:flex;flex-direction:column;min-height:100vh}.title.svelte-1n6ht58{display:inline-flex;font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.08em;color:var(--ink-soft);align-items: center;padding:0 14px;font-size:12px}.content.svelte-1n6ht58{display:flex;flex-direction:column;flex:1;align-items: center;gap:16px;padding:32px 20px}.card.svelte-1n6ht58{background:var(--bg-card);border:1px solid var(--rule);width:100%;max-width:560px;height:max-content}.danger-card.svelte-1n6ht58{border-color:var(--danger)}details.advanced.svelte-1n6ht58>summary:where(.svelte-1n6ht58){display:flex;cursor:pointer;list-style:none;font-family:var(--font-mono);align-items: center;gap:10px;padding:14px 16px}details.advanced.svelte-1n6ht58>summary:where(.svelte-1n6ht58)::-webkit-details-marker{display:none}details.advanced.svelte-1n6ht58>summary:where(.svelte-1n6ht58) .name:where(.svelte-1n6ht58){font-size:13px}details.advanced.svelte-1n6ht58>summary:where(.svelte-1n6ht58) .hint:where(.svelte-1n6ht58){color:var(--ink-soft);font-size:12px}details.advanced.svelte-1n6ht58>summary:where(.svelte-1n6ht58):after{content:"▸";color:var(--ink-soft);margin-left:auto}details.advanced[open].svelte-1n6ht58>summary:where(.svelte-1n6ht58):after{content:"▾"}.disabled-card.svelte-1n6ht58{opacity:.55;pointer-events:none}.danger-card.svelte-1n6ht58 .btn{white-space:nowrap;flex:none}.version.svelte-1n6ht58{font-family:var(--font-mono);letter-spacing:.08em;color:var(--ink-soft);font-size:11px}.shutting.svelte-1n6ht58{font-family:var(--font-mono);color:var(--danger);text-align:right;flex:none;max-width:200px;font-size:12px;line-height:1.4}.row.svelte-1n6ht58{display:flex;justify-content:space-between;align-items: center;gap:16px;padding:18px}.row.divided.svelte-1n6ht58{border-top:1px solid var(--rule);align-items: flex-start}.row.divided.svelte-1n6ht58 .btn{white-space:nowrap;flex:none}.sub.svelte-1n6ht58{padding:0 18px 14px}code.svelte-1n6ht58{font-family:var(--font-mono);background:var(--bg);border:1px solid var(--rule);border-radius:3px;padding:1px 4px;font-size:.92em}.label.svelte-1n6ht58{display:flex;color:var(--ink);align-items: flex-start;gap:12px;min-width:0}.label.svelte-1n6ht58>svg{flex:none}.text.svelte-1n6ht58{min-width:0}.name.svelte-1n6ht58{font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.06em;font-size:13px}.arch.svelte-1n6ht58{letter-spacing:.04em;color:var(--ink-soft);border:1px solid var(--rule);vertical-align:middle;border-radius:3px;margin-left:8px;padding:2px 6px;font-size:11px}.tip.svelte-1n6ht58{color:var(--ink-soft);background:color-mix(in srgb,var(--info)10%,transparent);border:1px solid color-mix(in srgb,var(--info)35%,transparent);border-radius:4px;width:100%;margin-top:4px;padding:8px 10px}.desc.svelte-1n6ht58{font-family:var(--font-mono);color:var(--ink-faint);margin-top:4px;font-size:12px;line-height:1.4}.image-row.svelte-1n6ht58,.token-row.svelte-1n6ht58{flex-wrap:wrap}.image-controls.svelte-1n6ht58{display:flex;flex:1;justify-content:flex-end;align-items: center;gap:8px;min-width:220px}.image-input.svelte-1n6ht58{font-family:var(--font-mono);color:var(--ink);background:var(--bg);border:1px solid var(--rule);flex:1;min-width:0;padding:8px 10px;font-size:12px}.image-input.svelte-1n6ht58:focus-visible{outline:2px solid var(--ink);outline-offset:1px}.image-controls.svelte-1n6ht58 .btn{flex:none}.msg.svelte-1n6ht58{font-family:var(--font-mono);width:100%;margin-top:2px;font-size:12px;line-height:1.4}.msg.error.svelte-1n6ht58{color:var(--danger)}.msg.ok.svelte-1n6ht58{color:var(--ink-soft)}.pets.svelte-1n6ht58{display:grid;grid-template-columns:repeat(auto-fill,minmax(44px,1fr));gap:6px}.pets.svelte-1n6ht58 form:where(.svelte-1n6ht58){display:contents}.pet.svelte-1n6ht58:disabled{cursor:progress}.pet.svelte-1n6ht58{display:flex;appearance:none;cursor:pointer;background:0 0;border:1px solid #0000;justify-content:center;align-items: center;padding:4px}.pet.svelte-1n6ht58:hover{border-color:var(--rule)}.pet.selected.svelte-1n6ht58{border-color:var(--attn-done);background:var(--switch-on-bg)}.pet.svelte-1n6ht58:focus-visible{outline:2px solid var(--ink);outline-offset:1px}.mode-toggle.svelte-1n6ht58{display:flex;border:1px solid var(--edge);flex:none}.mode-btn.svelte-1n6ht58{display:inline-flex;font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.05em;background:var(--bg);color:var(--ink);cursor:pointer;border:none;align-items: center;gap:6px;padding:7px 14px;font-size:12px;font-weight:700}.mode-btn.svelte-1n6ht58+.mode-btn:where(.svelte-1n6ht58){border-left:1px solid var(--edge)}.mode-btn.active.svelte-1n6ht58{background:var(--fill);color:var(--fill-ink)}.mode-btn.svelte-1n6ht58:disabled{cursor:not-allowed}.switch.svelte-1n6ht58{position:relative;cursor:pointer;flex:none}.switch.svelte-1n6ht58 input:where(.svelte-1n6ht58){position:absolute;opacity:0;cursor:pointer;margin:0;inset:0}.track.svelte-1n6ht58{display:block;border:1px solid var(--rule);background:var(--bg);border-radius:999px;width:44px;height:24px;transition:background .15s}.thumb.svelte-1n6ht58{display:block;background:var(--ink-faint);border-radius:999px;width:18px;height:18px;margin:2px;transition:transform .15s,background .15s}.switch.svelte-1n6ht58 input:where(.svelte-1n6ht58):checked+.track:where(.svelte-1n6ht58){background:var(--switch-on-bg)}.switch.svelte-1n6ht58 input:where(.svelte-1n6ht58):checked+.track:where(.svelte-1n6ht58) .thumb:where(.svelte-1n6ht58){background:var(--ink);transform:translate(20px)}.switch.svelte-1n6ht58 input:where(.svelte-1n6ht58):focus-visible+.track:where(.svelte-1n6ht58){outline:2px solid var(--ink);outline-offset:2px}.model-fields.svelte-1n6ht58{display:flex;flex-direction:column;flex:1;gap:6px;min-width:220px}.mcp-actions.svelte-1n6ht58{display:flex;flex-wrap:wrap;gap:6px}.mcp-command.svelte-1n6ht58{display:block;overflow-x:auto;white-space:pre;border:1px solid var(--edge);background:var(--panel);padding:6px 8px;font-size:11px;line-height:1.5}.model-row.svelte-1n6ht58{display:flex;align-items: center;gap:8px}.model-label.svelte-1n6ht58{font-family:var(--font-mono);color:var(--ink-soft);text-align:right;flex:none;width:72px;font-size:11px}.model-row.svelte-1n6ht58 .image-input:where(.svelte-1n6ht58){flex:1}.model-save-row.svelte-1n6ht58{display:flex;justify-content:flex-end;margin-top:4px}.var-row.svelte-1n6ht58{flex-direction:column;align-items:stretch;gap:10px}.var-list.svelte-1n6ht58{display:flex;flex-direction:column;gap:6px}.var-item.svelte-1n6ht58{display:flex;background:var(--bg);border:1px solid var(--rule);align-items: center;gap:8px;padding:6px 8px}.var-name.svelte-1n6ht58{font-family:var(--font-mono);color:var(--ink);word-break:break-all;flex:1;min-width:0;font-size:12px}.var-status.svelte-1n6ht58{font-family:var(--font-mono);letter-spacing:.04em;color:var(--danger);flex:none;font-size:11px}.var-status.present.svelte-1n6ht58{color:var(--ink-soft)}.var-remove.svelte-1n6ht58{display:inline-flex;color:var(--ink-soft);border:1px solid var(--rule);cursor:pointer;background:0 0;flex:none;justify-content:center;align-items: center;width:22px;height:22px;padding:0}.var-remove.svelte-1n6ht58:hover:not(:disabled){color:var(--danger);border-color:var(--danger)}.var-remove.svelte-1n6ht58:disabled{opacity:var(--dim);cursor:not-allowed}.var-empty.svelte-1n6ht58{font-family:var(--font-mono);color:var(--ink-faint);font-size:12px}.var-add.svelte-1n6ht58{display:flex;gap:8px}.var-add.svelte-1n6ht58 .btn{flex:none}
package/README.md CHANGED
@@ -4,7 +4,9 @@
4
4
 
5
5
  ## Quick start
6
6
 
7
- Requires [Bun](https://bun.sh) >= 1.3.13 (Node.js is not supported) and a running Docker daemon (Docker Desktop, Colima, OrbStack…). macOS and Linux are supported; Windows is untested.
7
+ Requires [Bun](https://bun.sh) >= 1.3.13 (Node.js is not supported) and a running Docker daemon (Docker Desktop, Colima, OrbStack…). macOS, Linux and Windows are supported.
8
+
9
+ On Windows, Docker Desktop must use its **WSL2 backend**, so the workspace copies under `%USERPROFILE%\.codebay\` can be bind-mounted into the Linux daemon. Symlinks in a source folder are copied as regular files rather than recreated, because Windows only permits creating them under Developer Mode — the same thing Git for Windows does by default.
8
10
 
9
11
  ```sh
10
12
  bunx codebay@latest
@@ -25,6 +27,35 @@ The UI opens at `http://localhost:6969`. State (SQLite DB + per-instance workspa
25
27
  - `CODEBAY_GITHUB_TOKEN` — GitHub token to inject instead of reading `gh auth token` from the host
26
28
  - `DISABLE_OPEN_BROWSER=1` — skip opening the browser on startup
27
29
 
30
+ ## MCP server
31
+
32
+ Codebay can expose itself to other AI agents over [MCP](https://modelcontextprotocol.io), so an agent
33
+ can spin up a sandbox, run Claude Code in it non-interactively, and read back the result.
34
+
35
+ It is **off by default**. Turn it on under Settings → **MCP server**, then copy the registration line
36
+ it shows you:
37
+
38
+ ```sh
39
+ claude mcp add --transport http codebay http://localhost:6969/mcp \
40
+ --header "Authorization: Bearer <token>"
41
+ ```
42
+
43
+ The endpoint is `/mcp`. It returns `404` while disabled and `401` without a valid bearer token, and
44
+ it is the one place that does not use `BASIC_AUTH_PASSWORD` — MCP clients send the token instead.
45
+
46
+ The tools cover the whole loop: `create_sandbox`, `run_agent`, `get_run`, `list_runs`, `stop_run`,
47
+ `get_diff`, `read_file`, `write_file`, `exec_command`, `git_push`, `create_pr`, `get_logs`,
48
+ `list_sandboxes`, `get_sandbox` and `delete_sandbox`. Runs are asynchronous — `run_agent` hands back
49
+ a run id and the work continues in the background, surviving a manager restart.
50
+
51
+ Sandboxes created this way are ordinary instances: they show up on the dashboard with a live
52
+ "agent running" line, and you can open the IDE to watch. They persist until an agent (or you)
53
+ deletes them.
54
+
55
+ > **Anything holding the token can create containers and run agents with your GitHub and Claude
56
+ > credentials, with permission prompts bypassed inside the container.** Treat it like a password, and
57
+ > regenerate it from Settings if it leaks.
58
+
28
59
  ## Troubleshooting
29
60
 
30
61
  - `403 Cross-site POST form submissions are forbidden` when clicking anything that saves — codebay is being reached at an origin other than `http://localhost:<PORT>`. Set `PUBLIC_ORIGIN` to the URL in your browser's address bar and restart (see Configuration).
@@ -0,0 +1,24 @@
1
+ CREATE TABLE IF NOT EXISTS agent_runs (
2
+ id TEXT PRIMARY KEY,
3
+ instance_id TEXT NOT NULL,
4
+ prompt TEXT NOT NULL,
5
+ status TEXT NOT NULL,
6
+ session_id TEXT,
7
+ resume_session_id TEXT,
8
+ options TEXT,
9
+ result TEXT,
10
+ structured_output TEXT,
11
+ last_activity TEXT,
12
+ is_error INTEGER NOT NULL DEFAULT 0,
13
+ exit_code INTEGER,
14
+ cost_usd REAL,
15
+ duration_ms INTEGER,
16
+ num_turns INTEGER,
17
+ error TEXT,
18
+ created_at INTEGER NOT NULL,
19
+ started_at INTEGER,
20
+ finished_at INTEGER
21
+ );
22
+
23
+ CREATE INDEX IF NOT EXISTS idx_agent_runs_instance ON agent_runs(instance_id, created_at DESC);
24
+ CREATE INDEX IF NOT EXISTS idx_agent_runs_status ON agent_runs(status);
@@ -0,0 +1,2 @@
1
+ -- The model Claude Code actually ran with, from the stream's `init` event; the requested alias stays in `options`.
2
+ ALTER TABLE agent_runs ADD COLUMN model TEXT;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codebay",
3
- "version": "0.14.0",
3
+ "version": "0.15.0",
4
4
  "description": "Web UI for spinning up isolated devcontainer instances from any local folder, each with a browser-based VS Code (code-server) — built for parallel Claude Code workflows.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -63,13 +63,17 @@
63
63
  "@fontsource-variable/doto": "^5.3.0",
64
64
  "@fontsource-variable/jetbrains-mono": "^5.3.0",
65
65
  "@lucide/svelte": "^1.31.0",
66
+ "@tmcp/adapter-valibot": "^0.1.6",
67
+ "@tmcp/transport-http": "^0.9.0",
66
68
  "@xterm/addon-fit": "^0.11.0",
67
69
  "@xterm/xterm": "^6.0.0",
68
70
  "@zihaolam/bun-sqlite-migrations": "^1.0.3",
69
71
  "dockerode": "^5.0.1",
70
72
  "mochi-framework": "^0.9.1",
71
73
  "svelte": "^5.56.9",
72
- "svelte-french-toast": "^1.2.0"
74
+ "svelte-french-toast": "^1.2.0",
75
+ "tmcp": "^1.20.0",
76
+ "valibot": "^1.1.0"
73
77
  },
74
78
  "devDependencies": {
75
79
  "@eslint/js": "^10.0.1",