@chibitek/chibi 0.1.155 → 0.1.158

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 (98) hide show
  1. package/dist/assets/{abnfDiagram-O67JEVCF-B8LdsCr1.js → abnfDiagram-O67JEVCF-B8-8zzMS.js} +1 -1
  2. package/dist/assets/{arc-DU5fSmKU.js → arc-CSHQ9Dx3.js} +1 -1
  3. package/dist/assets/{architectureDiagram-NJMV4G6O-DdQSIFKv.js → architectureDiagram-NJMV4G6O-BCHM9rXW.js} +1 -1
  4. package/dist/assets/{blockDiagram-BEXU5L5S-CaPcGYSS.js → blockDiagram-BEXU5L5S-B-5uxPFN.js} +1 -1
  5. package/dist/assets/{c4Diagram-YGBWAQC7-unt0ePgO.js → c4Diagram-YGBWAQC7-CuKNM_MR.js} +1 -1
  6. package/dist/assets/channel-bPN03jIt.js +1 -0
  7. package/dist/assets/{chunk-2Q5K7J3B-Babg5Fzu.js → chunk-2Q5K7J3B-BNF02GM4.js} +1 -1
  8. package/dist/assets/{chunk-3FUC2YCW-D1-dWVHo.js → chunk-3FUC2YCW-uEWgBd4M.js} +2 -2
  9. package/dist/assets/{chunk-5VM5RSS4-DU7nSMbT.js → chunk-5VM5RSS4-eSZuXctY.js} +1 -1
  10. package/dist/assets/{chunk-GTNCS2PH-QIjCJIhV.js → chunk-GTNCS2PH-CslS8nRS.js} +1 -1
  11. package/dist/assets/{chunk-GWA4HPMP-pSByUE5R.js → chunk-GWA4HPMP-w71-MeKj.js} +1 -1
  12. package/dist/assets/{chunk-JWPE2WC7-Dj2AaR1b.js → chunk-JWPE2WC7-Bjju7WGh.js} +1 -1
  13. package/dist/assets/{chunk-NETBCI7D-pZbN0-FP.js → chunk-NETBCI7D-HxrroD4M.js} +1 -1
  14. package/dist/assets/{chunk-WEXAMYUT-Bqarwe4D.js → chunk-WEXAMYUT-D7Rz470x.js} +1 -1
  15. package/dist/assets/{chunk-XXDRQBXY-WV8lm3aa.js → chunk-XXDRQBXY-BWKnAeyM.js} +1 -1
  16. package/dist/assets/{classDiagram-v2-NBCMYWYE-BANBtky1.js → classDiagram-v2-NBCMYWYE-CeBDRHmP.js} +1 -1
  17. package/dist/assets/{cose-bilkent-JH36ORCC-CtrktgBC.js → cose-bilkent-JH36ORCC-BBbRKlvr.js} +1 -1
  18. package/dist/assets/{cynefin-OW5HDTMX-DnhVVfba.js → cynefin-OW5HDTMX-W02cnJ0B.js} +1 -1
  19. package/dist/assets/{cynefinDiagram-VND7K2PF-B42SRR6x.js → cynefinDiagram-VND7K2PF-Cs47eaxd.js} +1 -1
  20. package/dist/assets/{dagre-6A5THRUB-DM--vFMK.js → dagre-6A5THRUB-8mG18hzT.js} +1 -1
  21. package/dist/assets/{diagram-22UHCM2B-ZBupBrPU.js → diagram-22UHCM2B-DF4Qrw67.js} +1 -1
  22. package/dist/assets/{diagram-3UASUU5V-BPQzNIOd.js → diagram-3UASUU5V-3BAVoed1.js} +1 -1
  23. package/dist/assets/{diagram-ATOU4E4O-5kO8vf8Z.js → diagram-ATOU4E4O-CKO3pECE.js} +1 -1
  24. package/dist/assets/{diagram-CDSNMT55-DtxogB86.js → diagram-CDSNMT55-CZhrGvur.js} +1 -1
  25. package/dist/assets/{diagram-MLGK6HIB-Co9-nYw_.js → diagram-MLGK6HIB-C07l9pdQ.js} +1 -1
  26. package/dist/assets/{diagram-MPIPVDR6-DWRuMh5y.js → diagram-MPIPVDR6-D3or4lkZ.js} +1 -1
  27. package/dist/assets/{ebnfDiagram-ZINNZB2B-DU_nmoG9.js → ebnfDiagram-ZINNZB2B-DV2epXp_.js} +1 -1
  28. package/dist/assets/{elk-276RUBZZ-Bcje7S94.js → elk-276RUBZZ-DlvEn4-1.js} +1 -1
  29. package/dist/assets/{erDiagram-OPXOYQCR-9aCo_2CI.js → erDiagram-OPXOYQCR-CB6X7Wt5.js} +1 -1
  30. package/dist/assets/{flowDiagram-KWPJA3E3-D4FYFyc5.js → flowDiagram-KWPJA3E3-C3bqz4lC.js} +1 -1
  31. package/dist/assets/{ganttDiagram-FUAMR5RP-WQHX5rbx.js → ganttDiagram-FUAMR5RP-yo_Evhab.js} +1 -1
  32. package/dist/assets/{gitGraphDiagram-X574FWY7-DRyzBdiV.js → gitGraphDiagram-X574FWY7-Cq2oUlap.js} +1 -1
  33. package/dist/assets/{index-CG1OvRgO.js → index-CG4AFPb4.js} +1 -1
  34. package/dist/assets/{index-D_8SD_0K.js → index-CMcr5k3Z.js} +205 -200
  35. package/dist/assets/index-CZjRcUdn.css +1 -0
  36. package/dist/assets/{infoDiagram-VRGFBTTK-D7SOmp4n.js → infoDiagram-VRGFBTTK-Y3ZrY7qL.js} +1 -1
  37. package/dist/assets/{ishikawaDiagram-OU5B5YK6-CdxOnDVX.js → ishikawaDiagram-OU5B5YK6-BpOtSfyi.js} +1 -1
  38. package/dist/assets/{journeyDiagram-ZHPQQLJL-CH7477Oa.js → journeyDiagram-ZHPQQLJL-UQM727le.js} +1 -1
  39. package/dist/assets/{kanban-definition-PNTS6WVX-BdpVvkHd.js → kanban-definition-PNTS6WVX-C3s5oHpY.js} +1 -1
  40. package/dist/assets/{linear-DTGq6qn5.js → linear-hX6nFOQr.js} +1 -1
  41. package/dist/assets/{mermaid.core-Sn2sv0Ti.js → mermaid.core-DZObGLN7.js} +5 -5
  42. package/dist/assets/{mindmap-definition-NLK3R4M7-koCmkYuq.js → mindmap-definition-NLK3R4M7-Br-i2E3P.js} +1 -1
  43. package/dist/assets/{pegDiagram-GJSIUBJH-C3sqhm8e.js → pegDiagram-GJSIUBJH-D5SERB-N.js} +1 -1
  44. package/dist/assets/{pieDiagram-5QR66LMP-BELBK122.js → pieDiagram-5QR66LMP-BPNf4J3D.js} +1 -1
  45. package/dist/assets/{quadrantDiagram-O4NWA36T-Cksi5LSm.js → quadrantDiagram-O4NWA36T-Bm_Wh7Jr.js} +1 -1
  46. package/dist/assets/{railroadDiagram-XR7U4H2S-f1av_EZW.js → railroadDiagram-XR7U4H2S-D6bo0abD.js} +1 -1
  47. package/dist/assets/{requirementDiagram-PLB6GJNP-CW4O5nER.js → requirementDiagram-PLB6GJNP--1n5rkV7.js} +1 -1
  48. package/dist/assets/{sankeyDiagram-IPEJSGJF-Dz8ADHNY.js → sankeyDiagram-IPEJSGJF-Ct4tP2kK.js} +1 -1
  49. package/dist/assets/{sequenceDiagram-PO4LG4MO-Cc3HE0iZ.js → sequenceDiagram-PO4LG4MO-CCG7O5fR.js} +1 -1
  50. package/dist/assets/{sizeCapture-INFHLROL-gdaZNHnK.js → sizeCapture-INFHLROL-CJJJra9c.js} +1 -1
  51. package/dist/assets/{stateDiagram-v2-GCMORJYK--u8LCnab.js → stateDiagram-v2-GCMORJYK-BMdRvVEX.js} +1 -1
  52. package/dist/assets/{swimlanes-2SLR337P-4MAw4kdO.js → swimlanes-2SLR337P-B1wnogip.js} +1 -1
  53. package/dist/assets/swimlanesDiagram-TC7HE7FX-DWyjdGGR.js +8 -0
  54. package/dist/assets/{timeline-definition-EJHVYXUP-CL89YkCT.js → timeline-definition-EJHVYXUP-l-3yhXUP.js} +1 -1
  55. package/dist/assets/{usecaseDiagram-POWQR4AR-C92l7YQe.js → usecaseDiagram-POWQR4AR-DtwtCOoJ.js} +1 -1
  56. package/dist/assets/{vennDiagram-UO4OBE2U-17siYhSj.js → vennDiagram-UO4OBE2U-BZAEce1B.js} +1 -1
  57. package/dist/assets/{wardleyDiagram-VNRHLVJA-CNwa9VmN.js → wardleyDiagram-VNRHLVJA-Na_BCMMO.js} +1 -1
  58. package/dist/assets/{xychartDiagram-PMCCYNJV-Dnu6Xzih.js → xychartDiagram-PMCCYNJV-BW9dlUfE.js} +1 -1
  59. package/dist/index.html +2 -2
  60. package/dist-server/container-mcp.js +68 -14
  61. package/dist-server/index.js +5633 -4562
  62. package/dist-server/openmausbot.js +140 -81
  63. package/dist-server/pair-cli.js +140 -81
  64. package/dist-server/server/app-installers.js +107 -7
  65. package/dist-server/server/avatar-subscription.js +51 -0
  66. package/dist-server/server/cli-help.js +74 -0
  67. package/dist-server/server/cli.js +37 -78
  68. package/dist-server/server/colima-docker.js +136 -0
  69. package/dist-server/server/container-computer.js +31 -63
  70. package/dist-server/server/container-mcp.js +5 -0
  71. package/dist-server/server/device-routing.js +60 -0
  72. package/dist-server/server/drivers/claude.js +13 -11
  73. package/dist-server/server/drivers/local-inject.js +3 -4
  74. package/dist-server/server/engine-failover.js +22 -12
  75. package/dist-server/server/engine-tiers.js +5 -14
  76. package/dist-server/server/index.js +124 -120
  77. package/dist-server/server/personal-goal-runtime.js +86 -0
  78. package/dist-server/server/personal-goal-turn.js +125 -0
  79. package/dist-server/server/personal-goals.js +195 -0
  80. package/dist-server/server/port-in-use.js +21 -0
  81. package/dist-server/server/routes/local-models.js +3 -2
  82. package/dist-server/server/session-fallback.js +76 -0
  83. package/dist-server/server/skill-fetch.js +14 -0
  84. package/dist-server/server/vault/routes.js +43 -0
  85. package/dist-server/server/vault/runtime.js +48 -0
  86. package/dist-server/server/vault/store.js +177 -0
  87. package/dist-server/shared/desktop-private-port.js +1 -0
  88. package/dist-server/shared/engine-choices.js +15 -0
  89. package/dist-server/shared/engine-tiers.js +74 -2
  90. package/dist-server/shared/local-models.js +1 -3
  91. package/dist-server/shared/personal-goal.js +43 -0
  92. package/dist-server/shared/subscription-bots.js +3 -0
  93. package/dist-server/shared/vault.js +59 -0
  94. package/dist-server/vps-container-mcp.js +4 -2
  95. package/package.json +1 -1
  96. package/dist/assets/channel-GBaPOaz-.js +0 -1
  97. package/dist/assets/index-C8YP7Zxj.css +0 -1
  98. package/dist/assets/swimlanesDiagram-TC7HE7FX-CamSa0NE.js +0 -8
@@ -24992,7 +24992,6 @@ function idsFromModelsPayload(payload) {
24992
24992
  if (typeof id !== "string" || !MODEL_ID.test(id)) return [];
24993
24993
  const low = id.toLowerCase();
24994
24994
  if (low.includes("embed") || low.includes("bge-") || low.includes("nomic")) return [];
24995
- if (isOllamaCloudModel(record5)) return [];
24996
24995
  return [id];
24997
24996
  });
24998
24997
  }
@@ -25099,7 +25098,7 @@ async function probeLocalInjects(env = process.env, fetchImpl = fetch) {
25099
25098
  id: encodeInjectId(host.id, model),
25100
25099
  host: host.id,
25101
25100
  model,
25102
- label: `${model} (${host.label})`,
25101
+ label: isOllamaCloudModel(model) ? `${model} (${host.label}, plan-billed)` : `${model} (${host.label})`,
25103
25102
  loaded: loaded.has(model),
25104
25103
  ...contextWindow ? { contextWindow } : {}
25105
25104
  });
@@ -30445,6 +30444,7 @@ var init_claude = __esm({
30445
30444
  active.delete(threadId);
30446
30445
  session.turn = null;
30447
30446
  retryState.delete(threadId);
30447
+ if (t2.authFailed) closeSession(threadId, "sign-in required");
30448
30448
  emit({ ...base3(threadId, t2.turnId), type: "turn.completed", ok, stopReason, cost, ...usage ? { usage } : {} });
30449
30449
  if (session.child.exitCode === null && !session.closing) armIdle(threadId);
30450
30450
  };
@@ -30634,6 +30634,11 @@ var init_claude = __esm({
30634
30634
  if (o.origin?.kind === "task-notification") break;
30635
30635
  {
30636
30636
  const t2 = session.turn;
30637
+ const resultError = [o.result, ...Array.isArray(o.errors) ? o.errors : []].filter((value) => typeof value === "string").join("\n");
30638
+ if (o.is_error === true && !t2?.authFailed && claudeAuthFailure({ error: o.error ?? "api_error" }, resultError)) {
30639
+ if (t2) t2.authFailed = true;
30640
+ emit({ ...base3(threadId, currentTurnId()), type: "runtime.error", message: resultError, setup: true, failure: "auth_failed" });
30641
+ }
30637
30642
  const usage = o.usage ? {
30638
30643
  input: (o.usage.input_tokens || 0) + (o.usage.cache_read_input_tokens || 0) + (o.usage.cache_creation_input_tokens || 0),
30639
30644
  output: o.usage.output_tokens || 0,
@@ -30641,7 +30646,7 @@ var init_claude = __esm({
30641
30646
  } : void 0;
30642
30647
  const earlier = t2?.carried;
30643
30648
  const result = {
30644
- ok: o.is_error !== true,
30649
+ ok: o.is_error !== true && !t2?.authFailed,
30645
30650
  stopReason: t2?.authFailed ? "auth_required" : t2?.refreshConflict && o.is_error === true ? "credential_refresh_conflict" : claudeResultStopReason(o),
30646
30651
  cost: sumCost(earlier?.cost ?? null, o.total_cost_usd ?? null),
30647
30652
  usage: sumUsage(earlier?.usage, usage)
@@ -58743,6 +58748,82 @@ var init_browser_engine = __esm({
58743
58748
  }
58744
58749
  });
58745
58750
 
58751
+ // server/cli-help.ts
58752
+ var USAGE = `chibi \u2014 your team of AI bots, ready in a few steps
58753
+
58754
+ chibi set up once, then open your workspace
58755
+ chibi setup [--data-dir DIR]
58756
+ chibi start [the same options as serve]
58757
+ chibi serve [--port 8799] [--data-dir DIR] [--label NAME]
58758
+ [--public-url https://host] [--tailscale | --tunnel | --domain HOST] [--no-pair]
58759
+ chibi pair [--label NAME] [--client] [--phone ios|android]
58760
+ [--public-url https://host]
58761
+ chibi sessions [revoke ID]
58762
+ chibi status
58763
+ chibi login [--email you@example.com]
58764
+ chibi logout
58765
+ chibi command --bot ID|--group ID --thread ID --text "/goal status" [--port 8799]
58766
+ chibi goal --bot ID --thread ID --text "--tokens 100000 objective"
58767
+ chibi access list | add EMAIL [--chat-only] | remove EMAIL
58768
+ chibi service install [--domain HOST | --tunnel | --tailscale] [--port N] [--data-dir DIR] | uninstall
58769
+ chibi browser install [--with-deps] | status
58770
+ chibi fleet init --domain HOST [--operator USER] | create NAME --admin EMAIL [--member EMAIL] [--brand FILE]
58771
+ [--anthropic-key-file FILE] [--cap USD] [--license-key KEY] [--memory 1G]
58772
+ | list | users NAME add|remove EMAIL [--chat-only] | suspend NAME | resume NAME
58773
+ | delete NAME --yes [--keep-data] | upgrade (all take --dry-run)
58774
+ | agent [--socket PATH] [--group USER] (root; installed by init --operator)
58775
+
58776
+ setup choose AI access and optional phone access; keep existing bots and chats
58777
+ start same as chibi: use your saved settings and open the workspace
58778
+ serve starts the server without prompts and prints a pairing link + QR code
58779
+ pair mints a pairing code against a running server (--client: chat only)
58780
+ sessions lists paired devices; "sessions revoke ID" signs one out
58781
+ status what the server says about itself
58782
+ login signs this machine in to an Atomic account (an emailed code)
58783
+ and reserves its public address for --tunnel
58784
+ logout releases that address and signs out
58785
+ access who may sign in with an emailed code at /pair: an address or
58786
+ @domain; --chat-only gives chat and approvals without settings.
58787
+ Takes effect at once, no restart.
58788
+ service keep the server running across reboots: writes a systemd unit
58789
+ (Linux) or a launchd agent (macOS) for the same serve options and
58790
+ prints the commands that install it. Install the package
58791
+ permanently first (npm install -g @chibitek/chibi).
58792
+ browser install: the bots' browser engine (agent-browser, pinned) into the
58793
+ data dir, and Chrome for Testing into the user's browser cache.
58794
+ --with-deps also installs
58795
+ the Linux libraries Chrome needs (run as root once). Then run
58796
+ browser install as the user running serve, from that user's home.
58797
+ status: what the current user and data directory have.
58798
+ fleet many client workspaces on one Linux server, each its own account,
58799
+ service, data folder, brand, sign-in list and keys at NAME.HOST
58800
+ behind the system Caddy. Plans are printed unless run as root;
58801
+ --dry-run always prints. Install the package permanently first.
58802
+ init --operator USER also installs the fleet agent, a root service
58803
+ on a Unix socket only USER may open, so the workspace running as
58804
+ USER manages the others from Settings \u2192 Workspaces.
58805
+
58806
+ --tailscale serve over your tailnet: Tailscale terminates HTTPS and the
58807
+ link uses this machine's MagicDNS name (needs Tailscale signed in
58808
+ and HTTPS certificates enabled for the tailnet)
58809
+ --tunnel serve at a public HTTPS address through a Cloudflare tunnel
58810
+ from a hosted control plane (set OMB_CONTROL_PLANE_URL; Atomic
58811
+ ships none). Run \`chibi login\` once on this machine first.
58812
+ --domain serve at https://HOST on your own domain: a pinned Caddy is
58813
+ downloaded once and run alongside the server, and gets the
58814
+ certificate itself. Point the domain's DNS at this machine and
58815
+ open ports 80 and 443.
58816
+
58817
+ --no-open do not open a browser window
58818
+ --no-pair skip phone setup and do not print a pairing code
58819
+ --local start locally this time, ignoring saved remote-access settings
58820
+ --quiet skip the welcome banner (it never shows when output is piped;
58821
+ NO_COLOR=1 shows it without color)
58822
+
58823
+ Install once with \`npm install -g @chibitek/chibi\`, then type \`chibi\`.
58824
+ Or run without a global install: \`npx @chibitek/chibi\`. Node 24+ is required.
58825
+ `;
58826
+
58746
58827
  // server/cli.ts
58747
58828
  var import_qrcode_terminal = __toESM(require_main(), 1);
58748
58829
  import { spawn as spawn10 } from "node:child_process";
@@ -60737,6 +60818,32 @@ function runServiceCommand(input, io2) {
60737
60818
  return 0;
60738
60819
  }
60739
60820
 
60821
+ // server/port-in-use.ts
60822
+ import { connect } from "node:net";
60823
+ function portInUse(port) {
60824
+ return new Promise((done) => {
60825
+ const socket = connect({ host: "127.0.0.1", port });
60826
+ socket.setTimeout(1e3);
60827
+ socket.once("connect", () => {
60828
+ socket.destroy();
60829
+ done(true);
60830
+ });
60831
+ socket.once("timeout", () => {
60832
+ socket.destroy();
60833
+ done(false);
60834
+ });
60835
+ socket.once("error", () => done(false));
60836
+ });
60837
+ }
60838
+ async function reportBusyPort(port, dataDirFlag) {
60839
+ for (const candidate of [port, Number(process.env.OMB_WEBHOOK_PORT || port + 1)]) {
60840
+ if (!await portInUse(candidate)) continue;
60841
+ console.error(`Port ${candidate} is already in use by another app. Choose a free port, for example: chibi start --port ${port + 20}${dataDirFlag}`);
60842
+ return true;
60843
+ }
60844
+ return false;
60845
+ }
60846
+
60740
60847
  // shared/chibi-mark.ts
60741
60848
  var CHIBI_GRID = [
60742
60849
  "A...............",
@@ -62433,7 +62540,7 @@ function describeTunnelState(state2, address) {
62433
62540
 
62434
62541
  // server/cli.ts
62435
62542
  var HERE2 = dirname13(fileURLToPath3(import.meta.url));
62436
- var COMMANDS = ["setup", "start", "serve", "pair", "sessions", "status", "login", "logout", "access", "service", "browser", "fleet", "help", "--help", "-h"];
62543
+ var COMMANDS = ["command", "goal", "setup", "start", "serve", "pair", "sessions", "status", "login", "logout", "access", "service", "browser", "fleet", "help", "--help", "-h"];
62437
62544
  function parseArgs2(argv, env = process.env) {
62438
62545
  const implicitStart = !argv.length || argv[0].startsWith("--") && argv[0] !== "--help";
62439
62546
  const [command = "start", ...rest] = implicitStart ? ["start", ...argv] : argv;
@@ -62461,7 +62568,11 @@ function parseArgs2(argv, env = process.env) {
62461
62568
  return v3;
62462
62569
  };
62463
62570
  try {
62464
- if (arg === "--port") options.port = Number(value());
62571
+ if ((options.command === "command" || options.command === "goal") && arg === "--bot") options.botId = value();
62572
+ else if ((options.command === "command" || options.command === "goal") && arg === "--group") options.groupId = value();
62573
+ else if ((options.command === "command" || options.command === "goal") && arg === "--thread") options.threadId = value();
62574
+ else if ((options.command === "command" || options.command === "goal") && arg === "--text") options.commandText = value();
62575
+ else if (arg === "--port") options.port = Number(value());
62465
62576
  else if (arg === "--data-dir") options.dataDir = resolve13(value());
62466
62577
  else if (arg === "--label") options.label = value();
62467
62578
  else if (arg === "--public-url") options.publicUrl = value().replace(/\/+$/, "");
@@ -62517,6 +62628,7 @@ function parseArgs2(argv, env = process.env) {
62517
62628
  if (!Number.isInteger(options.port) || options.port < 1 || options.port > 65535) return { error: "--port must be 1-65535" };
62518
62629
  if (options.publicUrl && !/^https?:\/\//.test(options.publicUrl)) return { error: "--public-url must start with http:// or https://" };
62519
62630
  if (options.tailscale && options.tunnel) return { error: "choose one of --tailscale (your tailnet) and --tunnel (a public address)" };
62631
+ if ((options.command === "command" || options.command === "goal") && (Boolean(options.botId) === Boolean(options.groupId) || !options.threadId || !options.commandText)) return { error: "command and goal require exactly one of --bot ID or --group ID, plus --thread ID --text TEXT" };
62520
62632
  if (options.command === "access" && !options.accessAction) return { error: "access needs one of: list, add EMAIL [--chat-only], remove EMAIL" };
62521
62633
  if (options.command === "service" && !options.serviceAction) return { error: "service needs one of: install [the same options as serve], uninstall" };
62522
62634
  if (options.domain && (options.tailscale || options.tunnel || options.publicUrl)) return { error: "--domain already gives the server its address; drop --tailscale, --tunnel and --public-url" };
@@ -62530,78 +62642,6 @@ function parseArgs2(argv, env = process.env) {
62530
62642
  }
62531
62643
  return options;
62532
62644
  }
62533
- var USAGE = `chibi \u2014 your team of AI bots, ready in a few steps
62534
-
62535
- chibi set up once, then open your workspace
62536
- chibi setup [--data-dir DIR]
62537
- chibi start [the same options as serve]
62538
- chibi serve [--port 8799] [--data-dir DIR] [--label NAME]
62539
- [--public-url https://host] [--tailscale | --tunnel | --domain HOST] [--no-pair]
62540
- chibi pair [--label NAME] [--client] [--phone ios|android]
62541
- [--public-url https://host]
62542
- chibi sessions [revoke ID]
62543
- chibi status
62544
- chibi login [--email you@example.com]
62545
- chibi logout
62546
- chibi access list | add EMAIL [--chat-only] | remove EMAIL
62547
- chibi service install [--domain HOST | --tunnel | --tailscale] [--port N] [--data-dir DIR] | uninstall
62548
- chibi browser install [--with-deps] | status
62549
- chibi fleet init --domain HOST [--operator USER] | create NAME --admin EMAIL [--member EMAIL] [--brand FILE]
62550
- [--anthropic-key-file FILE] [--cap USD] [--license-key KEY] [--memory 1G]
62551
- | list | users NAME add|remove EMAIL [--chat-only] | suspend NAME | resume NAME
62552
- | delete NAME --yes [--keep-data] | upgrade (all take --dry-run)
62553
- | agent [--socket PATH] [--group USER] (root; installed by init --operator)
62554
-
62555
- setup choose AI access and optional phone access; keep existing bots and chats
62556
- start same as chibi: use your saved settings and open the workspace
62557
- serve starts the server without prompts and prints a pairing link + QR code
62558
- pair mints a pairing code against a running server (--client: chat only)
62559
- sessions lists paired devices; "sessions revoke ID" signs one out
62560
- status what the server says about itself
62561
- login signs this machine in to an Atomic account (an emailed code)
62562
- and reserves its public address for --tunnel
62563
- logout releases that address and signs out
62564
- access who may sign in with an emailed code at /pair: an address or
62565
- @domain; --chat-only gives chat and approvals without settings.
62566
- Takes effect at once, no restart.
62567
- service keep the server running across reboots: writes a systemd unit
62568
- (Linux) or a launchd agent (macOS) for the same serve options and
62569
- prints the commands that install it. Install the package
62570
- permanently first (npm install -g @chibitek/chibi).
62571
- browser install: the bots' browser engine (agent-browser, pinned) into the
62572
- data dir, and Chrome for Testing into the user's browser cache.
62573
- --with-deps also installs
62574
- the Linux libraries Chrome needs (run as root once). Then run
62575
- browser install as the user running serve, from that user's home.
62576
- status: what the current user and data directory have.
62577
- fleet many client workspaces on one Linux server, each its own account,
62578
- service, data folder, brand, sign-in list and keys at NAME.HOST
62579
- behind the system Caddy. Plans are printed unless run as root;
62580
- --dry-run always prints. Install the package permanently first.
62581
- init --operator USER also installs the fleet agent, a root service
62582
- on a Unix socket only USER may open, so the workspace running as
62583
- USER manages the others from Settings \u2192 Workspaces.
62584
-
62585
- --tailscale serve over your tailnet: Tailscale terminates HTTPS and the
62586
- link uses this machine's MagicDNS name (needs Tailscale signed in
62587
- and HTTPS certificates enabled for the tailnet)
62588
- --tunnel serve at a public HTTPS address through a Cloudflare tunnel
62589
- from a hosted control plane (set OMB_CONTROL_PLANE_URL; Atomic
62590
- ships none). Run \`chibi login\` once on this machine first.
62591
- --domain serve at https://HOST on your own domain: a pinned Caddy is
62592
- downloaded once and run alongside the server, and gets the
62593
- certificate itself. Point the domain's DNS at this machine and
62594
- open ports 80 and 443.
62595
-
62596
- --no-open do not open a browser window
62597
- --no-pair skip phone setup and do not print a pairing code
62598
- --local start locally this time, ignoring saved remote-access settings
62599
- --quiet skip the welcome banner (it never shows when output is piped;
62600
- NO_COLOR=1 shows it without color)
62601
-
62602
- Install once with \`npm install -g @chibitek/chibi\`, then type \`chibi\`.
62603
- Or run without a global install: \`npx @chibitek/chibi\`. Node 24+ is required.
62604
- `;
62605
62645
  function defaultIo() {
62606
62646
  return {
62607
62647
  log: (line) => console.log(line),
@@ -63106,6 +63146,7 @@ async function runServe(options, log = console.log) {
63106
63146
  console.error(`something already answers on http://127.0.0.1:${options.port}; use \`chibi pair\` against it, or --port for a second server`);
63107
63147
  return 1;
63108
63148
  }
63149
+ if (await reportBusyPort(options.port, options.dataDir === join53(homedir29(), ".atomic-app") ? "" : ` --data-dir ${options.dataDir}`)) return 1;
63109
63150
  let publicUrl = options.publicUrl;
63110
63151
  let tailscale = null;
63111
63152
  if (options.tailscale) {
@@ -63373,10 +63414,9 @@ async function runOnboardingCommand(options, io2 = defaultIo(), startServer = ru
63373
63414
  saveCliStartup2(options.dataDir, startupPreferences(launch));
63374
63415
  }
63375
63416
  if (options.command === "setup") {
63376
- io2.log("\nAll set. Start with: chibi (or npx @chibitek/chibi without a global install).");
63377
- if (options.dataDir !== join53(homedir29(), ".atomic-app") || options.port !== 8799) {
63378
- io2.log(`Use the same --data-dir (${options.dataDir}) and --port (${options.port}) options when starting.`);
63379
- }
63417
+ const flags = [options.dataDir !== join53(homedir29(), ".atomic-app") && ` --data-dir ${options.dataDir}`, options.port !== 8799 && ` --port ${options.port}`].filter(Boolean).join("");
63418
+ io2.log(`
63419
+ All set. Start with: chibi${flags ? ` start${flags}` : ""} (or npx @chibitek/chibi without a global install).`);
63380
63420
  return 0;
63381
63421
  }
63382
63422
  if (saved) io2.log("\nWelcome back. Using your saved AI connection.");
@@ -63412,6 +63452,25 @@ ${welcomeBanner(options)}
63412
63452
  `);
63413
63453
  }
63414
63454
  switch (options.command) {
63455
+ case "command":
63456
+ case "goal": {
63457
+ const text4 = options.command === "goal" ? `/goal ${options.commandText}` : options.commandText;
63458
+ const roomGoal = options.groupId ? /^\/goal(?:\s+([\s\S]*))?$/.exec(text4 ?? "") : null;
63459
+ if (roomGoal && (!roomGoal[1]?.trim() || (roomGoal[1].startsWith("--") || /^(status|pause|resume|stop|help)(?:\s|$)/.test(roomGoal[1])))) {
63460
+ console.error("Room goals accept an objective. Personal goal controls and budgets require --bot.");
63461
+ return 2;
63462
+ }
63463
+ const result = await api(options.port, `/api/${options.groupId ? "groups" : "bots"}/${encodeURIComponent(options.groupId ?? options.botId)}/messages`, {
63464
+ method: "POST",
63465
+ body: JSON.stringify({ threadId: options.threadId, text: roomGoal ? roomGoal[1] : text4, ...roomGoal ? { mode: "goal" } : {}, sendId: randomBytes4(16).toString("hex") })
63466
+ });
63467
+ if (result.status >= 400) {
63468
+ console.error(result.body.error ?? `Server returned ${result.status}`);
63469
+ return 1;
63470
+ }
63471
+ console.log(options.json ? JSON.stringify(result.body) : result.body.goalSummary ?? JSON.stringify(result.body));
63472
+ return 0;
63473
+ }
63415
63474
  case "setup":
63416
63475
  case "start":
63417
63476
  return runOnboardingCommand(options);
@@ -24992,7 +24992,6 @@ function idsFromModelsPayload(payload) {
24992
24992
  if (typeof id !== "string" || !MODEL_ID.test(id)) return [];
24993
24993
  const low = id.toLowerCase();
24994
24994
  if (low.includes("embed") || low.includes("bge-") || low.includes("nomic")) return [];
24995
- if (isOllamaCloudModel(record5)) return [];
24996
24995
  return [id];
24997
24996
  });
24998
24997
  }
@@ -25099,7 +25098,7 @@ async function probeLocalInjects(env = process.env, fetchImpl = fetch) {
25099
25098
  id: encodeInjectId(host.id, model),
25100
25099
  host: host.id,
25101
25100
  model,
25102
- label: `${model} (${host.label})`,
25101
+ label: isOllamaCloudModel(model) ? `${model} (${host.label}, plan-billed)` : `${model} (${host.label})`,
25103
25102
  loaded: loaded.has(model),
25104
25103
  ...contextWindow ? { contextWindow } : {}
25105
25104
  });
@@ -30445,6 +30444,7 @@ var init_claude = __esm({
30445
30444
  active.delete(threadId);
30446
30445
  session.turn = null;
30447
30446
  retryState.delete(threadId);
30447
+ if (t2.authFailed) closeSession(threadId, "sign-in required");
30448
30448
  emit({ ...base3(threadId, t2.turnId), type: "turn.completed", ok, stopReason, cost, ...usage ? { usage } : {} });
30449
30449
  if (session.child.exitCode === null && !session.closing) armIdle(threadId);
30450
30450
  };
@@ -30634,6 +30634,11 @@ var init_claude = __esm({
30634
30634
  if (o.origin?.kind === "task-notification") break;
30635
30635
  {
30636
30636
  const t2 = session.turn;
30637
+ const resultError = [o.result, ...Array.isArray(o.errors) ? o.errors : []].filter((value) => typeof value === "string").join("\n");
30638
+ if (o.is_error === true && !t2?.authFailed && claudeAuthFailure({ error: o.error ?? "api_error" }, resultError)) {
30639
+ if (t2) t2.authFailed = true;
30640
+ emit({ ...base3(threadId, currentTurnId()), type: "runtime.error", message: resultError, setup: true, failure: "auth_failed" });
30641
+ }
30637
30642
  const usage = o.usage ? {
30638
30643
  input: (o.usage.input_tokens || 0) + (o.usage.cache_read_input_tokens || 0) + (o.usage.cache_creation_input_tokens || 0),
30639
30644
  output: o.usage.output_tokens || 0,
@@ -30641,7 +30646,7 @@ var init_claude = __esm({
30641
30646
  } : void 0;
30642
30647
  const earlier = t2?.carried;
30643
30648
  const result = {
30644
- ok: o.is_error !== true,
30649
+ ok: o.is_error !== true && !t2?.authFailed,
30645
30650
  stopReason: t2?.authFailed ? "auth_required" : t2?.refreshConflict && o.is_error === true ? "credential_refresh_conflict" : claudeResultStopReason(o),
30646
30651
  cost: sumCost(earlier?.cost ?? null, o.total_cost_usd ?? null),
30647
30652
  usage: sumUsage(earlier?.usage, usage)
@@ -58743,6 +58748,82 @@ var init_browser_engine = __esm({
58743
58748
  }
58744
58749
  });
58745
58750
 
58751
+ // server/cli-help.ts
58752
+ var USAGE = `chibi \u2014 your team of AI bots, ready in a few steps
58753
+
58754
+ chibi set up once, then open your workspace
58755
+ chibi setup [--data-dir DIR]
58756
+ chibi start [the same options as serve]
58757
+ chibi serve [--port 8799] [--data-dir DIR] [--label NAME]
58758
+ [--public-url https://host] [--tailscale | --tunnel | --domain HOST] [--no-pair]
58759
+ chibi pair [--label NAME] [--client] [--phone ios|android]
58760
+ [--public-url https://host]
58761
+ chibi sessions [revoke ID]
58762
+ chibi status
58763
+ chibi login [--email you@example.com]
58764
+ chibi logout
58765
+ chibi command --bot ID|--group ID --thread ID --text "/goal status" [--port 8799]
58766
+ chibi goal --bot ID --thread ID --text "--tokens 100000 objective"
58767
+ chibi access list | add EMAIL [--chat-only] | remove EMAIL
58768
+ chibi service install [--domain HOST | --tunnel | --tailscale] [--port N] [--data-dir DIR] | uninstall
58769
+ chibi browser install [--with-deps] | status
58770
+ chibi fleet init --domain HOST [--operator USER] | create NAME --admin EMAIL [--member EMAIL] [--brand FILE]
58771
+ [--anthropic-key-file FILE] [--cap USD] [--license-key KEY] [--memory 1G]
58772
+ | list | users NAME add|remove EMAIL [--chat-only] | suspend NAME | resume NAME
58773
+ | delete NAME --yes [--keep-data] | upgrade (all take --dry-run)
58774
+ | agent [--socket PATH] [--group USER] (root; installed by init --operator)
58775
+
58776
+ setup choose AI access and optional phone access; keep existing bots and chats
58777
+ start same as chibi: use your saved settings and open the workspace
58778
+ serve starts the server without prompts and prints a pairing link + QR code
58779
+ pair mints a pairing code against a running server (--client: chat only)
58780
+ sessions lists paired devices; "sessions revoke ID" signs one out
58781
+ status what the server says about itself
58782
+ login signs this machine in to an Atomic account (an emailed code)
58783
+ and reserves its public address for --tunnel
58784
+ logout releases that address and signs out
58785
+ access who may sign in with an emailed code at /pair: an address or
58786
+ @domain; --chat-only gives chat and approvals without settings.
58787
+ Takes effect at once, no restart.
58788
+ service keep the server running across reboots: writes a systemd unit
58789
+ (Linux) or a launchd agent (macOS) for the same serve options and
58790
+ prints the commands that install it. Install the package
58791
+ permanently first (npm install -g @chibitek/chibi).
58792
+ browser install: the bots' browser engine (agent-browser, pinned) into the
58793
+ data dir, and Chrome for Testing into the user's browser cache.
58794
+ --with-deps also installs
58795
+ the Linux libraries Chrome needs (run as root once). Then run
58796
+ browser install as the user running serve, from that user's home.
58797
+ status: what the current user and data directory have.
58798
+ fleet many client workspaces on one Linux server, each its own account,
58799
+ service, data folder, brand, sign-in list and keys at NAME.HOST
58800
+ behind the system Caddy. Plans are printed unless run as root;
58801
+ --dry-run always prints. Install the package permanently first.
58802
+ init --operator USER also installs the fleet agent, a root service
58803
+ on a Unix socket only USER may open, so the workspace running as
58804
+ USER manages the others from Settings \u2192 Workspaces.
58805
+
58806
+ --tailscale serve over your tailnet: Tailscale terminates HTTPS and the
58807
+ link uses this machine's MagicDNS name (needs Tailscale signed in
58808
+ and HTTPS certificates enabled for the tailnet)
58809
+ --tunnel serve at a public HTTPS address through a Cloudflare tunnel
58810
+ from a hosted control plane (set OMB_CONTROL_PLANE_URL; Atomic
58811
+ ships none). Run \`chibi login\` once on this machine first.
58812
+ --domain serve at https://HOST on your own domain: a pinned Caddy is
58813
+ downloaded once and run alongside the server, and gets the
58814
+ certificate itself. Point the domain's DNS at this machine and
58815
+ open ports 80 and 443.
58816
+
58817
+ --no-open do not open a browser window
58818
+ --no-pair skip phone setup and do not print a pairing code
58819
+ --local start locally this time, ignoring saved remote-access settings
58820
+ --quiet skip the welcome banner (it never shows when output is piped;
58821
+ NO_COLOR=1 shows it without color)
58822
+
58823
+ Install once with \`npm install -g @chibitek/chibi\`, then type \`chibi\`.
58824
+ Or run without a global install: \`npx @chibitek/chibi\`. Node 24+ is required.
58825
+ `;
58826
+
58746
58827
  // server/cli.ts
58747
58828
  var import_qrcode_terminal = __toESM(require_main(), 1);
58748
58829
  import { spawn as spawn10 } from "node:child_process";
@@ -60737,6 +60818,32 @@ function runServiceCommand(input, io2) {
60737
60818
  return 0;
60738
60819
  }
60739
60820
 
60821
+ // server/port-in-use.ts
60822
+ import { connect } from "node:net";
60823
+ function portInUse(port) {
60824
+ return new Promise((done) => {
60825
+ const socket = connect({ host: "127.0.0.1", port });
60826
+ socket.setTimeout(1e3);
60827
+ socket.once("connect", () => {
60828
+ socket.destroy();
60829
+ done(true);
60830
+ });
60831
+ socket.once("timeout", () => {
60832
+ socket.destroy();
60833
+ done(false);
60834
+ });
60835
+ socket.once("error", () => done(false));
60836
+ });
60837
+ }
60838
+ async function reportBusyPort(port, dataDirFlag) {
60839
+ for (const candidate of [port, Number(process.env.OMB_WEBHOOK_PORT || port + 1)]) {
60840
+ if (!await portInUse(candidate)) continue;
60841
+ console.error(`Port ${candidate} is already in use by another app. Choose a free port, for example: chibi start --port ${port + 20}${dataDirFlag}`);
60842
+ return true;
60843
+ }
60844
+ return false;
60845
+ }
60846
+
60740
60847
  // shared/chibi-mark.ts
60741
60848
  var CHIBI_GRID = [
60742
60849
  "A...............",
@@ -62433,7 +62540,7 @@ function describeTunnelState(state2, address) {
62433
62540
 
62434
62541
  // server/cli.ts
62435
62542
  var HERE2 = dirname13(fileURLToPath3(import.meta.url));
62436
- var COMMANDS = ["setup", "start", "serve", "pair", "sessions", "status", "login", "logout", "access", "service", "browser", "fleet", "help", "--help", "-h"];
62543
+ var COMMANDS = ["command", "goal", "setup", "start", "serve", "pair", "sessions", "status", "login", "logout", "access", "service", "browser", "fleet", "help", "--help", "-h"];
62437
62544
  function parseArgs2(argv, env = process.env) {
62438
62545
  const implicitStart = !argv.length || argv[0].startsWith("--") && argv[0] !== "--help";
62439
62546
  const [command = "start", ...rest] = implicitStart ? ["start", ...argv] : argv;
@@ -62461,7 +62568,11 @@ function parseArgs2(argv, env = process.env) {
62461
62568
  return v3;
62462
62569
  };
62463
62570
  try {
62464
- if (arg === "--port") options.port = Number(value());
62571
+ if ((options.command === "command" || options.command === "goal") && arg === "--bot") options.botId = value();
62572
+ else if ((options.command === "command" || options.command === "goal") && arg === "--group") options.groupId = value();
62573
+ else if ((options.command === "command" || options.command === "goal") && arg === "--thread") options.threadId = value();
62574
+ else if ((options.command === "command" || options.command === "goal") && arg === "--text") options.commandText = value();
62575
+ else if (arg === "--port") options.port = Number(value());
62465
62576
  else if (arg === "--data-dir") options.dataDir = resolve13(value());
62466
62577
  else if (arg === "--label") options.label = value();
62467
62578
  else if (arg === "--public-url") options.publicUrl = value().replace(/\/+$/, "");
@@ -62517,6 +62628,7 @@ function parseArgs2(argv, env = process.env) {
62517
62628
  if (!Number.isInteger(options.port) || options.port < 1 || options.port > 65535) return { error: "--port must be 1-65535" };
62518
62629
  if (options.publicUrl && !/^https?:\/\//.test(options.publicUrl)) return { error: "--public-url must start with http:// or https://" };
62519
62630
  if (options.tailscale && options.tunnel) return { error: "choose one of --tailscale (your tailnet) and --tunnel (a public address)" };
62631
+ if ((options.command === "command" || options.command === "goal") && (Boolean(options.botId) === Boolean(options.groupId) || !options.threadId || !options.commandText)) return { error: "command and goal require exactly one of --bot ID or --group ID, plus --thread ID --text TEXT" };
62520
62632
  if (options.command === "access" && !options.accessAction) return { error: "access needs one of: list, add EMAIL [--chat-only], remove EMAIL" };
62521
62633
  if (options.command === "service" && !options.serviceAction) return { error: "service needs one of: install [the same options as serve], uninstall" };
62522
62634
  if (options.domain && (options.tailscale || options.tunnel || options.publicUrl)) return { error: "--domain already gives the server its address; drop --tailscale, --tunnel and --public-url" };
@@ -62530,78 +62642,6 @@ function parseArgs2(argv, env = process.env) {
62530
62642
  }
62531
62643
  return options;
62532
62644
  }
62533
- var USAGE = `chibi \u2014 your team of AI bots, ready in a few steps
62534
-
62535
- chibi set up once, then open your workspace
62536
- chibi setup [--data-dir DIR]
62537
- chibi start [the same options as serve]
62538
- chibi serve [--port 8799] [--data-dir DIR] [--label NAME]
62539
- [--public-url https://host] [--tailscale | --tunnel | --domain HOST] [--no-pair]
62540
- chibi pair [--label NAME] [--client] [--phone ios|android]
62541
- [--public-url https://host]
62542
- chibi sessions [revoke ID]
62543
- chibi status
62544
- chibi login [--email you@example.com]
62545
- chibi logout
62546
- chibi access list | add EMAIL [--chat-only] | remove EMAIL
62547
- chibi service install [--domain HOST | --tunnel | --tailscale] [--port N] [--data-dir DIR] | uninstall
62548
- chibi browser install [--with-deps] | status
62549
- chibi fleet init --domain HOST [--operator USER] | create NAME --admin EMAIL [--member EMAIL] [--brand FILE]
62550
- [--anthropic-key-file FILE] [--cap USD] [--license-key KEY] [--memory 1G]
62551
- | list | users NAME add|remove EMAIL [--chat-only] | suspend NAME | resume NAME
62552
- | delete NAME --yes [--keep-data] | upgrade (all take --dry-run)
62553
- | agent [--socket PATH] [--group USER] (root; installed by init --operator)
62554
-
62555
- setup choose AI access and optional phone access; keep existing bots and chats
62556
- start same as chibi: use your saved settings and open the workspace
62557
- serve starts the server without prompts and prints a pairing link + QR code
62558
- pair mints a pairing code against a running server (--client: chat only)
62559
- sessions lists paired devices; "sessions revoke ID" signs one out
62560
- status what the server says about itself
62561
- login signs this machine in to an Atomic account (an emailed code)
62562
- and reserves its public address for --tunnel
62563
- logout releases that address and signs out
62564
- access who may sign in with an emailed code at /pair: an address or
62565
- @domain; --chat-only gives chat and approvals without settings.
62566
- Takes effect at once, no restart.
62567
- service keep the server running across reboots: writes a systemd unit
62568
- (Linux) or a launchd agent (macOS) for the same serve options and
62569
- prints the commands that install it. Install the package
62570
- permanently first (npm install -g @chibitek/chibi).
62571
- browser install: the bots' browser engine (agent-browser, pinned) into the
62572
- data dir, and Chrome for Testing into the user's browser cache.
62573
- --with-deps also installs
62574
- the Linux libraries Chrome needs (run as root once). Then run
62575
- browser install as the user running serve, from that user's home.
62576
- status: what the current user and data directory have.
62577
- fleet many client workspaces on one Linux server, each its own account,
62578
- service, data folder, brand, sign-in list and keys at NAME.HOST
62579
- behind the system Caddy. Plans are printed unless run as root;
62580
- --dry-run always prints. Install the package permanently first.
62581
- init --operator USER also installs the fleet agent, a root service
62582
- on a Unix socket only USER may open, so the workspace running as
62583
- USER manages the others from Settings \u2192 Workspaces.
62584
-
62585
- --tailscale serve over your tailnet: Tailscale terminates HTTPS and the
62586
- link uses this machine's MagicDNS name (needs Tailscale signed in
62587
- and HTTPS certificates enabled for the tailnet)
62588
- --tunnel serve at a public HTTPS address through a Cloudflare tunnel
62589
- from a hosted control plane (set OMB_CONTROL_PLANE_URL; Atomic
62590
- ships none). Run \`chibi login\` once on this machine first.
62591
- --domain serve at https://HOST on your own domain: a pinned Caddy is
62592
- downloaded once and run alongside the server, and gets the
62593
- certificate itself. Point the domain's DNS at this machine and
62594
- open ports 80 and 443.
62595
-
62596
- --no-open do not open a browser window
62597
- --no-pair skip phone setup and do not print a pairing code
62598
- --local start locally this time, ignoring saved remote-access settings
62599
- --quiet skip the welcome banner (it never shows when output is piped;
62600
- NO_COLOR=1 shows it without color)
62601
-
62602
- Install once with \`npm install -g @chibitek/chibi\`, then type \`chibi\`.
62603
- Or run without a global install: \`npx @chibitek/chibi\`. Node 24+ is required.
62604
- `;
62605
62645
  function defaultIo() {
62606
62646
  return {
62607
62647
  log: (line) => console.log(line),
@@ -63106,6 +63146,7 @@ async function runServe(options, log = console.log) {
63106
63146
  console.error(`something already answers on http://127.0.0.1:${options.port}; use \`chibi pair\` against it, or --port for a second server`);
63107
63147
  return 1;
63108
63148
  }
63149
+ if (await reportBusyPort(options.port, options.dataDir === join53(homedir29(), ".atomic-app") ? "" : ` --data-dir ${options.dataDir}`)) return 1;
63109
63150
  let publicUrl = options.publicUrl;
63110
63151
  let tailscale = null;
63111
63152
  if (options.tailscale) {
@@ -63373,10 +63414,9 @@ async function runOnboardingCommand(options, io2 = defaultIo(), startServer = ru
63373
63414
  saveCliStartup2(options.dataDir, startupPreferences(launch));
63374
63415
  }
63375
63416
  if (options.command === "setup") {
63376
- io2.log("\nAll set. Start with: chibi (or npx @chibitek/chibi without a global install).");
63377
- if (options.dataDir !== join53(homedir29(), ".atomic-app") || options.port !== 8799) {
63378
- io2.log(`Use the same --data-dir (${options.dataDir}) and --port (${options.port}) options when starting.`);
63379
- }
63417
+ const flags = [options.dataDir !== join53(homedir29(), ".atomic-app") && ` --data-dir ${options.dataDir}`, options.port !== 8799 && ` --port ${options.port}`].filter(Boolean).join("");
63418
+ io2.log(`
63419
+ All set. Start with: chibi${flags ? ` start${flags}` : ""} (or npx @chibitek/chibi without a global install).`);
63380
63420
  return 0;
63381
63421
  }
63382
63422
  if (saved) io2.log("\nWelcome back. Using your saved AI connection.");
@@ -63412,6 +63452,25 @@ ${welcomeBanner(options)}
63412
63452
  `);
63413
63453
  }
63414
63454
  switch (options.command) {
63455
+ case "command":
63456
+ case "goal": {
63457
+ const text4 = options.command === "goal" ? `/goal ${options.commandText}` : options.commandText;
63458
+ const roomGoal = options.groupId ? /^\/goal(?:\s+([\s\S]*))?$/.exec(text4 ?? "") : null;
63459
+ if (roomGoal && (!roomGoal[1]?.trim() || (roomGoal[1].startsWith("--") || /^(status|pause|resume|stop|help)(?:\s|$)/.test(roomGoal[1])))) {
63460
+ console.error("Room goals accept an objective. Personal goal controls and budgets require --bot.");
63461
+ return 2;
63462
+ }
63463
+ const result = await api(options.port, `/api/${options.groupId ? "groups" : "bots"}/${encodeURIComponent(options.groupId ?? options.botId)}/messages`, {
63464
+ method: "POST",
63465
+ body: JSON.stringify({ threadId: options.threadId, text: roomGoal ? roomGoal[1] : text4, ...roomGoal ? { mode: "goal" } : {}, sendId: randomBytes4(16).toString("hex") })
63466
+ });
63467
+ if (result.status >= 400) {
63468
+ console.error(result.body.error ?? `Server returned ${result.status}`);
63469
+ return 1;
63470
+ }
63471
+ console.log(options.json ? JSON.stringify(result.body) : result.body.goalSummary ?? JSON.stringify(result.body));
63472
+ return 0;
63473
+ }
63415
63474
  case "setup":
63416
63475
  case "start":
63417
63476
  return runOnboardingCommand(options);