beehaven 0.8.16

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 (171) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +201 -0
  3. package/dist/__tests__/_cli-switch.js +105 -0
  4. package/dist/__tests__/agent-wrap-probe.js +136 -0
  5. package/dist/agent-attribution.js +192 -0
  6. package/dist/agent-identity-daemon.js +135 -0
  7. package/dist/agent-onboard.js +156 -0
  8. package/dist/agent-scope.js +79 -0
  9. package/dist/agent-sessions.js +370 -0
  10. package/dist/agent-state.js +482 -0
  11. package/dist/app-manifest.js +221 -0
  12. package/dist/app-signer.js +54 -0
  13. package/dist/app-signing.js +127 -0
  14. package/dist/audio-play.js +98 -0
  15. package/dist/avatar-protocol.js +25 -0
  16. package/dist/avatar-route.js +79 -0
  17. package/dist/bee/cloud-proxy.js +130 -0
  18. package/dist/blocked-actions.js +120 -0
  19. package/dist/build-channel.js +101 -0
  20. package/dist/buildings/daemon-workspace.js +475 -0
  21. package/dist/claude-bridge.js +310 -0
  22. package/dist/claude-credential-store.js +633 -0
  23. package/dist/clearly-sync.js +518 -0
  24. package/dist/clearly.js +616 -0
  25. package/dist/cli-state.js +334 -0
  26. package/dist/cli.js +6973 -0
  27. package/dist/commands/agent-skills.js +302 -0
  28. package/dist/commands/agent.js +1310 -0
  29. package/dist/commands/app.js +1123 -0
  30. package/dist/commands/auth.js +171 -0
  31. package/dist/commands/building-shell.js +392 -0
  32. package/dist/commands/canvas.js +932 -0
  33. package/dist/commands/claude.js +218 -0
  34. package/dist/commands/compliance.js +387 -0
  35. package/dist/commands/compose.js +507 -0
  36. package/dist/commands/composition.js +95 -0
  37. package/dist/commands/connect-session.js +409 -0
  38. package/dist/commands/conversations.js +160 -0
  39. package/dist/commands/costs.js +443 -0
  40. package/dist/commands/d1.js +253 -0
  41. package/dist/commands/daemon-cmd.js +162 -0
  42. package/dist/commands/deploy.js +164 -0
  43. package/dist/commands/design.js +617 -0
  44. package/dist/commands/dev-generate.js +125 -0
  45. package/dist/commands/dev.js +109 -0
  46. package/dist/commands/devices.js +135 -0
  47. package/dist/commands/explain-router.js +24 -0
  48. package/dist/commands/explain.js +882 -0
  49. package/dist/commands/explore.js +264 -0
  50. package/dist/commands/help.js +2418 -0
  51. package/dist/commands/init.js +408 -0
  52. package/dist/commands/install.js +129 -0
  53. package/dist/commands/library.js +229 -0
  54. package/dist/commands/marketplace.js +138 -0
  55. package/dist/commands/mcp-install.js +476 -0
  56. package/dist/commands/mention.js +257 -0
  57. package/dist/commands/omni.js +529 -0
  58. package/dist/commands/secrets.js +323 -0
  59. package/dist/commands/self-update.js +206 -0
  60. package/dist/commands/sheet.js +264 -0
  61. package/dist/commands/shell-output.js +309 -0
  62. package/dist/commands/shell.js +1048 -0
  63. package/dist/commands/shop.js +368 -0
  64. package/dist/commands/skills.js +311 -0
  65. package/dist/commands/slides.js +418 -0
  66. package/dist/commands/swarm.js +341 -0
  67. package/dist/commands/ticket.js +1301 -0
  68. package/dist/commands/tool.js +312 -0
  69. package/dist/commands/trash.js +106 -0
  70. package/dist/commands/treehouse.js +120 -0
  71. package/dist/commands/user.js +240 -0
  72. package/dist/commands/voice-tts.js +319 -0
  73. package/dist/commands/voice.js +166 -0
  74. package/dist/commands/watch-git.js +401 -0
  75. package/dist/commands/webhook.js +159 -0
  76. package/dist/commands/workspace.js +3354 -0
  77. package/dist/composition-repos.js +91 -0
  78. package/dist/daemon-avatar.js +130 -0
  79. package/dist/daemon-claude-code-auto-install.js +52 -0
  80. package/dist/daemon-client.js +363 -0
  81. package/dist/daemon-codex.js +349 -0
  82. package/dist/daemon-entry.js +101 -0
  83. package/dist/daemon-fetch-allowlist.js +194 -0
  84. package/dist/daemon-gemini.js +224 -0
  85. package/dist/daemon-mcp-clearly.js +353 -0
  86. package/dist/daemon-mcp-sandbox.js +127 -0
  87. package/dist/daemon-omni-company.js +338 -0
  88. package/dist/daemon-omni-hooks.js +375 -0
  89. package/dist/daemon-omni-identity.js +229 -0
  90. package/dist/daemon-omni-pipeline.js +251 -0
  91. package/dist/daemon-omni-policies.js +346 -0
  92. package/dist/daemon-omni-principal.js +580 -0
  93. package/dist/daemon-omni-tool-gate.js +116 -0
  94. package/dist/daemon-omni.js +1442 -0
  95. package/dist/daemon-path-transform.js +120 -0
  96. package/dist/daemon-write-sandbox.js +191 -0
  97. package/dist/daemon.js +4471 -0
  98. package/dist/dashboard.js +846 -0
  99. package/dist/dep-installer.js +286 -0
  100. package/dist/dev-mode.js +67 -0
  101. package/dist/doc-indexer.js +169 -0
  102. package/dist/e2e-crypto.js +94 -0
  103. package/dist/feature-flags.js +154 -0
  104. package/dist/file-browser.js +88 -0
  105. package/dist/first-party-hives.js +211 -0
  106. package/dist/folder-sync.js +226 -0
  107. package/dist/health.js +56 -0
  108. package/dist/ipc-auth.js +299 -0
  109. package/dist/ipc-client.js +495 -0
  110. package/dist/ipc-server.js +684 -0
  111. package/dist/keychain.js +224 -0
  112. package/dist/lib/admin-client.js +73 -0
  113. package/dist/lib/mobile-build-parser.js +156 -0
  114. package/dist/local-app-manager.js +660 -0
  115. package/dist/log-redaction.js +89 -0
  116. package/dist/logger.js +108 -0
  117. package/dist/manifest.js +143 -0
  118. package/dist/marketplace.js +793 -0
  119. package/dist/min-version-copy.js +51 -0
  120. package/dist/min-version.js +214 -0
  121. package/dist/public-surface.js +23 -0
  122. package/dist/relay-presets.js +89 -0
  123. package/dist/relay.js +2723 -0
  124. package/dist/repl.js +619 -0
  125. package/dist/setup-relay.js +363 -0
  126. package/dist/setup-service.js +536 -0
  127. package/dist/shared/bee-memory/index.js +6 -0
  128. package/dist/shared/bee-memory/types.js +682 -0
  129. package/dist/shared/claude-code/daemon-types.js +112 -0
  130. package/dist/shared/claude-code/index.js +2 -0
  131. package/dist/shared/claude-code/types.js +1 -0
  132. package/dist/shared/status/schema.js +133 -0
  133. package/dist/shell-session.js +198 -0
  134. package/dist/speak-lock.js +97 -0
  135. package/dist/spinner.js +57 -0
  136. package/dist/telemetry.js +166 -0
  137. package/dist/terminal/block-parser.js +207 -0
  138. package/dist/terminal/shell-integration.js +108 -0
  139. package/dist/transcript-tailer.js +215 -0
  140. package/dist/tts-qwen3.js +547 -0
  141. package/dist/types.js +4 -0
  142. package/dist/utm.js +38 -0
  143. package/dist/verb-surface.js +331 -0
  144. package/dist/version-utils.js +85 -0
  145. package/dist/watcher.js +207 -0
  146. package/package.json +88 -0
  147. package/python/qwen3_tts.py +230 -0
  148. package/scripts/stamp-build-channel.mjs +115 -0
  149. package/scripts/sync-installed.mjs +159 -0
  150. package/scripts/sync-skills.mjs +112 -0
  151. package/skills/README.md +24 -0
  152. package/skills/agent-doodle-space/SKILL.md +118 -0
  153. package/skills/agent-home/SKILL.md +98 -0
  154. package/skills/beehaven-cli/SKILL.md +144 -0
  155. package/skills/claude-shopper/README.md +90 -0
  156. package/skills/claude-shopper/SKILL.md +151 -0
  157. package/skills/clearly-agent/SKILL.md +183 -0
  158. package/skills/clearly-canvas/SKILL.md +222 -0
  159. package/skills/clearly-canvas/authoring.md +160 -0
  160. package/skills/clearly-canvas/communications.md +32 -0
  161. package/skills/clearly-canvas/composition-patterns.md +84 -0
  162. package/skills/clearly-canvas/example.mjs +78 -0
  163. package/skills/clearly-canvas/project-plans.md +156 -0
  164. package/skills/clearly-canvas/review.mjs +68 -0
  165. package/skills/clearly-workspace/SKILL.md +79 -0
  166. package/skills/clearly-workspace/survey.mjs +101 -0
  167. package/skills/codebase-map/SKILL.md +142 -0
  168. package/skills/design-craft/SKILL.md +300 -0
  169. package/skills/index.json +79 -0
  170. package/skills/layout-systems/SKILL.md +184 -0
  171. package/skills/pair-on-canvas/SKILL.md +92 -0
@@ -0,0 +1,341 @@
1
+ /**
2
+ * swarm / hive — the distributed "hive mesh" from the terminal.
3
+ *
4
+ * A **beehive** is a node running the daemon + hives (this Mac, a headless
5
+ * server, or a Cloudflare container). A **queen hive** orchestrates; **worker
6
+ * hives** execute. This machine registers itself automatically when the daemon
7
+ * connects (see daemon.ts `registerBeehive`) — these verbs let you SEE the
8
+ * swarm, designate roles, and route work.
9
+ *
10
+ * beehaven swarm list # every beehive: role, kind, hives, online
11
+ * beehaven swarm queen # who's orchestrating
12
+ * beehaven swarm role <queen|worker> # make THIS machine the queen (or a worker)
13
+ * beehaven swarm whoami # this machine's beehive identity
14
+ * beehaven swarm dispatch <cap> <action> [json] # route work to the best worker
15
+ * beehaven swarm forget <deviceId> # drop a stale offline node
16
+ *
17
+ * All cloud state lives in the always-on Home DO (rpc/swarm-rpcs.ts); these
18
+ * verbs reach it through the daemon over IPC via `rpcOnce`. (One namespace on
19
+ * purpose — `hive`/`hives` already name the local-apps concept.)
20
+ */
21
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
22
+ import { homedir, hostname, platform } from 'node:os';
23
+ import { join } from 'node:path';
24
+ import { rpcOnce } from '../ipc-client.js';
25
+ const dim = (s) => `\x1b[2m${s}\x1b[0m`;
26
+ const green = (s) => `\x1b[32m${s}\x1b[0m`;
27
+ const cyan = (s) => `\x1b[36m${s}\x1b[0m`;
28
+ const yellow = (s) => `\x1b[33m${s}\x1b[0m`;
29
+ const red = (s) => `\x1b[31m${s}\x1b[0m`;
30
+ const bold = (s) => `\x1b[1m${s}\x1b[0m`;
31
+ const CONFIG_DIR = join(homedir(), '.beehaven');
32
+ const CONFIG_FILE = join(CONFIG_DIR, 'config.json');
33
+ /** This machine's stable beehive id — MUST match relay.ts `deviceId`. */
34
+ function thisDeviceId() {
35
+ return `${hostname()}-${platform()}`;
36
+ }
37
+ function loadConfig() {
38
+ try {
39
+ return existsSync(CONFIG_FILE) ? JSON.parse(readFileSync(CONFIG_FILE, 'utf8')) : {};
40
+ }
41
+ catch {
42
+ return {};
43
+ }
44
+ }
45
+ function saveConfig(update) {
46
+ mkdirSync(CONFIG_DIR, { recursive: true });
47
+ writeFileSync(CONFIG_FILE, JSON.stringify({ ...loadConfig(), ...update }, null, 2));
48
+ }
49
+ /** Unwrap the daemon's { result } envelope (rpcOnce sometimes returns it raw). */
50
+ function unwrap(r) {
51
+ return r?.result ?? r ?? {};
52
+ }
53
+ // ══════════════════════════════ swarm ══════════════════════════════
54
+ export async function cmdSwarm(positionals, values) {
55
+ const sub = positionals[1] ?? 'list';
56
+ const rest = positionals.slice(2);
57
+ switch (sub) {
58
+ case 'list':
59
+ case 'ls':
60
+ return swarmList();
61
+ case 'queen':
62
+ return swarmQueen();
63
+ case 'role':
64
+ return swarmRole(rest);
65
+ case 'whoami':
66
+ case 'status':
67
+ case 'me':
68
+ return swarmWhoami();
69
+ case 'dispatch':
70
+ case 'run':
71
+ return swarmDispatch(rest, values);
72
+ case 'forget':
73
+ case 'rm':
74
+ return swarmForget(rest);
75
+ // The bees — 👑 Queen (samples all the honey) + 🐝 Omni (the owner's twin).
76
+ case 'bees':
77
+ return swarmBees();
78
+ case 'sample-honey':
79
+ case 'sample':
80
+ return swarmSampleHoney();
81
+ case 'forge-omni':
82
+ case 'forge':
83
+ return swarmForgeOmni(values);
84
+ default:
85
+ console.log(` Unknown swarm subcommand '${sub}'.`);
86
+ console.log(dim(' Try: beehaven swarm list | queen | role | whoami | dispatch | forget | bees | sample-honey | forge-omni'));
87
+ }
88
+ }
89
+ async function swarmList() {
90
+ let res;
91
+ try {
92
+ res = unwrap(await rpcOnce('swarm-list', {}, 8_000));
93
+ }
94
+ catch {
95
+ console.log(red(' Cannot reach the swarm.') + dim(' Is the daemon running? beehaven status'));
96
+ return;
97
+ }
98
+ const beehives = res?.beehives ?? [];
99
+ if (!beehives.length) {
100
+ console.log('');
101
+ console.log(' 🐝 No beehives registered yet.');
102
+ console.log(dim(' This machine registers automatically when the daemon connects.'));
103
+ console.log(dim(' Bring more online: `beehaven start --headless` on a server, or a cloud container.'));
104
+ console.log('');
105
+ return;
106
+ }
107
+ const me = thisDeviceId();
108
+ console.log('');
109
+ console.log(` 🐝 ${bold('Swarm')} ${dim(`— ${res.online}/${res.count} online`)}`);
110
+ console.log(dim(' ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
111
+ for (const b of beehives) {
112
+ const dot = b.online ? green('●') : dim('○');
113
+ const crown = b.role === 'queen' ? '👑' : '🐝';
114
+ const mine = b.deviceId === me ? cyan(' ← this machine') : '';
115
+ const caps = (b.capabilities ?? []).slice(0, 8).join(', ') || dim('no hives');
116
+ console.log(` ${dot} ${crown} ${cyan(String(b.hostname).padEnd(22))} ${dim(String(b.kind).padEnd(15))} ${(b.role === 'queen' ? yellow('queen') : 'worker').padEnd(7)}${mine}`);
117
+ console.log(` ${dim(b.deviceId)}`);
118
+ console.log(` ${dim('hives:')} ${caps}${b.load ? dim(` load:${b.load}`) : ''}`);
119
+ }
120
+ console.log('');
121
+ console.log(dim(` queen: ${res.queen ? green(res.queen) : 'none — promote one with `beehaven swarm role queen`'}`));
122
+ console.log('');
123
+ }
124
+ async function swarmQueen() {
125
+ let res;
126
+ try {
127
+ res = unwrap(await rpcOnce('swarm-queen', {}, 8_000));
128
+ }
129
+ catch {
130
+ console.log(red(' Cannot reach the swarm.'));
131
+ return;
132
+ }
133
+ const q = res?.queen;
134
+ console.log('');
135
+ if (!q) {
136
+ console.log(' 👑 No queen designated.');
137
+ console.log(dim(' Make this machine the queen: beehaven swarm role queen'));
138
+ }
139
+ else {
140
+ console.log(` 👑 ${bold('Queen')} — ${cyan(q.hostname)} ${q.online ? green('(online)') : dim('(offline)')}`);
141
+ console.log(` ${dim(q.deviceId)} · ${dim(q.kind)}`);
142
+ console.log(` ${dim('hives:')} ${(q.capabilities ?? []).join(', ') || dim('none')}`);
143
+ }
144
+ console.log('');
145
+ }
146
+ async function swarmDispatch(rest, values) {
147
+ const capability = rest[0];
148
+ const action = rest[1];
149
+ if (!capability || !action) {
150
+ console.log(' Usage: beehaven swarm dispatch <capability> <action> [json]');
151
+ console.log(dim(' capability = the hive to run (e.g. motion-studio, voice)'));
152
+ console.log(dim(' e.g. beehaven swarm dispatch voice synthesize \'{"text":"hi"}\''));
153
+ return;
154
+ }
155
+ let input = {};
156
+ if (rest[2]) {
157
+ try {
158
+ input = JSON.parse(rest[2]);
159
+ }
160
+ catch {
161
+ console.log(red(' Invalid JSON payload.'));
162
+ return;
163
+ }
164
+ }
165
+ const targetDeviceId = typeof values?.target === 'string' ? values.target : undefined;
166
+ console.log(dim(` → dispatching ${capability}:${action}${targetDeviceId ? ` @${targetDeviceId}` : ' to the best worker'}…`));
167
+ let res;
168
+ try {
169
+ res = unwrap(await rpcOnce('swarm-dispatch', { capability, action, input, targetDeviceId }, 300_000));
170
+ }
171
+ catch (e) {
172
+ console.log(red(` Dispatch failed: ${e?.message ?? e}`));
173
+ return;
174
+ }
175
+ if (res?.error || res?.ok === false) {
176
+ console.log(red(` ✗ ${res.error ?? 'dispatch failed'}`) + (res.code ? dim(` [${res.code}]`) : ''));
177
+ return;
178
+ }
179
+ console.log(green(` ✓ ${capability}:${action}`) + dim(` on ${res.targetDeviceId} · ${res.durationMs}ms`));
180
+ console.log(JSON.stringify(res.result, null, 2));
181
+ }
182
+ async function swarmForget(rest) {
183
+ const deviceId = rest[0];
184
+ if (!deviceId) {
185
+ console.log(' Usage: beehaven swarm forget <deviceId>');
186
+ return;
187
+ }
188
+ let res;
189
+ try {
190
+ res = unwrap(await rpcOnce('swarm-forget', { deviceId }, 8_000));
191
+ }
192
+ catch (e) {
193
+ console.log(red(` ${e?.message ?? e}`));
194
+ return;
195
+ }
196
+ if (res?.error) {
197
+ console.log(red(` ✗ ${res.error}`));
198
+ return;
199
+ }
200
+ console.log(green(` ✓ forgot beehive ${deviceId}`));
201
+ }
202
+ // ══════════════════════════════ the bees ══════════════════════════════
203
+ // 👑 Queen samples ALL the honey (the full corpus, gets fat); 🐝 Omni is the
204
+ // owner's AI twin, forged BY the Queen from a lean distillation of the honey.
205
+ async function swarmBees() {
206
+ let res;
207
+ try {
208
+ res = unwrap(await rpcOnce('bees-status', {}, 8_000));
209
+ }
210
+ catch {
211
+ console.log(red(' Cannot reach the hive.') + dim(' Is the daemon running?'));
212
+ return;
213
+ }
214
+ const q = res?.queen ?? {};
215
+ const o = res?.omni ?? {};
216
+ console.log('');
217
+ console.log(` 🐝 ${bold('The bees')}`);
218
+ console.log(dim(' ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
219
+ console.log(` 👑 ${cyan('Queen')} ${q.exists ? green('ready') : dim('not yet')} · honey ${q.honeySampled ? green('sampled') : yellow('not sampled')}`);
220
+ console.log(dim(` samples all the honey → gets fat → forges Omni`));
221
+ console.log(` 🐝 ${cyan('Omni')} ${o.exists ? green('ready') : dim('not yet')} · twin ${o.forged ? green('forged') : yellow('not forged')}${o.twinMemories ? dim(` (${o.twinMemories} memories)`) : ''}`);
222
+ console.log(dim(` ${o.description || 'your AI twin — a copy of you'}`));
223
+ console.log('');
224
+ if (!q.honeySampled)
225
+ console.log(dim(' Next: beehaven swarm sample-honey'));
226
+ else if (!o.forged)
227
+ console.log(dim(' Next: beehaven swarm forge-omni'));
228
+ else
229
+ console.log(dim(' Log in as your twin: beehaven agent login omni-bee'));
230
+ console.log('');
231
+ }
232
+ async function swarmSampleHoney() {
233
+ console.log(dim(' 👑 Queen is sampling all the honey…'));
234
+ let res;
235
+ try {
236
+ res = unwrap(await rpcOnce('queen-sample-honey', {}, 120_000));
237
+ }
238
+ catch (e) {
239
+ console.log(red(` Sampling failed: ${e?.message ?? e}`));
240
+ return;
241
+ }
242
+ if (res?.error) {
243
+ console.log(red(` ✗ ${res.error}`));
244
+ return;
245
+ }
246
+ const counts = res.counts ?? {};
247
+ console.log('');
248
+ console.log(green(` ✓ Queen sampled the honey`) + dim(` · fatness ${res.fatness}`));
249
+ console.log(dim(` ${Object.entries(counts)
250
+ .map(([k, n]) => `${n} ${k}`)
251
+ .join(' · ')}`));
252
+ if (res.ownerName)
253
+ console.log(dim(` owner: ${res.ownerName}`));
254
+ console.log('');
255
+ console.log(dim(' Now forge the twin: beehaven swarm forge-omni'));
256
+ console.log('');
257
+ }
258
+ async function swarmForgeOmni(values) {
259
+ console.log(dim(' 👑 Queen is forging Omni — distilling a lean copy of you…'));
260
+ let res;
261
+ try {
262
+ res = unwrap(await rpcOnce('queen-forge-omni', { resample: !!values?.force }, 180_000));
263
+ }
264
+ catch (e) {
265
+ console.log(red(` Forge failed: ${e?.message ?? e}`));
266
+ return;
267
+ }
268
+ if (res?.error) {
269
+ console.log(red(` ✗ ${res.error}`));
270
+ return;
271
+ }
272
+ const t = res.twin ?? {};
273
+ console.log('');
274
+ console.log(green(` ✓ Omni forged`) +
275
+ dim(` · twin of ${res.owner} · ${res.usedLLM ? 'LLM-distilled' : 'structured'} · ${res.factsSeeded} facts seeded`));
276
+ if (t.persona)
277
+ console.log(`\n ${bold('Who Omni is:')}\n ${dim(String(t.persona).slice(0, 300))}`);
278
+ if (t.voice)
279
+ console.log(`\n ${bold('Voice:')} ${dim(String(t.voice).slice(0, 200))}`);
280
+ if (Array.isArray(t.priorities) && t.priorities.length)
281
+ console.log(`\n ${bold('Priorities:')} ${dim(t.priorities.slice(0, 5).join(' · '))}`);
282
+ console.log('');
283
+ console.log(dim(` Log in as your twin: ${res.loginCommand || 'beehaven agent login omni-bee'}`));
284
+ console.log('');
285
+ }
286
+ // ─────────────────────── this machine's role/identity ───────────────────────
287
+ async function swarmRole(rest) {
288
+ const role = (rest[0] ?? '').toLowerCase();
289
+ if (role !== 'queen' && role !== 'worker') {
290
+ const current = loadConfig().role ?? (process.env.BEEHAVEN_HEADLESS === '1' ? 'worker' : 'queen');
291
+ console.log(' Usage: beehaven swarm role <queen|worker>');
292
+ console.log(dim(` This machine is currently: ${current === 'queen' ? yellow('👑 queen') : 'worker'}`));
293
+ return;
294
+ }
295
+ // 1) Persist locally so it survives restarts + the daemon re-registers with it.
296
+ saveConfig({ role });
297
+ // 2) Update the cloud row immediately so the swarm reflects it now (the daemon
298
+ // catches up its own role on its next register, ~2 min, or `beehaven restart`).
299
+ const deviceId = thisDeviceId();
300
+ let synced = false;
301
+ try {
302
+ const r = unwrap(await rpcOnce('hive-set-role', { deviceId, role }, 8_000));
303
+ synced = !!r?.ok;
304
+ }
305
+ catch {
306
+ /* daemon offline — local config still saved; applies on next connect */
307
+ }
308
+ console.log('');
309
+ console.log(` ${green('✓')} This machine is now the ${role === 'queen' ? yellow('👑 queen hive') : 'worker hive'}.`);
310
+ console.log(dim(synced
311
+ ? ' Synced to the swarm. It orchestrates from here.'
312
+ : ' Saved locally — applies when the daemon next connects (beehaven restart to apply now).'));
313
+ console.log('');
314
+ }
315
+ async function swarmWhoami() {
316
+ const cfg = loadConfig();
317
+ const deviceId = thisDeviceId();
318
+ const role = cfg.role ?? (process.env.BEEHAVEN_HEADLESS === '1' ? 'worker' : 'queen');
319
+ const kind = process.env.BEEHAVEN_KIND ?? (process.env.BEEHAVEN_HEADLESS === '1' ? 'headless' : 'local-mac');
320
+ console.log('');
321
+ console.log(` 🐝 ${bold('This beehive')}`);
322
+ console.log(` deviceId: ${cyan(deviceId)}`);
323
+ console.log(` role: ${role === 'queen' ? yellow('👑 queen') : 'worker'} ${dim('(beehaven swarm role <queen|worker>)')}`);
324
+ console.log(` kind: ${kind}`);
325
+ // Best-effort live view from the swarm registry.
326
+ try {
327
+ const res = unwrap(await rpcOnce('swarm-list', {}, 5_000));
328
+ const me = (res?.beehives ?? []).find((b) => b.deviceId === deviceId);
329
+ if (me) {
330
+ console.log(` online: ${me.online ? green('yes') : dim('no')}`);
331
+ console.log(` hives: ${(me.capabilities ?? []).join(', ') || dim('none')}`);
332
+ }
333
+ else {
334
+ console.log(dim(' not yet registered in the swarm — the daemon registers on connect.'));
335
+ }
336
+ }
337
+ catch {
338
+ /* daemon offline */
339
+ }
340
+ console.log('');
341
+ }