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,536 @@
1
+ // ============================================================================
2
+ // Beehaven Service Installer — launchd (macOS) / systemd (Linux)
3
+ //
4
+ // Registers the daemon as a user-level service with auto-restart on crash.
5
+ // No root/sudo required — uses ~/Library/LaunchAgents or ~/.config/systemd/user.
6
+ // ============================================================================
7
+ import { execSync } from 'child_process';
8
+ import { existsSync, mkdirSync, writeFileSync, unlinkSync, readFileSync, chmodSync } from 'fs';
9
+ import { homedir } from 'os';
10
+ import { join, dirname } from 'path';
11
+ import { fileURLToPath } from 'url';
12
+ const __dirname = dirname(fileURLToPath(import.meta.url));
13
+ const LABEL = 'sh.clearly.beehaven';
14
+ const LOG_DIR = join(homedir(), '.beehaven', 'logs');
15
+ // macOS
16
+ const LAUNCH_AGENTS_DIR = join(homedir(), 'Library', 'LaunchAgents');
17
+ const PLIST_PATH = join(LAUNCH_AGENTS_DIR, `${LABEL}.plist`);
18
+ // Linux
19
+ const SYSTEMD_USER_DIR = join(homedir(), '.config', 'systemd', 'user');
20
+ const UNIT_PATH = join(SYSTEMD_USER_DIR, 'beehaven.service');
21
+ // ── Resolve paths ──────────────────────────────────────────────────────────
22
+ function resolveNodePath() {
23
+ return process.execPath;
24
+ }
25
+ function resolveCliPath() {
26
+ // Prefer the compiled dist/cli.js adjacent to this file
27
+ const distCli = join(__dirname, '..', 'dist', 'cli.js');
28
+ if (existsSync(distCli))
29
+ return distCli;
30
+ // Fallback: adjacent cli.js (we're already in dist/)
31
+ const adjacentCli = join(__dirname, 'cli.js');
32
+ if (existsSync(adjacentCli))
33
+ return adjacentCli;
34
+ // Last resort: resolve from npm global
35
+ try {
36
+ const bin = execSync('which beehaven', { encoding: 'utf8' }).trim();
37
+ // Read the shebang to find the actual script
38
+ const content = readFileSync(bin, 'utf8');
39
+ if (content.startsWith('#!/')) {
40
+ // npm bin wrapper — the real script is this file itself
41
+ return bin;
42
+ }
43
+ return bin;
44
+ }
45
+ catch {
46
+ throw new Error('Could not resolve beehaven CLI path. Is it installed globally?');
47
+ }
48
+ }
49
+ function getCurrentPath() {
50
+ return process.env.PATH || '/usr/local/bin:/usr/bin:/bin';
51
+ }
52
+ // ── Helpers ────────────────────────────────────────────────────────────────
53
+ /** Escape strings for safe inclusion in XML/plist files */
54
+ function escapeXml(str) {
55
+ return str
56
+ .replace(/&/g, '&')
57
+ .replace(/</g, '&lt;')
58
+ .replace(/>/g, '&gt;')
59
+ .replace(/"/g, '&quot;')
60
+ .replace(/'/g, '&apos;');
61
+ }
62
+ // ── Templates ──────────────────────────────────────────────────────────────
63
+ function generatePlist(nodePath, cliPath) {
64
+ const envPath = getCurrentPath();
65
+ return `<?xml version="1.0" encoding="UTF-8"?>
66
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
67
+ <plist version="1.0">
68
+ <dict>
69
+ <key>Label</key>
70
+ <string>${escapeXml(LABEL)}</string>
71
+ <key>ProgramArguments</key>
72
+ <array>
73
+ <string>${escapeXml(nodePath)}</string>
74
+ <string>${escapeXml(cliPath)}</string>
75
+ <string>start</string>
76
+ </array>
77
+ <key>WorkingDirectory</key>
78
+ <string>${escapeXml(homedir())}</string>
79
+ <key>RunAtLoad</key>
80
+ <true/>
81
+ <key>KeepAlive</key>
82
+ <dict>
83
+ <key>SuccessfulExit</key>
84
+ <false/>
85
+ </dict>
86
+ <key>ThrottleInterval</key>
87
+ <integer>10</integer>
88
+ <key>StandardOutPath</key>
89
+ <string>${escapeXml(LOG_DIR)}/stdout.log</string>
90
+ <key>StandardErrorPath</key>
91
+ <string>${escapeXml(LOG_DIR)}/stderr.log</string>
92
+ <key>EnvironmentVariables</key>
93
+ <dict>
94
+ <key>PATH</key>
95
+ <string>${escapeXml(envPath)}</string>
96
+ <key>HOME</key>
97
+ <string>${escapeXml(homedir())}</string>
98
+ </dict>
99
+ </dict>
100
+ </plist>`;
101
+ }
102
+ function generateUnit(nodePath, cliPath) {
103
+ const envPath = getCurrentPath();
104
+ return `[Unit]
105
+ Description=Beehaven Daemon — Claude Code activity relay
106
+ After=network-online.target
107
+
108
+ [Service]
109
+ Type=simple
110
+ ExecStart="${nodePath}" "${cliPath}" start
111
+ WorkingDirectory=${homedir()}
112
+ Restart=on-failure
113
+ RestartSec=10
114
+ StandardOutput=append:${LOG_DIR}/stdout.log
115
+ StandardError=append:${LOG_DIR}/stderr.log
116
+ Environment=PATH=${envPath}
117
+ Environment=HOME=${homedir()}
118
+
119
+ [Install]
120
+ WantedBy=default.target`;
121
+ }
122
+ // ── Install ────────────────────────────────────────────────────────────────
123
+ export async function installService() {
124
+ const platform = process.platform;
125
+ console.log('');
126
+ console.log(' \uD83D\uDC1D Beehaven — Install Service');
127
+ console.log(' \u2501'.repeat(30));
128
+ console.log('');
129
+ // Resolve paths
130
+ const nodePath = resolveNodePath();
131
+ const cliPath = resolveCliPath();
132
+ console.log(` Node: ${nodePath}`);
133
+ console.log(` CLI: ${cliPath}`);
134
+ console.log('');
135
+ // Create log directory with restricted permissions
136
+ mkdirSync(LOG_DIR, { recursive: true, mode: 0o700 });
137
+ if (platform === 'darwin') {
138
+ await installLaunchd(nodePath, cliPath);
139
+ }
140
+ else if (platform === 'linux') {
141
+ await installSystemd(nodePath, cliPath);
142
+ }
143
+ else {
144
+ console.error(` Unsupported platform: ${platform}`);
145
+ console.error(' Supported: macOS (launchd), Linux (systemd)');
146
+ process.exit(1);
147
+ }
148
+ }
149
+ async function installLaunchd(nodePath, cliPath) {
150
+ // Unload existing service if present
151
+ if (existsSync(PLIST_PATH)) {
152
+ try {
153
+ execSync(`launchctl unload "${PLIST_PATH}"`, { stdio: 'ignore' });
154
+ }
155
+ catch {
156
+ /* may not be loaded */
157
+ }
158
+ }
159
+ // Write plist
160
+ mkdirSync(LAUNCH_AGENTS_DIR, { recursive: true });
161
+ const plist = generatePlist(nodePath, cliPath);
162
+ writeFileSync(PLIST_PATH, plist, { mode: 0o600 });
163
+ chmodSync(PLIST_PATH, 0o600);
164
+ console.log(` \u2713 Created ${PLIST_PATH} (mode 0600)`);
165
+ // Load service
166
+ try {
167
+ execSync(`launchctl load -w "${PLIST_PATH}"`, { stdio: 'pipe' });
168
+ console.log(' \u2713 Service loaded (launchd)');
169
+ }
170
+ catch (err) {
171
+ console.error(` \u2717 Failed to load service: ${err.message}`);
172
+ console.error(' Try: launchctl load -w ~/Library/LaunchAgents/sh.clearly.beehaven.plist');
173
+ return;
174
+ }
175
+ // Verify
176
+ await sleep(1000);
177
+ try {
178
+ const output = execSync(`launchctl list | grep ${LABEL}`, { encoding: 'utf8' });
179
+ const parts = output.trim().split(/\s+/);
180
+ const pid = parts[0];
181
+ if (pid && pid !== '-') {
182
+ console.log(` \u2713 Running (PID ${pid})`);
183
+ }
184
+ else {
185
+ console.log(' \u26A0 Service loaded but not yet running (will start on next login)');
186
+ }
187
+ }
188
+ catch {
189
+ console.log(' \u26A0 Could not verify — check: launchctl list | grep beehaven');
190
+ }
191
+ printSuccess('launchd');
192
+ }
193
+ async function installSystemd(nodePath, cliPath) {
194
+ // Write unit file
195
+ mkdirSync(SYSTEMD_USER_DIR, { recursive: true });
196
+ const unit = generateUnit(nodePath, cliPath);
197
+ writeFileSync(UNIT_PATH, unit, { mode: 0o600 });
198
+ chmodSync(UNIT_PATH, 0o600);
199
+ console.log(` \u2713 Created ${UNIT_PATH} (mode 0600)`);
200
+ // Reload + enable + start
201
+ try {
202
+ execSync('systemctl --user daemon-reload', { stdio: 'pipe' });
203
+ execSync('systemctl --user enable --now beehaven', { stdio: 'pipe' });
204
+ console.log(' \u2713 Service enabled and started (systemd)');
205
+ }
206
+ catch (err) {
207
+ console.error(` \u2717 Failed to enable service: ${err.message}`);
208
+ console.error(' Try: systemctl --user enable --now beehaven');
209
+ return;
210
+ }
211
+ // Verify
212
+ await sleep(1000);
213
+ try {
214
+ const output = execSync('systemctl --user is-active beehaven', { encoding: 'utf8' }).trim();
215
+ if (output === 'active') {
216
+ console.log(' \u2713 Running');
217
+ }
218
+ else {
219
+ console.log(` \u26A0 Status: ${output}`);
220
+ }
221
+ }
222
+ catch {
223
+ console.log(' \u26A0 Could not verify — check: systemctl --user status beehaven');
224
+ }
225
+ printSuccess('systemd');
226
+ }
227
+ function printSuccess(backend) {
228
+ console.log('');
229
+ console.log(' Service installed. Beehaven will:');
230
+ console.log(' • Start automatically on login');
231
+ console.log(' • Restart automatically on crash (10s delay)');
232
+ console.log(' • Stay stopped on clean shutdown');
233
+ console.log('');
234
+ console.log(` Logs: ${LOG_DIR}/`);
235
+ console.log('');
236
+ if (backend === 'launchd') {
237
+ console.log(' Manage: use the Clearly Dev Mac app tray.');
238
+ console.log(' Daemon lifecycle (start/stop/uninstall) is tray-only.');
239
+ console.log(' Diagnostic: launchctl list | grep beehaven');
240
+ }
241
+ else {
242
+ console.log(' Manage:');
243
+ console.log(' systemctl --user stop beehaven # Stop');
244
+ console.log(' systemctl --user start beehaven # Start');
245
+ console.log(' systemctl --user status beehaven # Status');
246
+ }
247
+ console.log('');
248
+ }
249
+ // ── Uninstall ──────────────────────────────────────────────────────────────
250
+ export async function uninstallService() {
251
+ const platform = process.platform;
252
+ if (platform === 'darwin') {
253
+ await uninstallLaunchd();
254
+ }
255
+ else if (platform === 'linux') {
256
+ await uninstallSystemd();
257
+ }
258
+ else {
259
+ console.log(' No service to uninstall (unsupported platform)');
260
+ }
261
+ }
262
+ async function uninstallLaunchd() {
263
+ if (!existsSync(PLIST_PATH)) {
264
+ console.log(' No launchd service installed');
265
+ return;
266
+ }
267
+ try {
268
+ execSync(`launchctl unload "${PLIST_PATH}"`, { stdio: 'ignore' });
269
+ }
270
+ catch {
271
+ /* may not be loaded */
272
+ }
273
+ unlinkSync(PLIST_PATH);
274
+ console.log(' \u2713 Removed launchd service');
275
+ }
276
+ async function uninstallSystemd() {
277
+ if (!existsSync(UNIT_PATH)) {
278
+ console.log(' No systemd service installed');
279
+ return;
280
+ }
281
+ try {
282
+ execSync('systemctl --user disable --now beehaven', { stdio: 'ignore' });
283
+ }
284
+ catch {
285
+ /* may not be enabled */
286
+ }
287
+ unlinkSync(UNIT_PATH);
288
+ try {
289
+ execSync('systemctl --user daemon-reload', { stdio: 'ignore' });
290
+ }
291
+ catch {
292
+ /* best effort */
293
+ }
294
+ console.log(' \u2713 Removed systemd service');
295
+ }
296
+ // ── Check if service is installed ──────────────────────────────────────────
297
+ export function isServiceInstalled() {
298
+ if (process.platform === 'darwin')
299
+ return existsSync(PLIST_PATH);
300
+ if (process.platform === 'linux')
301
+ return existsSync(UNIT_PATH);
302
+ return false;
303
+ }
304
+ // ── Start / Stop (without install/uninstall) ──────────────────────────────
305
+ /** Start the service if installed. Returns true if started via service manager. */
306
+ export function startService() {
307
+ if (process.platform === 'darwin' && existsSync(PLIST_PATH)) {
308
+ try {
309
+ execSync(`launchctl load -w "${PLIST_PATH}"`, { stdio: 'pipe' });
310
+ console.log(' Service started (launchd).');
311
+ return true;
312
+ }
313
+ catch {
314
+ // May already be loaded — try kickstart
315
+ try {
316
+ execSync(`launchctl start ${LABEL}`, { stdio: 'pipe' });
317
+ console.log(' Service started (launchd).');
318
+ return true;
319
+ }
320
+ catch { }
321
+ }
322
+ }
323
+ if (process.platform === 'linux' && existsSync(UNIT_PATH)) {
324
+ try {
325
+ execSync('systemctl --user start beehaven', { stdio: 'pipe' });
326
+ console.log(' Service started (systemd).');
327
+ return true;
328
+ }
329
+ catch { }
330
+ }
331
+ return false;
332
+ }
333
+ /** Stop the service if installed. Returns true if stopped via service manager. */
334
+ export function stopService() {
335
+ if (process.platform === 'darwin' && existsSync(PLIST_PATH)) {
336
+ try {
337
+ execSync(`launchctl unload "${PLIST_PATH}"`, { stdio: 'pipe' });
338
+ console.log(' Service stopped (launchd unloaded — will not auto-restart).');
339
+ return true;
340
+ }
341
+ catch { }
342
+ }
343
+ if (process.platform === 'linux' && existsSync(UNIT_PATH)) {
344
+ try {
345
+ execSync('systemctl --user stop beehaven', { stdio: 'pipe' });
346
+ console.log(' Service stopped (systemd).');
347
+ return true;
348
+ }
349
+ catch { }
350
+ }
351
+ return false;
352
+ }
353
+ // ── Full uninstall (for Mac app uninstall hook) ───────────────────────────
354
+ //
355
+ // Single entry point the Clearly Dev Mac app invokes when the user removes
356
+ // the app. Tears down the LaunchAgent, revokes the server session, purges
357
+ // all local state, and removes the socket + event log. Safe to call
358
+ // repeatedly — every step swallows its own errors.
359
+ //
360
+ // The CLI no longer exposes this as a `beehaven uninstall` command by
361
+ // design; daemon lifecycle is owned by the tray. The Mac app imports this
362
+ // function directly and calls it from its own teardown routine.
363
+ export async function cleanupAll() {
364
+ const steps = [];
365
+ const errors = [];
366
+ // 1. LaunchAgent / systemd unit
367
+ try {
368
+ await uninstallService();
369
+ steps.push('service-unregistered');
370
+ }
371
+ catch (err) {
372
+ errors.push(`service-unregister: ${err?.message ?? err}`);
373
+ }
374
+ // 2. Revoke server session + clear keychain token
375
+ try {
376
+ const { Relay } = await import('./relay.js');
377
+ const r = new Relay();
378
+ await r.deleteSession();
379
+ steps.push('session-deleted');
380
+ }
381
+ catch (err) {
382
+ errors.push(`session-delete: ${err?.message ?? err}`);
383
+ }
384
+ try {
385
+ const { logout } = await import('./setup-relay.js');
386
+ logout();
387
+ steps.push('logged-out');
388
+ }
389
+ catch (err) {
390
+ errors.push(`logout: ${err?.message ?? err}`);
391
+ }
392
+ // 3. Socket + event log
393
+ const { existsSync, unlinkSync, readdirSync, rmSync, readFileSync, writeFileSync } = await import('node:fs');
394
+ const { join } = await import('node:path');
395
+ for (const p of ['/tmp/beehaven.sock', '/tmp/beehaven-ipc.sock', '/tmp/beehaven-events.jsonl']) {
396
+ try {
397
+ if (existsSync(p)) {
398
+ unlinkSync(p);
399
+ steps.push(`removed:${p}`);
400
+ }
401
+ }
402
+ catch { }
403
+ }
404
+ try {
405
+ for (const f of readdirSync('/tmp')) {
406
+ if (f.startsWith('beehaven-events.jsonl.') && f.endsWith('.old')) {
407
+ try {
408
+ unlinkSync(join('/tmp', f));
409
+ }
410
+ catch { }
411
+ }
412
+ }
413
+ }
414
+ catch { }
415
+ // 4. ~/.beehaven/ (config, sessions, cache, apps, logs)
416
+ const configDir = join(homedir(), '.beehaven');
417
+ try {
418
+ if (existsSync(configDir)) {
419
+ rmSync(configDir, { recursive: true, force: true });
420
+ steps.push('config-dir-removed');
421
+ }
422
+ }
423
+ catch (err) {
424
+ errors.push(`config-dir: ${err?.message ?? err}`);
425
+ }
426
+ /**
427
+ * 5–6. The `~/.local/bin` wrappers the Mac app writes.
428
+ *
429
+ * ⚠⚠ ONLY OURS. These two steps used to `unlinkSync` unconditionally, and that is not a
430
+ * theoretical hazard — it was observed deleting a developer's own `beehaven`: `cli-installer`'s
431
+ * `uninstallBeehavenCli` inspected the file, correctly reported *"wrapper is not managed by this
432
+ * app"*, declined — and then this ran and removed it anyway. Two uninstallers with opposite
433
+ * policies on the same path, and the destructive one went second.
434
+ *
435
+ * ⚠ THE HEADER IS THE OWNERSHIP TEST, and it is the same one the installer uses in the other
436
+ * direction: it refuses to overwrite a wrapper it did not write. An uninstall has to honour the
437
+ * same boundary or the pair is incoherent — you can be unable to install over a file you are
438
+ * nonetheless willing to delete.
439
+ */
440
+ const WRAPPER_MAGIC = '# managed-by: clearly-mac-app';
441
+ const removeOurWrapper = (name) => {
442
+ try {
443
+ const wrapper = join(homedir(), '.local', 'bin', name);
444
+ if (!existsSync(wrapper))
445
+ return;
446
+ if (!readFileSync(wrapper, 'utf8').includes(WRAPPER_MAGIC)) {
447
+ steps.push(`kept:~/.local/bin/${name} (not ours)`);
448
+ return;
449
+ }
450
+ unlinkSync(wrapper);
451
+ steps.push(`removed:~/.local/bin/${name}`);
452
+ }
453
+ catch { }
454
+ };
455
+ removeOurWrapper('beehaven');
456
+ // 6. mwah CLI wrappers — the Mac app installs these under CLEARLY_AVATARS
457
+ // (cleanupAll only handled `beehaven` before, so they were orphaned).
458
+ // ⚠ Same ownership test as above; see the note there.
459
+ for (const name of ['mwah', 'mwah-say'])
460
+ removeOurWrapper(name);
461
+ // 7. Claude Code integration — the avatar/mwah hooks, skills, commands, and
462
+ // caches the Mac app installs (installClaudeAssets), plus the hook entries
463
+ // in ~/.claude/settings.json. Leaves ~/.mwah/pets (the user's generated art).
464
+ const claudeDir = join(homedir(), '.claude');
465
+ for (const rel of [
466
+ 'hooks/mwah-hook', 'hooks/mwah-narrate', 'hooks/mwah-stream-bubble', 'hooks/mwah-stream-watcher',
467
+ 'skills/mwah-avatar', 'skills/mwah-pet', 'commands/mwah.md',
468
+ ]) {
469
+ try {
470
+ const full = join(claudeDir, rel);
471
+ if (existsSync(full)) {
472
+ rmSync(full, { recursive: true, force: true });
473
+ steps.push(`removed:~/.claude/${rel}`);
474
+ }
475
+ }
476
+ catch { }
477
+ }
478
+ for (const cache of ['mwah-scene', 'mwah-stream', 'mwah-narrate']) {
479
+ try {
480
+ const full = join(homedir(), '.cache', cache);
481
+ if (existsSync(full))
482
+ rmSync(full, { recursive: true, force: true });
483
+ }
484
+ catch { }
485
+ }
486
+ try {
487
+ const settingsPath = join(claudeDir, 'settings.json');
488
+ if (existsSync(settingsPath)) {
489
+ const cfg = JSON.parse(readFileSync(settingsPath, 'utf8'));
490
+ let removed = 0;
491
+ if (cfg && cfg.hooks && typeof cfg.hooks === 'object') {
492
+ for (const ev of Object.keys(cfg.hooks)) {
493
+ const list = cfg.hooks[ev];
494
+ if (!Array.isArray(list))
495
+ continue;
496
+ const kept = list.filter((entry) => !((entry.hooks || []).some((h) => typeof h.command === 'string' && /mwah|spawn-pet/i.test(h.command))));
497
+ removed += list.length - kept.length;
498
+ if (kept.length)
499
+ cfg.hooks[ev] = kept;
500
+ else
501
+ delete cfg.hooks[ev];
502
+ }
503
+ if (removed) {
504
+ writeFileSync(settingsPath, JSON.stringify(cfg, null, 2) + '\n');
505
+ steps.push(`stripped ${removed} mwah hook(s) from settings.json`);
506
+ }
507
+ }
508
+ }
509
+ }
510
+ catch (err) {
511
+ errors.push(`claude-hooks: ${err?.message ?? err}`);
512
+ }
513
+ // 8. On-device voice model caches OUTSIDE ~/.beehaven — the Parakeet-MLX weights
514
+ // live in the HuggingFace hub cache, so removing ~/.beehaven alone leaves them
515
+ // behind. (The venv + Kokoro model live inside ~/.beehaven, already removed.)
516
+ try {
517
+ const hubDir = join(homedir(), '.cache', 'huggingface', 'hub');
518
+ if (existsSync(hubDir)) {
519
+ for (const d of readdirSync(hubDir)) {
520
+ if (/^models--(mlx-community|nvidia)--parakeet/i.test(d)) {
521
+ try {
522
+ rmSync(join(hubDir, d), { recursive: true, force: true });
523
+ steps.push(`removed:hf-cache/${d}`);
524
+ }
525
+ catch { }
526
+ }
527
+ }
528
+ }
529
+ }
530
+ catch { }
531
+ return { steps, errors };
532
+ }
533
+ // ── Utilities ──────────────────────────────────────────────────────────────
534
+ function sleep(ms) {
535
+ return new Promise((resolve) => setTimeout(resolve, ms));
536
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Bee Memory Module
3
+ *
4
+ * Exports all bee memory types and utilities.
5
+ */
6
+ export * from './types.js';