hyperclaw 5.3.3 → 5.3.45

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 (70) hide show
  1. package/dist/banner-3jmKGq6t.js +143 -0
  2. package/dist/banner-BhOid_rp.js +7 -0
  3. package/dist/banner-C5D09Afq.js +7 -0
  4. package/dist/banner-CP4LeLsu.js +143 -0
  5. package/dist/banner-D14weGVJ.js +7 -0
  6. package/dist/banner-DG3v43xA.js +143 -0
  7. package/dist/banner-mu7p6UkV.js +143 -0
  8. package/dist/banner-nRaqjrSN.js +7 -0
  9. package/dist/chat-BTJoZxtU.js +523 -0
  10. package/dist/chat-BYqxftlU.js +523 -0
  11. package/dist/chat-D-i8bqTf.js +523 -0
  12. package/dist/daemon-BXfbyPBf.js +7 -0
  13. package/dist/daemon-BtQqCfEH.js +7 -0
  14. package/dist/daemon-C4j0wqxz.js +421 -0
  15. package/dist/daemon-CUNaq2Ls.js +421 -0
  16. package/dist/daemon-IU5UIMJo.js +7 -0
  17. package/dist/daemon-WOoAWS-0.js +421 -0
  18. package/dist/engine-2Tc-z0e4.js +7 -0
  19. package/dist/engine-C88DJeLD.js +327 -0
  20. package/dist/engine-D9BvbJxu.js +7 -0
  21. package/dist/engine-DQ3hT5JD.js +327 -0
  22. package/dist/hyperclawbot-DC9nGl99.js +516 -0
  23. package/dist/hyperclawbot-DcQt62PL.js +516 -0
  24. package/dist/mcp-loader-Bd7MNj5-.js +93 -0
  25. package/dist/mcp-loader-CPlsbcja.js +93 -0
  26. package/dist/onboard-BF29mNbC.js +14 -0
  27. package/dist/onboard-BRN9NLFk.js +14 -0
  28. package/dist/onboard-C7vJegQc.js +3812 -0
  29. package/dist/onboard-CJKxKPBH.js +3812 -0
  30. package/dist/onboard-Cxjlal2t.js +14 -0
  31. package/dist/onboard-CytL65jh.js +14 -0
  32. package/dist/onboard-i20xz3_O.js +3812 -0
  33. package/dist/onboard-x4I788gj.js +3812 -0
  34. package/dist/orchestrator-B9gd0u_r.js +6 -0
  35. package/dist/orchestrator-CfswFK1w.js +189 -0
  36. package/dist/orchestrator-DcEHTInR.js +6 -0
  37. package/dist/orchestrator-PQY07fH7.js +189 -0
  38. package/dist/osint-BXu9NVcF.js +283 -0
  39. package/dist/osint-D3OoMs9I.js +283 -0
  40. package/dist/osint-DsUwGzgP.js +283 -0
  41. package/dist/osint-DtRd_A_O.js +283 -0
  42. package/dist/osint-chat-BJfbgXwk.js +789 -0
  43. package/dist/osint-chat-CY2ODmY-.js +789 -0
  44. package/dist/osint-chat-CfexqWop.js +789 -0
  45. package/dist/osint-chat-DdclrVa6.js +789 -0
  46. package/dist/run-main.js +198 -50
  47. package/dist/server-C9f6qtEW.js +1366 -0
  48. package/dist/server-CSu5UPl3.js +4 -0
  49. package/dist/server-CdlilrcS.js +4 -0
  50. package/dist/server-DfrmKeE6.js +1356 -0
  51. package/dist/server-jkSNOitC.js +1366 -0
  52. package/dist/server-rHap51o6.js +4 -0
  53. package/dist/skill-runtime-249xckq3.js +5 -0
  54. package/dist/skill-runtime-Bv59FUVu.js +104 -0
  55. package/dist/skill-runtime-CdR3YMFp.js +5 -0
  56. package/dist/skill-runtime-CxL0PxyP.js +104 -0
  57. package/dist/src-CboISwpv.js +63 -0
  58. package/dist/src-ClYgvXAj.js +63 -0
  59. package/dist/src-DiYkDVnn.js +458 -0
  60. package/dist/src-PutqGezt.js +458 -0
  61. package/dist/sub-agent-tools-8-Imp1B5.js +39 -0
  62. package/dist/sub-agent-tools-DNbBE93o.js +39 -0
  63. package/dist/update-check-B-wd-tpa.js +7 -0
  64. package/dist/update-check-DlUFL81n.js +118 -0
  65. package/package.json +2 -2
  66. package/static/chat.html +764 -764
  67. package/static/web/assets/index-BeAuHLb2.js +75 -0
  68. package/static/web/assets/index-D2RfO0dG.css +1 -0
  69. package/static/web/icon.png +0 -0
  70. package/static/web/index.html +14 -0
@@ -0,0 +1,3812 @@
1
+ const require_chunk = require('./chunk-jS-bbMI5.js');
2
+ const require_paths = require('./paths-AIyBxIzm.js');
3
+ const require_paths$1 = require('./paths-DPovhojT.js');
4
+ const require_config = require('./config-DGAAJ49A.js');
5
+ const require_daemon = require('./daemon-C4j0wqxz.js');
6
+ const require_gateway = require('./gateway-CzI8dnlS.js');
7
+ const require_providers = require('./providers-DP8T0QCR.js');
8
+ const require_theme = require('./theme-CLXvI6Hr.js');
9
+ const require_banner = require('./banner-DG3v43xA.js');
10
+ const chalk = require_chunk.__toESM(require("chalk"));
11
+ const inquirer = require_chunk.__toESM(require("inquirer"));
12
+ const ora = require_chunk.__toESM(require("ora"));
13
+ const boxen = require_chunk.__toESM(require("boxen"));
14
+ const fs_extra = require_chunk.__toESM(require("fs-extra"));
15
+ const path = require_chunk.__toESM(require("path"));
16
+ const os = require_chunk.__toESM(require("os"));
17
+ const crypto = require_chunk.__toESM(require("crypto"));
18
+ const https = require_chunk.__toESM(require("https"));
19
+
20
+ //#region src/cli/channels.ts
21
+ const CHANNEL_DEFS = [
22
+ {
23
+ id: "telegram",
24
+ name: "Telegram",
25
+ emoji: "✈️",
26
+ requiresGateway: false,
27
+ supportsDM: true,
28
+ platforms: ["all"],
29
+ tokenLabel: "Telegram Bot Token",
30
+ tokenHint: "Get from @BotFather → /newbot",
31
+ setupSteps: [
32
+ "1. Open Telegram → @BotFather → /newbot",
33
+ "2. Set name and username (must end in bot)",
34
+ "3. Copy the Bot Token",
35
+ " 🔗 t.me/BotFather"
36
+ ],
37
+ npmPackage: "node-telegram-bot-api",
38
+ defaultDMPolicy: "pairing"
39
+ },
40
+ {
41
+ id: "discord",
42
+ name: "Discord",
43
+ emoji: "🎮",
44
+ requiresGateway: false,
45
+ supportsDM: true,
46
+ platforms: ["all"],
47
+ tokenLabel: "Discord Bot Token",
48
+ tokenHint: "discord.com/developers/applications",
49
+ setupSteps: [
50
+ "1. discord.com/developers/applications → New Application",
51
+ "2. Bot → Add Bot → Reset Token",
52
+ "3. OAuth2 → General → copy Application ID",
53
+ " 🔗 discord.com/developers/applications"
54
+ ],
55
+ extraFields: [{
56
+ name: "clientId",
57
+ label: "Client ID",
58
+ required: true
59
+ }],
60
+ npmPackage: "discord.js",
61
+ defaultDMPolicy: "pairing"
62
+ },
63
+ {
64
+ id: "whatsapp",
65
+ name: "WhatsApp",
66
+ emoji: "📱",
67
+ requiresGateway: true,
68
+ supportsDM: true,
69
+ platforms: ["all"],
70
+ tokenLabel: "WhatsApp Business API key",
71
+ tokenHint: "business.whatsapp.com",
72
+ setupSteps: [
73
+ "1. developers.facebook.com → My Apps → Create App",
74
+ "2. WhatsApp → API Setup → Access Token",
75
+ " 🔗 business.whatsapp.com"
76
+ ],
77
+ npmPackage: "@whiskeysockets/baileys",
78
+ defaultDMPolicy: "pairing"
79
+ },
80
+ {
81
+ id: "slack",
82
+ name: "Slack",
83
+ emoji: "💼",
84
+ requiresGateway: true,
85
+ supportsDM: true,
86
+ platforms: ["all"],
87
+ tokenLabel: "Slack Bot Token (xoxb-...)",
88
+ tokenHint: "api.slack.com/apps",
89
+ setupSteps: [
90
+ "1. api.slack.com/apps → Create App → Bot",
91
+ "2. Install App → copy Bot Token (xoxb-)",
92
+ "3. Basic Information → Signing Secret",
93
+ " 🔗 api.slack.com/apps"
94
+ ],
95
+ extraFields: [{
96
+ name: "signingSecret",
97
+ label: "Signing Secret",
98
+ required: true
99
+ }],
100
+ defaultDMPolicy: "allowlist"
101
+ },
102
+ {
103
+ id: "signal",
104
+ name: "Signal",
105
+ emoji: "🔒",
106
+ requiresGateway: true,
107
+ supportsDM: true,
108
+ platforms: ["linux", "darwin"],
109
+ tokenLabel: "Registered phone number",
110
+ tokenHint: "Requires signal-cli installed",
111
+ setupSteps: [
112
+ "1. Install signal-cli or signald",
113
+ "2. Link your number (signal-cli link or signald register)",
114
+ " 🔗 github.com/AsamK/signal-cli"
115
+ ],
116
+ notes: "Needs signal-cli + registered number",
117
+ defaultDMPolicy: "pairing"
118
+ },
119
+ {
120
+ id: "imessage",
121
+ name: "iMessage",
122
+ emoji: "🍏",
123
+ requiresGateway: true,
124
+ supportsDM: true,
125
+ platforms: ["darwin"],
126
+ tokenLabel: "BlueBubbles server password",
127
+ tokenHint: "bluebubbles.app on macOS",
128
+ setupSteps: [
129
+ "1. Install BlueBubbles on a Mac",
130
+ "2. Configure and connect",
131
+ " 🔗 bluebubbles.app"
132
+ ],
133
+ notes: "macOS only via BlueBubbles",
134
+ defaultDMPolicy: "pairing"
135
+ },
136
+ {
137
+ id: "imessage-native",
138
+ name: "iMessage (imsg)",
139
+ emoji: "💬",
140
+ requiresGateway: true,
141
+ supportsDM: true,
142
+ platforms: ["darwin"],
143
+ tokenLabel: "Not required",
144
+ tokenHint: "Uses imsg CLI",
145
+ setupSteps: [
146
+ "1. Install imsg CLI: brew install steipete/imsg/imsg",
147
+ "2. No token required — uses native iMessage",
148
+ " 🔗 github.com/steipete/imsg"
149
+ ],
150
+ notes: "macOS only, native via imsg CLI (github.com/steipete/imsg). No BlueBubbles.",
151
+ defaultDMPolicy: "pairing"
152
+ },
153
+ {
154
+ id: "matrix",
155
+ name: "Matrix",
156
+ emoji: "🔢",
157
+ requiresGateway: true,
158
+ supportsDM: true,
159
+ platforms: ["all"],
160
+ tokenLabel: "Matrix access token",
161
+ tokenHint: "element.io → Settings → Help → Access Token",
162
+ setupSteps: [
163
+ "1. Element → Settings → Help & About → Access Token",
164
+ "2. Copy the access token",
165
+ " 🔗 element.io"
166
+ ],
167
+ extraFields: [{
168
+ name: "homeserver",
169
+ label: "Homeserver URL",
170
+ hint: "https://matrix.org",
171
+ required: true
172
+ }, {
173
+ name: "userId",
174
+ label: "User ID (@user:server)",
175
+ required: true
176
+ }],
177
+ defaultDMPolicy: "pairing"
178
+ },
179
+ {
180
+ id: "email",
181
+ name: "Email",
182
+ emoji: "📧",
183
+ requiresGateway: false,
184
+ supportsDM: true,
185
+ platforms: ["all"],
186
+ tokenLabel: "Gmail app password or IMAP password",
187
+ tokenHint: "Use app-specific password",
188
+ setupSteps: [
189
+ "1. Enable IMAP/SMTP in your email provider",
190
+ "2. Use app password if you have 2FA",
191
+ " 🔗 Gmail: myaccount.google.com/apppasswords"
192
+ ],
193
+ extraFields: [
194
+ {
195
+ name: "imapHost",
196
+ label: "IMAP host",
197
+ hint: "imap.gmail.com",
198
+ required: true
199
+ },
200
+ {
201
+ name: "smtpHost",
202
+ label: "SMTP host",
203
+ hint: "smtp.gmail.com",
204
+ required: true
205
+ },
206
+ {
207
+ name: "user",
208
+ label: "Email address",
209
+ required: true
210
+ }
211
+ ],
212
+ defaultDMPolicy: "allowlist"
213
+ },
214
+ {
215
+ id: "feishu",
216
+ name: "Feishu/Lark",
217
+ emoji: "🪶",
218
+ requiresGateway: true,
219
+ supportsDM: true,
220
+ platforms: ["all"],
221
+ tokenLabel: "Feishu App ID",
222
+ tokenHint: "open.feishu.cn",
223
+ setupSteps: [
224
+ "1. open.feishu.cn → Create application",
225
+ "2. Copy App ID and App Secret",
226
+ " 🔗 open.feishu.cn"
227
+ ],
228
+ extraFields: [{
229
+ name: "appSecret",
230
+ label: "App Secret",
231
+ required: true
232
+ }],
233
+ defaultDMPolicy: "pairing"
234
+ },
235
+ {
236
+ id: "msteams",
237
+ name: "MS Teams",
238
+ emoji: "🟦",
239
+ requiresGateway: true,
240
+ supportsDM: true,
241
+ platforms: ["all"],
242
+ tokenLabel: "Azure Bot App ID",
243
+ tokenHint: "portal.azure.com → Bot Services",
244
+ setupSteps: [
245
+ "1. dev.botframework.com → Register Bot",
246
+ "2. Azure Bot → Configuration → copy App ID & Secret",
247
+ " 🔗 dev.botframework.com"
248
+ ],
249
+ extraFields: [{
250
+ name: "appPassword",
251
+ label: "App Password",
252
+ required: true
253
+ }],
254
+ defaultDMPolicy: "allowlist"
255
+ },
256
+ {
257
+ id: "messenger",
258
+ name: "Messenger",
259
+ emoji: "💬",
260
+ requiresGateway: true,
261
+ supportsDM: true,
262
+ platforms: ["all"],
263
+ tokenLabel: "Page Access Token",
264
+ tokenHint: "developers.facebook.com",
265
+ setupSteps: [
266
+ "1. developers.facebook.com → My Apps → Create App",
267
+ "2. Add Messenger product → Page Access Token",
268
+ " 🔗 developers.facebook.com"
269
+ ],
270
+ extraFields: [{
271
+ name: "verifyToken",
272
+ label: "Webhook Verify Token",
273
+ required: true
274
+ }],
275
+ defaultDMPolicy: "pairing"
276
+ },
277
+ {
278
+ id: "nostr",
279
+ name: "Nostr",
280
+ emoji: "🌐",
281
+ requiresGateway: false,
282
+ supportsDM: true,
283
+ platforms: ["all"],
284
+ tokenLabel: "Nostr private key (hex or nsec)",
285
+ tokenHint: "Generate with: openssl rand -hex 32",
286
+ setupSteps: [
287
+ "1. Use existing nostr client or generate new keypair",
288
+ "2. Copy nsec (private key)",
289
+ " 🔗 nostr.com"
290
+ ],
291
+ notes: "Decentralized — no account needed",
292
+ defaultDMPolicy: "open"
293
+ },
294
+ {
295
+ id: "line",
296
+ name: "LINE",
297
+ emoji: "💚",
298
+ requiresGateway: true,
299
+ supportsDM: true,
300
+ platforms: ["all"],
301
+ tokenLabel: "LINE Channel Access Token",
302
+ tokenHint: "developers.line.biz",
303
+ setupSteps: [
304
+ "1. developers.line.biz → Add Messaging API channel",
305
+ "2. Copy Channel Secret & Access Token",
306
+ " 🔗 developers.line.biz"
307
+ ],
308
+ extraFields: [{
309
+ name: "channelSecret",
310
+ label: "Channel Secret",
311
+ required: true
312
+ }],
313
+ defaultDMPolicy: "pairing"
314
+ },
315
+ {
316
+ id: "viber",
317
+ name: "Viber",
318
+ emoji: "💜",
319
+ requiresGateway: true,
320
+ supportsDM: true,
321
+ platforms: ["all"],
322
+ tokenLabel: "Viber Auth Token",
323
+ tokenHint: "partners.viber.com",
324
+ setupSteps: [
325
+ "1. partners.viber.com → Create Bot",
326
+ "2. Copy the Auth Token",
327
+ " 🔗 partners.viber.com"
328
+ ],
329
+ defaultDMPolicy: "pairing"
330
+ },
331
+ {
332
+ id: "zalo",
333
+ name: "Zalo OA",
334
+ emoji: "🔵",
335
+ requiresGateway: true,
336
+ supportsDM: true,
337
+ platforms: ["all"],
338
+ tokenLabel: "Zalo OA Access Token",
339
+ tokenHint: "developers.zalo.me",
340
+ setupSteps: [
341
+ "1. developers.zalo.me → Create application",
342
+ "2. Copy App ID and Access Token",
343
+ " 🔗 developers.zalo.me"
344
+ ],
345
+ extraFields: [{
346
+ name: "secretKey",
347
+ label: "Secret Key",
348
+ required: true
349
+ }],
350
+ defaultDMPolicy: "pairing"
351
+ },
352
+ {
353
+ id: "twitter",
354
+ name: "Twitter/X DM",
355
+ emoji: "🐦",
356
+ requiresGateway: true,
357
+ supportsDM: true,
358
+ platforms: ["all"],
359
+ tokenLabel: "Twitter API Key",
360
+ tokenHint: "developer.twitter.com",
361
+ setupSteps: [
362
+ "1. developer.twitter.com → Developer Portal",
363
+ "2. Create Project & App → copy API keys",
364
+ " 🔗 developer.twitter.com"
365
+ ],
366
+ extraFields: [
367
+ {
368
+ name: "apiKeySecret",
369
+ label: "API Key Secret",
370
+ required: true
371
+ },
372
+ {
373
+ name: "accessToken",
374
+ label: "Access Token",
375
+ required: true
376
+ },
377
+ {
378
+ name: "accessTokenSecret",
379
+ label: "Access Token Secret",
380
+ required: true
381
+ }
382
+ ],
383
+ defaultDMPolicy: "allowlist"
384
+ },
385
+ {
386
+ id: "irc",
387
+ name: "IRC",
388
+ emoji: "📡",
389
+ requiresGateway: true,
390
+ supportsDM: true,
391
+ platforms: ["all"],
392
+ tokenLabel: "NickServ password (optional)",
393
+ tokenHint: "freenode, libera.chat, etc.",
394
+ setupSteps: [
395
+ "1. Choose IRC server (e.g. irc.libera.chat)",
396
+ "2. Configure nick and password if needed",
397
+ " 🔗 libera.chat"
398
+ ],
399
+ extraFields: [{
400
+ name: "server",
401
+ label: "IRC server",
402
+ hint: "irc.libera.chat",
403
+ required: true
404
+ }, {
405
+ name: "nick",
406
+ label: "Nickname",
407
+ required: true
408
+ }],
409
+ defaultDMPolicy: "allowlist"
410
+ },
411
+ {
412
+ id: "mattermost",
413
+ name: "Mattermost",
414
+ emoji: "🏗️",
415
+ requiresGateway: true,
416
+ supportsDM: true,
417
+ platforms: ["all"],
418
+ tokenLabel: "Mattermost bot token",
419
+ tokenHint: "Settings → Integrations → Bot Accounts",
420
+ setupSteps: [
421
+ "1. Mattermost → Account Settings → Security → Personal Access Tokens",
422
+ "2. Create token and copy it",
423
+ " 🔗 docs.mattermost.com"
424
+ ],
425
+ extraFields: [{
426
+ name: "serverUrl",
427
+ label: "Server URL",
428
+ required: true
429
+ }],
430
+ defaultDMPolicy: "allowlist"
431
+ },
432
+ {
433
+ id: "nextcloud",
434
+ name: "Nextcloud Talk",
435
+ emoji: "☁️",
436
+ requiresGateway: true,
437
+ supportsDM: true,
438
+ platforms: ["all"],
439
+ tokenLabel: "Nextcloud app password",
440
+ tokenHint: "Your Nextcloud server",
441
+ setupSteps: [
442
+ "1. Nextcloud Admin → OAuth → new client",
443
+ "2. Talk → view bot credentials",
444
+ " 🔗 nextcloud.com"
445
+ ],
446
+ extraFields: [{
447
+ name: "serverUrl",
448
+ label: "Nextcloud URL",
449
+ required: true
450
+ }, {
451
+ name: "username",
452
+ label: "Username",
453
+ required: true
454
+ }],
455
+ defaultDMPolicy: "allowlist"
456
+ },
457
+ {
458
+ id: "googlechat",
459
+ name: "Google Chat",
460
+ emoji: "🔵",
461
+ requiresGateway: true,
462
+ supportsDM: false,
463
+ platforms: ["all"],
464
+ tokenLabel: "Google Chat Webhook URL",
465
+ tokenHint: "chat.google.com → Space → Apps & Integrations",
466
+ setupSteps: [
467
+ "1. chat.google.com → Space → Apps & Integrations → Manage webhooks",
468
+ "2. Add webhook and copy the URL",
469
+ " 🔗 chat.google.com"
470
+ ],
471
+ defaultDMPolicy: "none"
472
+ },
473
+ {
474
+ id: "whatsapp-baileys",
475
+ name: "WhatsApp (Baileys)",
476
+ emoji: "📲",
477
+ requiresGateway: true,
478
+ supportsDM: true,
479
+ platforms: ["all"],
480
+ tokenLabel: "Not required — scans QR code on first run",
481
+ setupSteps: [
482
+ "1. No Meta Business API needed — uses WhatsApp Web protocol",
483
+ "2. Start the gateway — a QR code will appear in the terminal",
484
+ "3. Open WhatsApp on your phone → Linked Devices → Link a device",
485
+ "4. Scan the QR code. Session is saved — no QR needed on future starts",
486
+ " 🔗 github.com/WhiskeySockets/Baileys"
487
+ ],
488
+ notes: "WhatsApp Web via Baileys — no Meta Business needed. QR scan on first run.",
489
+ npmPackage: "@whiskeysockets/baileys",
490
+ defaultDMPolicy: "pairing"
491
+ },
492
+ {
493
+ id: "instagram",
494
+ name: "Instagram DMs",
495
+ emoji: "📸",
496
+ requiresGateway: true,
497
+ supportsDM: true,
498
+ platforms: ["all"],
499
+ tokenLabel: "Meta Page Access Token",
500
+ tokenHint: "developers.facebook.com → Instagram product",
501
+ setupSteps: [
502
+ "1. Meta for Developers → My Apps → Create App → Business",
503
+ "2. Add Instagram product → Connect Instagram Business account",
504
+ "3. Webhooks: subscribe to messages, URL: https://<host>/webhook/instagram",
505
+ "4. Copy Page Access Token from Graph API Explorer (pages_messaging scope)",
506
+ " 🔗 developers.facebook.com"
507
+ ],
508
+ extraFields: [{
509
+ name: "instagramAccountId",
510
+ label: "Instagram Business Account ID",
511
+ required: true
512
+ }, {
513
+ name: "verifyToken",
514
+ label: "Webhook Verify Token (any string)",
515
+ required: true
516
+ }],
517
+ notes: "Requires Instagram Business + Meta App",
518
+ defaultDMPolicy: "pairing"
519
+ },
520
+ {
521
+ id: "zalo-personal",
522
+ name: "Zalo Personal",
523
+ emoji: "🔵",
524
+ requiresGateway: true,
525
+ supportsDM: true,
526
+ platforms: ["all"],
527
+ tokenLabel: "Zalo cookie token",
528
+ tokenHint: "Extracted from browser session",
529
+ setupSteps: [
530
+ "1. Open Zalo Web in browser (chat.zalo.me)",
531
+ "2. Open DevTools → Application → Cookies → find zpw_sek or _zlang",
532
+ "3. Copy the session cookie value",
533
+ " ⚠ Unofficial API — may break on Zalo updates. Use at your own risk."
534
+ ],
535
+ notes: "Unofficial — uses Zalo Personal API. May break on updates.",
536
+ defaultDMPolicy: "pairing"
537
+ },
538
+ {
539
+ id: "voice-call",
540
+ name: "Voice Call",
541
+ emoji: "🎙️",
542
+ requiresGateway: true,
543
+ supportsDM: false,
544
+ platforms: ["all"],
545
+ tokenLabel: "Not required",
546
+ setupSteps: [
547
+ "1. No external account needed",
548
+ "2. Requires microphone + ElevenLabs API key for TTS (optional)",
549
+ "3. Start with: hyperclaw voice-call",
550
+ " 💡 Works in terminal — voice input → agent → voice output"
551
+ ],
552
+ notes: "Terminal voice session — hyperclaw voice-call",
553
+ defaultDMPolicy: "none"
554
+ },
555
+ {
556
+ id: "web",
557
+ name: "WebChat UI",
558
+ emoji: "🌐",
559
+ requiresGateway: true,
560
+ supportsDM: false,
561
+ platforms: ["all"],
562
+ setupSteps: [
563
+ "1. Built-in — no setup needed",
564
+ "2. Start gateway: hyperclaw gateway",
565
+ "3. Open: http://localhost:<port>/dashboard",
566
+ " 💡 Works in any browser on your local network"
567
+ ],
568
+ notes: "Built-in WebChat at http://localhost:<port>",
569
+ defaultDMPolicy: "none"
570
+ },
571
+ {
572
+ id: "cli",
573
+ name: "CLI / Terminal",
574
+ emoji: "🖥️",
575
+ requiresGateway: false,
576
+ supportsDM: false,
577
+ platforms: ["all"],
578
+ setupSteps: [
579
+ "1. Always active — no setup needed",
580
+ "2. Use: hyperclaw chat or hyperclaw agent --message \"...\"",
581
+ " 💡 Works offline without any channel configured"
582
+ ],
583
+ notes: "Always active — hyperclaw chat",
584
+ defaultDMPolicy: "none"
585
+ },
586
+ {
587
+ id: "chrome-extension",
588
+ name: "Chrome Extension",
589
+ emoji: "🔌",
590
+ requiresGateway: true,
591
+ supportsDM: false,
592
+ platforms: ["all"],
593
+ setupSteps: [
594
+ "1. Open Chrome → chrome://extensions → Enable Developer mode",
595
+ "2. Load unpacked → select: extensions/chrome-extension/",
596
+ "3. Extension connects to gateway via WebSocket automatically",
597
+ " 💡 Gives the agent access to your browser context"
598
+ ],
599
+ notes: "Load extensions/chrome-extension/ as unpacked extension in Chrome",
600
+ defaultDMPolicy: "none"
601
+ },
602
+ {
603
+ id: "synology-chat",
604
+ name: "Synology Chat",
605
+ emoji: "🖥️",
606
+ requiresGateway: true,
607
+ supportsDM: true,
608
+ platforms: ["all"],
609
+ tokenLabel: "Incoming Webhook URL",
610
+ tokenHint: "DSM → Synology Chat → Integration → Incoming Webhooks",
611
+ setupSteps: [
612
+ "1. Open Synology Chat (DSM package)",
613
+ "2. Top-right menu → Integration → Incoming Webhooks → Create",
614
+ "3. Copy the Webhook URL (used to POST messages from HyperClaw)",
615
+ "4. Outgoing Webhook: Integration → Outgoing Webhooks → Create",
616
+ " URL: http://<your-server>:7789/synology-hook",
617
+ " Method: POST",
618
+ " 🔗 kb.synology.com/synologychat"
619
+ ],
620
+ extraFields: [{
621
+ name: "webhookPort",
622
+ label: "Outgoing webhook port",
623
+ hint: "7789",
624
+ required: false
625
+ }, {
626
+ name: "webhookToken",
627
+ label: "Outgoing webhook token (optional HMAC)",
628
+ required: false
629
+ }],
630
+ notes: "Requires Synology Chat installed on your Synology NAS (DSM 7+)",
631
+ defaultDMPolicy: "pairing"
632
+ },
633
+ {
634
+ id: "tlon",
635
+ name: "Tlon (Urbit)",
636
+ emoji: "🪐",
637
+ requiresGateway: true,
638
+ supportsDM: true,
639
+ platforms: ["all"],
640
+ tokenLabel: "Urbit login code",
641
+ tokenHint: "From your Urbit ship: +code",
642
+ setupSteps: [
643
+ "1. Start your Urbit ship (e.g. via Port or urbit binary)",
644
+ "2. In the Dojo: +code → copy the code",
645
+ "3. Note your ship name (e.g. ~sampel-palnet) and ship URL",
646
+ "4. Optionally configure a group: ~sampel-palnet/my-group",
647
+ " 🔗 tlon.io · urbit.org/getting-started"
648
+ ],
649
+ extraFields: [
650
+ {
651
+ name: "shipUrl",
652
+ label: "Ship URL",
653
+ hint: "http://localhost:8080",
654
+ required: true
655
+ },
656
+ {
657
+ name: "ship",
658
+ label: "Ship name",
659
+ hint: "~sampel-palnet",
660
+ required: true
661
+ },
662
+ {
663
+ name: "group",
664
+ label: "Group path (optional)",
665
+ hint: "~sampel-palnet/my-group",
666
+ required: false
667
+ }
668
+ ],
669
+ notes: "Requires a running Urbit ship with Tlon/Groups installed",
670
+ defaultDMPolicy: "pairing"
671
+ },
672
+ {
673
+ id: "twitch",
674
+ name: "Twitch",
675
+ emoji: "🟣",
676
+ requiresGateway: true,
677
+ supportsDM: true,
678
+ platforms: ["all"],
679
+ tokenLabel: "OAuth token (oauth:xxxxxxx)",
680
+ tokenHint: "twitchapps.com/tmi → connect with your bot account",
681
+ setupSteps: [
682
+ "1. Create a Twitch bot account (or use your own)",
683
+ "2. Go to: twitchapps.com/tmi → Connect → copy the OAuth token",
684
+ "3. Token format: oauth:xxxxxxxxxxxxxxxxxxxxxx",
685
+ "4. Bot username = the Twitch account you logged in with",
686
+ " 💡 To receive commands, users type: !<message>",
687
+ " 💡 Moderators and the broadcaster bypass the allowlist by default",
688
+ " 🔗 twitchapps.com/tmi"
689
+ ],
690
+ extraFields: [
691
+ {
692
+ name: "username",
693
+ label: "Bot Twitch username (lowercase)",
694
+ required: true
695
+ },
696
+ {
697
+ name: "channels",
698
+ label: "Channel(s) to join (comma-separated)",
699
+ hint: "mychannel or mychannel,otherchannel",
700
+ required: true
701
+ },
702
+ {
703
+ name: "commandPrefix",
704
+ label: "Command prefix",
705
+ hint: "! (default)",
706
+ required: false
707
+ }
708
+ ],
709
+ notes: "Chat-based; uses Twitch IRC over WebSocket. Command prefix required (default: !)",
710
+ defaultDMPolicy: "pairing"
711
+ }
712
+ ];
713
+ async function getChannelStatus(def, configuredIds) {
714
+ const platform = os.default.platform();
715
+ if (!def.platforms.includes("all") && !def.platforms.includes(platform)) return "unavailable";
716
+ if (configuredIds.includes(def.id)) return "configured";
717
+ if (["telegram", "discord"].includes(def.id)) return "recommended";
718
+ return "available";
719
+ }
720
+ /** Human-readable reason why a channel is unavailable on this OS */
721
+ function unavailableReason(def) {
722
+ const platform = os.default.platform();
723
+ if (def.platforms.length === 1 && def.platforms[0] === "darwin") return "macOS only";
724
+ if (def.platforms.includes("linux") && def.platforms.includes("darwin") && !def.platforms.includes("win32")) return "Linux/macOS only";
725
+ if (!def.platforms.includes("all") && !def.platforms.includes(platform)) return `not supported on ${platform}`;
726
+ return "unavailable on this OS";
727
+ }
728
+ function statusBadge(status, def) {
729
+ switch (status) {
730
+ case "configured": return chalk.default.green("[configured]");
731
+ case "recommended": return chalk.default.cyan("[recommended]");
732
+ case "available": return chalk.default.gray("[available]");
733
+ case "unavailable": {
734
+ const reason = def ? unavailableReason(def) : "unavailable";
735
+ return chalk.default.red(`[${reason}]`);
736
+ }
737
+ }
738
+ }
739
+ const CHANNELS = CHANNEL_DEFS;
740
+ async function getAvailableChannels(configuredIds = []) {
741
+ const result = [];
742
+ for (const def of CHANNEL_DEFS) {
743
+ const status = await getChannelStatus(def, configuredIds);
744
+ result.push({
745
+ ...def,
746
+ status
747
+ });
748
+ }
749
+ return result;
750
+ }
751
+
752
+ //#endregion
753
+ //#region src/cli/memory.ts
754
+ require_paths$1.init_paths();
755
+ const getAgentsFile = () => path.default.join(require_paths.getHyperClawDir(), "AGENTS.md");
756
+ const getMemoryFile = () => path.default.join(require_paths.getHyperClawDir(), "MEMORY.md");
757
+ const getSoulFile = () => path.default.join(require_paths.getHyperClawDir(), "SOUL.md");
758
+ const getLogDir = () => path.default.join(require_paths.getHyperClawDir(), "logs");
759
+ var MemoryManager = class {
760
+ async init(opts = {}) {
761
+ await fs_extra.default.ensureDir(require_paths.getHyperClawDir());
762
+ await fs_extra.default.ensureDir(getLogDir());
763
+ const agentName = opts.agentName || "HyperClaw";
764
+ const userName = opts.userName || os.default.userInfo().username;
765
+ const lang = opts.language || "English";
766
+ const personality = opts.personality || "Direct and efficient, helpful without being sycophantic, honest about uncertainty";
767
+ const rules = opts.rules && opts.rules.length > 0 ? opts.rules.map((r, i) => `${i + 1}. ${r}`).join("\n") : `1. Always respond in the user's preferred language unless asked otherwise
768
+ 2. Be concise unless detail is explicitly requested
769
+ 3. Never share API keys, tokens, or secrets in responses
770
+ 4. If unsure, ask before acting — especially for destructive operations
771
+ 5. Log all PC access actions to ~/.hyperclaw/pc-access.log`;
772
+ const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
773
+ if (!await fs_extra.default.pathExists(getAgentsFile())) await fs_extra.default.writeFile(getAgentsFile(), `# AGENTS.md — Global Rules
774
+ > All sessions and subagents must follow these rules.
775
+
776
+ ## Identity
777
+ - Agent name: ${agentName}
778
+ - User name: ${userName}
779
+ - Primary language: ${lang}
780
+ - Created: ${today}
781
+
782
+ ## Behavior Rules
783
+ ${rules}
784
+
785
+ ## DM Policy Default
786
+ - Require pairing before responding to unknown senders
787
+ - Allowlist: (add trusted IDs here)
788
+
789
+ ## Hierarchy
790
+ - SOUL.md: persona and values (read-only by subagents)
791
+ - AGENTS.md: operational rules (this file)
792
+ - MEMORY.md: accumulated facts about the user
793
+
794
+ ## User prompt & skill integration
795
+ - When the user provides a prompt, system instruction, or persona text in chat: integrate it into SOUL.md using create_memory_file (filename: SOUL.md).
796
+ - When the user provides a skill definition or asks to add a skill: use the skill install tools (install from clawhub, paste content, or write_skill).
797
+ `);
798
+ if (!await fs_extra.default.pathExists(getMemoryFile())) await fs_extra.default.writeFile(getMemoryFile(), `# MEMORY.md — User Context
799
+ > Automatically updated by HyperClaw after each session.
800
+
801
+ ## User Profile
802
+ - Name: ${userName}
803
+ - Language: ${lang}
804
+ - Initialized: ${today}
805
+
806
+ ## Notes
807
+ (auto-populated from conversations)
808
+ `);
809
+ if (!await fs_extra.default.pathExists(getSoulFile())) await fs_extra.default.writeFile(getSoulFile(), `# SOUL.md — Agent Persona
810
+ > Who I am and how I behave.
811
+
812
+ ## Name
813
+ ${agentName}
814
+
815
+ ## Personality
816
+ - ${personality.replace(/\n/g, "\n- ")}
817
+
818
+ ## Values
819
+ - User autonomy first
820
+ - Do no harm
821
+ - Transparency about capabilities and limits
822
+
823
+ ## Wake phrase
824
+ ${opts.wakeWord ? opts.wakeWord : `Wake up, ${agentName}! Your user ${userName} needs you.`}
825
+ `);
826
+ const logFile = path.default.join(getLogDir(), `${today}.md`);
827
+ if (!await fs_extra.default.pathExists(logFile)) await fs_extra.default.writeFile(logFile, `# Session Log — ${today}\n\n`);
828
+ }
829
+ async load() {
830
+ if (!await fs_extra.default.pathExists(getAgentsFile())) return null;
831
+ const agents = await fs_extra.default.readFile(getAgentsFile(), "utf8").catch(() => "");
832
+ const memory = await fs_extra.default.readFile(getMemoryFile(), "utf8").catch(() => "");
833
+ const soul = await fs_extra.default.readFile(getSoulFile(), "utf8").catch(() => void 0);
834
+ return {
835
+ agents,
836
+ memory,
837
+ soul
838
+ };
839
+ }
840
+ async appendRule(rule) {
841
+ await fs_extra.default.ensureDir(require_paths.getHyperClawDir());
842
+ const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
843
+ const line = `\n- ${today}: ${rule}\n`;
844
+ await fs_extra.default.appendFile(getAgentsFile(), line);
845
+ console.log(chalk.default.green(` ✅ Rule added to AGENTS.md: ${rule}`));
846
+ }
847
+ async addMemory(fact) {
848
+ await fs_extra.default.ensureDir(require_paths.getHyperClawDir());
849
+ const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
850
+ await fs_extra.default.appendFile(getMemoryFile(), `\n- ${today}: ${fact}\n`);
851
+ console.log(chalk.default.green(` ✅ Memory saved: ${fact}`));
852
+ }
853
+ async updateSoul(content) {
854
+ await fs_extra.default.ensureDir(require_paths.getHyperClawDir());
855
+ await fs_extra.default.writeFile(getSoulFile(), content);
856
+ console.log(chalk.default.green(" ✅ SOUL.md updated"));
857
+ }
858
+ async show() {
859
+ const data = await this.load();
860
+ if (!data) {
861
+ console.log(chalk.default.yellow("\n No memory initialized. Run: hyperclaw init\n"));
862
+ return;
863
+ }
864
+ console.log(chalk.default.bold.cyan("\n 🧠 MEMORY\n"));
865
+ for (const [label, content] of [
866
+ ["SOUL.md", data.soul],
867
+ ["AGENTS.md", data.agents],
868
+ ["MEMORY.md", data.memory]
869
+ ]) {
870
+ if (!content) continue;
871
+ console.log(chalk.default.bold.white(` ── ${label} ──`));
872
+ const lines = content.split("\n").slice(0, 20);
873
+ for (const line of lines) if (line.startsWith("#")) console.log(chalk.default.cyan(` ${line}`));
874
+ else if (line.startsWith("-")) console.log(chalk.default.gray(` ${line}`));
875
+ else console.log(` ${line}`);
876
+ console.log();
877
+ }
878
+ }
879
+ async runPersonaBootstrap() {
880
+ console.log(chalk.default.bold.cyan("\n 🌅 Wake up, my friend!\n"));
881
+ const { agentName } = await inquirer.default.prompt([{
882
+ type: "input",
883
+ name: "agentName",
884
+ message: "What should I call myself? (agent name)",
885
+ default: "HyperClaw"
886
+ }]);
887
+ const { userName } = await inquirer.default.prompt([{
888
+ type: "input",
889
+ name: "userName",
890
+ message: `What shall ${agentName} call you?`,
891
+ default: os.default.userInfo().username
892
+ }]);
893
+ console.log(chalk.default.green(`\n ✨ I am ${agentName}. Hello, ${userName}.\n`));
894
+ return {
895
+ agentName,
896
+ userName
897
+ };
898
+ }
899
+ async getContextForAI() {
900
+ let context = "";
901
+ for (const [label, file] of [
902
+ ["SOUL", getSoulFile()],
903
+ ["AGENTS", getAgentsFile()],
904
+ ["MEMORY", getMemoryFile()]
905
+ ]) if (await fs_extra.default.pathExists(file)) {
906
+ const content = await fs_extra.default.readFile(file, "utf8");
907
+ context += `## ${label}.md\n${content}\n\n`;
908
+ }
909
+ return context;
910
+ }
911
+ async search(query) {
912
+ const data = await this.load();
913
+ if (!data) {
914
+ console.log(chalk.default.gray(" No memory\n"));
915
+ return;
916
+ }
917
+ const allText = [
918
+ data.agents,
919
+ data.memory,
920
+ data.soul || ""
921
+ ].join("\n");
922
+ const lines = allText.split("\n").filter((l) => l.toLowerCase().includes(query.toLowerCase()));
923
+ if (lines.length === 0) {
924
+ console.log(chalk.default.gray(` Nothing found for "${query}"\n`));
925
+ return;
926
+ }
927
+ console.log(chalk.default.bold.cyan(`\n 🔍 "${query}"\n`));
928
+ lines.forEach((l) => console.log(` ${l}`));
929
+ console.log();
930
+ }
931
+ async clear(file = "memory") {
932
+ const { confirm } = await inquirer.default.prompt([{
933
+ type: "confirm",
934
+ name: "confirm",
935
+ message: `Clear ${file === "all" ? "ALL memory files" : "MEMORY.md"}?`,
936
+ default: false
937
+ }]);
938
+ if (!confirm) return;
939
+ if (file === "all") {
940
+ await fs_extra.default.remove(getAgentsFile());
941
+ await fs_extra.default.remove(getMemoryFile());
942
+ await fs_extra.default.remove(getSoulFile());
943
+ console.log(chalk.default.green(" ✅ All memory cleared"));
944
+ } else {
945
+ await fs_extra.default.writeFile(getMemoryFile(), "# MEMORY.md\n\n");
946
+ console.log(chalk.default.green(" ✅ MEMORY.md cleared"));
947
+ }
948
+ }
949
+ };
950
+
951
+ //#endregion
952
+ //#region src/cli/security.ts
953
+ let _securityDisclaimerShown = false;
954
+ async function showSecurityDisclaimer() {
955
+ if (_securityDisclaimerShown) return true;
956
+ _securityDisclaimerShown = true;
957
+ console.clear();
958
+ console.log(chalk.default.bgRed.white.bold("\n ⚠️ SECURITY NOTICE — READ CAREFULLY ⚠️ \n"));
959
+ console.log(chalk.default.white.bold(" A bad prompt can trick it into doing unsafe things\n"));
960
+ console.log(chalk.default.hex("#06b6d4")(" Protections to enable:"));
961
+ console.log(chalk.default.gray(" ● Pairing / allowlists — limit who can DM your agent"));
962
+ console.log(chalk.default.gray(" ● Sandbox + least privilege — restrict PC access level"));
963
+ console.log(chalk.default.gray(" ● Keep secrets out — never put tokens in SOUL.md"));
964
+ console.log(chalk.default.gray(" ● Use strongest model — smarter = better at refusing tricks\n"));
965
+ const { understood } = await inquirer.default.prompt([{
966
+ type: "list",
967
+ name: "understood",
968
+ message: chalk.default.bold("I understand this is powerful and inherently risky."),
969
+ choices: [{
970
+ name: chalk.default.green("Yes, continue — I understand the risks"),
971
+ value: true
972
+ }, {
973
+ name: chalk.default.gray("No, exit — I'll come back later"),
974
+ value: false
975
+ }],
976
+ default: false
977
+ }]);
978
+ if (!understood) console.log(chalk.default.gray("\n Aborted. Come back when ready.\n"));
979
+ return understood;
980
+ }
981
+ async function configureDMPolicy(channelName) {
982
+ const { configureDMPolicy: cdmp } = await Promise.resolve().then(() => require("./security-B4vH02lO.js"));
983
+ const res = await cdmp(channelName);
984
+ return {
985
+ dmPolicy: res.policy,
986
+ allowFrom: res.allowFrom ?? []
987
+ };
988
+ }
989
+
990
+ //#endregion
991
+ //#region src/infra/channel-icons.ts
992
+ const CDN = "https://cdn.simpleicons.org";
993
+ function icon(slug, name, color, darkColor) {
994
+ return {
995
+ slug,
996
+ name,
997
+ color,
998
+ url: `${CDN}/${slug}`,
999
+ darkColor
1000
+ };
1001
+ }
1002
+ const CHANNEL_ICONS = {
1003
+ "telegram": icon("telegram", "Telegram", "26A5E4"),
1004
+ "discord": icon("discord", "Discord", "5865F2"),
1005
+ "whatsapp": icon("whatsapp", "WhatsApp", "25D366"),
1006
+ "whatsapp-baileys": icon("whatsapp", "WhatsApp (Baileys)", "25D366"),
1007
+ "slack": icon("slack", "Slack", "4A154B"),
1008
+ "signal": icon("signal", "Signal", "3A76F0", "FFFFFF"),
1009
+ "imessage": icon("imessage", "iMessage", "29CC40"),
1010
+ "imessage-native": icon("imessage", "iMessage (native)", "29CC40"),
1011
+ "matrix": icon("matrix", "Matrix", "000000", "FFFFFF"),
1012
+ "email": icon("gmail", "Email", "EA4335"),
1013
+ "feishu": icon("lark", "Feishu / Lark", "00D6B9"),
1014
+ "msteams": icon("microsoftteams", "Microsoft Teams", "6264A7"),
1015
+ "messenger": icon("messenger", "Facebook Messenger", "00B2FF"),
1016
+ "nostr": icon("nostr", "Nostr", "8E44AD", "C39BD3"),
1017
+ "line": icon("line", "LINE", "00C300"),
1018
+ "viber": icon("viber", "Viber", "7360F2"),
1019
+ "zalo": icon("zalo", "Zalo", "0068FF"),
1020
+ "zalo-personal": icon("zalo", "Zalo Personal", "0068FF"),
1021
+ "twitter": icon("x", "Twitter / X", "000000", "FFFFFF"),
1022
+ "irc": icon("irc", "IRC", "1A3B5C", "FFFFFF"),
1023
+ "mattermost": icon("mattermost", "Mattermost", "0058CC"),
1024
+ "nextcloud": icon("nextcloud", "Nextcloud Talk", "0082C9"),
1025
+ "googlechat": icon("googlechat", "Google Chat", "00897B"),
1026
+ "instagram": icon("instagram", "Instagram", "E4405F"),
1027
+ "synology-chat": icon("synology", "Synology Chat", "B5B5B6", "FFFFFF"),
1028
+ "tlon": icon("urbit", "Tlon (Urbit)", "000000", "FFFFFF"),
1029
+ "twitch": icon("twitch", "Twitch", "9146FF"),
1030
+ "voice-call": icon("webrtc", "Voice Call", "333333", "FFFFFF"),
1031
+ "web": icon("googlechrome", "WebChat", "4285F4"),
1032
+ "cli": icon("gnubash", "CLI / Terminal", "4EAA25", "FFFFFF"),
1033
+ "chrome-extension": icon("googlechrome", "Chrome Extension", "4285F4")
1034
+ };
1035
+ const PROVIDER_ICONS = {
1036
+ "anthropic": icon("anthropic", "Anthropic (Claude)", "D4C5A9", "191919"),
1037
+ "openai": icon("openai", "OpenAI", "000000", "FFFFFF"),
1038
+ "openrouter": icon("openrouter", "OpenRouter", "6467F2"),
1039
+ "groq": icon("groq", "Groq", "F55036"),
1040
+ "xai": icon("x", "xAI (Grok)", "000000", "FFFFFF"),
1041
+ "custom": icon("jsonwebtokens", "Custom OpenAI API", "A0A0A0"),
1042
+ "ollama": icon("ollama", "Ollama (local)", "000000", "FFFFFF"),
1043
+ "mistral": icon("mistral", "Mistral AI", "FF7000"),
1044
+ "cohere": icon("cohere", "Cohere", "39594D", "FFFFFF"),
1045
+ "google": icon("googlegemini", "Google Gemini", "8E75B2"),
1046
+ "deepseek": icon("deepseek", "DeepSeek", "4D6BFE"),
1047
+ "together": icon("togetherai", "Together AI", "000000", "FFFFFF")
1048
+ };
1049
+
1050
+ //#endregion
1051
+ //#region src/cli/onboard.ts
1052
+ require_providers.init_providers();
1053
+ /** Brand-colored square - closest we can get to a real icon in a terminal */
1054
+ function brandIcon(id, type = "channel") {
1055
+ const map = type === "channel" ? CHANNEL_ICONS : PROVIDER_ICONS;
1056
+ const ic = map[id];
1057
+ if (!ic) return chalk.default.gray("●");
1058
+ return chalk.default.hex("#" + ic.color)("●");
1059
+ }
1060
+ var HyperClawWizard = class {
1061
+ config = new require_config.ConfigStore();
1062
+ daemon = new require_daemon.DaemonManager();
1063
+ gateway = new require_gateway.GatewayManager();
1064
+ async run(options) {
1065
+ const existingCfg = await this.config.load();
1066
+ const hasExistingConfig = !!(existingCfg && (existingCfg.provider || existingCfg.providers?.length));
1067
+ if (hasExistingConfig && !options.nonInteractive) {
1068
+ const { getConfigPath } = await Promise.resolve().then(() => require("./paths-D-QecARF.js"));
1069
+ console.log(chalk.default.yellow(`\n ⚠ Existing config detected: ${chalk.default.bold(getConfigPath())}\n`));
1070
+ const { configAction } = await inquirer.default.prompt([{
1071
+ type: "list",
1072
+ name: "configAction",
1073
+ message: "What would you like to do?",
1074
+ choices: [
1075
+ {
1076
+ name: `${chalk.default.green("✔")} Keep & modify ${chalk.default.gray("(keep existing config, change specific settings)")}`,
1077
+ value: "modify"
1078
+ },
1079
+ {
1080
+ name: ` Keep & continue ${chalk.default.gray("(re-run wizard, keep all current values as defaults)")}`,
1081
+ value: "keep"
1082
+ },
1083
+ {
1084
+ name: `${chalk.default.yellow("⚠")} Reset ${chalk.default.gray("(back up config and start fresh)")}`,
1085
+ value: "reset"
1086
+ }
1087
+ ]
1088
+ }]);
1089
+ if (configAction === "reset") {
1090
+ const { resetScope } = await inquirer.default.prompt([{
1091
+ type: "list",
1092
+ name: "resetScope",
1093
+ message: "Reset scope:",
1094
+ choices: [
1095
+ {
1096
+ name: "Config only",
1097
+ value: "config"
1098
+ },
1099
+ {
1100
+ name: "Config + credentials + sessions",
1101
+ value: "config+creds"
1102
+ },
1103
+ {
1104
+ name: "Full reset (config + credentials + workspace)",
1105
+ value: "full"
1106
+ }
1107
+ ]
1108
+ }]);
1109
+ const fs$3 = await import("fs-extra");
1110
+ const path$3 = await import("path");
1111
+ const { getHyperClawDir: getHyperClawDir$1, getConfigPath: getConfigPath$1 } = await Promise.resolve().then(() => require("./paths-D-QecARF.js"));
1112
+ const hcDir = getHyperClawDir$1();
1113
+ const backupDir = path$3.join(hcDir, `backup-${Date.now()}`);
1114
+ await fs$3.ensureDir(backupDir);
1115
+ const toRemove = [getConfigPath$1()];
1116
+ if (resetScope !== "config") toRemove.push(path$3.join(hcDir, "credentials"), path$3.join(hcDir, "sessions"));
1117
+ if (resetScope === "full") toRemove.push(path$3.join(hcDir, "workspace"));
1118
+ for (const f of toRemove) if (await fs$3.pathExists(f)) await fs$3.move(f, path$3.join(backupDir, path$3.basename(f)));
1119
+ console.log(chalk.default.green(`\n ✔ Config backed up to ${backupDir}\n Starting fresh...\n`));
1120
+ } else if (configAction === "modify") console.log(chalk.default.gray("\n Continuing with existing config as defaults...\n"));
1121
+ }
1122
+ const proceed = await showSecurityDisclaimer();
1123
+ if (!proceed) return;
1124
+ const { allThemes, getThemeName, setThemeName } = await Promise.resolve().then(() => require("./theme-DdZT-Bq4.js"));
1125
+ const currentTheme = getThemeName();
1126
+ const { chosenTheme } = await inquirer.default.prompt([{
1127
+ type: "list",
1128
+ name: "chosenTheme",
1129
+ message: "Choose a color theme:",
1130
+ default: currentTheme,
1131
+ choices: [
1132
+ {
1133
+ name: `${chalk.default.hex("#06b6d4")("●")} Dark Professional ${chalk.default.gray("(neon cyan on black)")}`,
1134
+ value: "dark"
1135
+ },
1136
+ {
1137
+ name: `${chalk.default.hex("#64748b")("●")} Grey Professional ${chalk.default.gray("(muted cyan, neutral)")}`,
1138
+ value: "grey"
1139
+ },
1140
+ {
1141
+ name: `${chalk.default.hex("#0284c7")("●")} White / Light ${chalk.default.gray("(deep cyan, light bg)")}`,
1142
+ value: "white"
1143
+ }
1144
+ ]
1145
+ }]);
1146
+ if (chosenTheme !== currentTheme) await setThemeName(chosenTheme);
1147
+ await new require_banner.Banner().showWizardBanner();
1148
+ const { mode } = await inquirer.default.prompt([{
1149
+ type: "list",
1150
+ name: "mode",
1151
+ message: "Setup mode:",
1152
+ choices: [{
1153
+ name: `${chalk.default.green("✔")} QuickStart ${chalk.default.gray("(Recommended)")}`,
1154
+ value: "quick"
1155
+ }, {
1156
+ name: ` Manual ${chalk.default.gray("(Full control)")}`,
1157
+ value: "manual"
1158
+ }]
1159
+ }]);
1160
+ if (mode === "quick") return this.quickSetup(options);
1161
+ return this.fullSetup(options);
1162
+ }
1163
+ async quickstart(options) {
1164
+ const proceed = await showSecurityDisclaimer();
1165
+ if (!proceed) return;
1166
+ return this.quickSetup(options);
1167
+ }
1168
+ stepHeader(n, total, title) {
1169
+ const { getTheme } = (require_theme.init_theme(), require_chunk.__toCommonJS(require_theme.theme_exports));
1170
+ const t = getTheme(false);
1171
+ const bar = chalk.default.gray("-".repeat(52));
1172
+ console.log(`\n${bar}`);
1173
+ console.log(` ${t.c(`Step ${n} / ${total}`)} — ${chalk.default.bold(title)}`);
1174
+ console.log(`${bar}\n`);
1175
+ }
1176
+ async quickSetup(options) {
1177
+ const STEPS = 8;
1178
+ this.stepHeader(1, STEPS, "Workspace");
1179
+ const { workspaceName } = await inquirer.default.prompt([{
1180
+ type: "input",
1181
+ name: "workspaceName",
1182
+ message: "Workspace name:",
1183
+ default: "my-hyperclaw"
1184
+ }]);
1185
+ this.stepHeader(2, STEPS, "AI Providers & Models");
1186
+ const { providers: allProviders, primary: providerConfig } = await this.selectProvidersAndModels();
1187
+ this.stepHeader(3, STEPS, "Gateway (optional)");
1188
+ const gatewayConfig = await this.configureGateway(false);
1189
+ this.stepHeader(4, STEPS, "Channels");
1190
+ const channelConfigs = await this.selectChannels();
1191
+ this.stepHeader(5, STEPS, "Agent Identity & Persona");
1192
+ const identity = await this.configureIdentity();
1193
+ this.stepHeader(6, STEPS, "Skills & Hooks");
1194
+ const { hooks, heartbeat: heartbeatEnabled } = await this.configureSkillsAndHooks();
1195
+ this.stepHeader(7, STEPS, "Launch");
1196
+ const launchChoices = [{
1197
+ name: `${chalk.default.yellow("Hatch in TUI")} (recommended) — terminal chat`,
1198
+ value: "tui"
1199
+ }];
1200
+ if (gatewayConfig && !gatewayConfig.remote) launchChoices.push({
1201
+ name: `${chalk.default.cyan("Open the Web UI")} — browser at http://localhost:${gatewayConfig.port}`,
1202
+ value: "web"
1203
+ });
1204
+ launchChoices.push({
1205
+ name: chalk.default.gray("Do this later — I will start manually"),
1206
+ value: "skip"
1207
+ });
1208
+ const { hatchMode } = await inquirer.default.prompt([{
1209
+ type: "list",
1210
+ name: "hatchMode",
1211
+ message: "How do you want to hatch your bot?",
1212
+ choices: launchChoices
1213
+ }]);
1214
+ let installDaemon = options.installDaemon ?? false;
1215
+ let startNow = false;
1216
+ if (!installDaemon) {
1217
+ const { wantDaemon } = await inquirer.default.prompt([{
1218
+ type: "confirm",
1219
+ name: "wantDaemon",
1220
+ message: "Install as system daemon (auto-start on boot)?",
1221
+ default: false
1222
+ }]);
1223
+ installDaemon = wantDaemon;
1224
+ }
1225
+ if (installDaemon || !Object.keys(channelConfigs).length) startNow = true;
1226
+ else {
1227
+ const { wantStart } = await inquirer.default.prompt([{
1228
+ type: "confirm",
1229
+ name: "wantStart",
1230
+ message: "Start the bot now? (Telegram/Discord will respond once running)",
1231
+ default: true
1232
+ }]);
1233
+ startNow = wantStart;
1234
+ }
1235
+ this.stepHeader(8, STEPS, "Extras");
1236
+ const { skipExtras } = await inquirer.default.prompt([{
1237
+ type: "confirm",
1238
+ name: "skipExtras",
1239
+ message: "Skip optional extras for now? (web search, memory, services, etc. — configure later)",
1240
+ default: true
1241
+ }]);
1242
+ let webSearch, memoryIntegration, serviceApiKeys, hyperclawbotConfig, talkModeConfig;
1243
+ let pcAccess, updateChannel, groupSandbox, rateLimit;
1244
+ if (!skipExtras) {
1245
+ webSearch = await this.configureWebSearch(options.skipSearch);
1246
+ memoryIntegration = await this.configureMemoryIntegration();
1247
+ serviceApiKeys = await this.configureServiceApiKeys();
1248
+ hyperclawbotConfig = await this.configureHyperClawBot(gatewayConfig, channelConfigs);
1249
+ talkModeConfig = await this.configureTalkMode();
1250
+ pcAccess = await this.configurePcAccess();
1251
+ updateChannel = await this.configureUpdateChannel();
1252
+ groupSandbox = await this.configureGroupSandbox();
1253
+ await this.configureSkillHub();
1254
+ await this.configureMcpServers();
1255
+ await this.configureWorkspaceTemplate();
1256
+ await this.configureCronTasks();
1257
+ await this.configureAutoReply();
1258
+ await this.configureWebhooks();
1259
+ await this.configureNodes();
1260
+ await this.configureOAuth();
1261
+ await this.configureAgentBindings();
1262
+ rateLimit = await this.configureRateLimiting();
1263
+ await this.configureDeveloperKey();
1264
+ await this.configureVoiceCall();
1265
+ await this.configureCanvas();
1266
+ await this.configureDeploy();
1267
+ }
1268
+ await this.saveAll({
1269
+ workspaceName,
1270
+ providerConfig,
1271
+ providers: allProviders,
1272
+ channelConfigs,
1273
+ gatewayConfig,
1274
+ identity,
1275
+ hooks,
1276
+ heartbeatEnabled,
1277
+ webSearch,
1278
+ memoryIntegration,
1279
+ serviceApiKeys,
1280
+ hyperclawbotConfig,
1281
+ talkModeConfig,
1282
+ pcAccess,
1283
+ updateChannel,
1284
+ groupSandbox,
1285
+ rateLimit,
1286
+ installDaemon,
1287
+ startNow,
1288
+ hatchMode
1289
+ }, options);
1290
+ }
1291
+ async fullSetup(options) {
1292
+ const STEPS = 9;
1293
+ this.stepHeader(1, STEPS, "Workspace");
1294
+ const { workspaceName } = await inquirer.default.prompt([{
1295
+ type: "input",
1296
+ name: "workspaceName",
1297
+ message: "Workspace directory:",
1298
+ default: "my-hyperclaw"
1299
+ }]);
1300
+ this.stepHeader(2, STEPS, "AI Providers & Models");
1301
+ const { providers: allProviders, primary: providerConfig } = await this.selectProvidersAndModels();
1302
+ this.stepHeader(3, STEPS, "Gateway");
1303
+ const gatewayConfig = await this.configureGateway(true);
1304
+ this.stepHeader(4, STEPS, "Channels");
1305
+ const { configureChannels } = await inquirer.default.prompt([{
1306
+ type: "confirm",
1307
+ name: "configureChannels",
1308
+ message: "Configure chat channels now?",
1309
+ default: true
1310
+ }]);
1311
+ const channelConfigs = configureChannels ? await this.selectChannels(true) : {};
1312
+ if (configureChannels) {
1313
+ const { configureDM } = await inquirer.default.prompt([{
1314
+ type: "confirm",
1315
+ name: "configureDM",
1316
+ message: "Configure DM access policies now?",
1317
+ default: true
1318
+ }]);
1319
+ if (configureDM) for (const [channelId] of Object.entries(channelConfigs)) {
1320
+ const ch = CHANNELS.find((c) => c.id === channelId);
1321
+ if (ch?.supportsDM) {
1322
+ const dm = await configureDMPolicy(ch.name);
1323
+ channelConfigs[channelId].dmPolicy = dm;
1324
+ }
1325
+ }
1326
+ }
1327
+ this.stepHeader(5, STEPS, "Agent Identity & Persona");
1328
+ const identity = await this.configureIdentity();
1329
+ this.stepHeader(6, STEPS, "Skills & Hooks");
1330
+ const { hooks, heartbeat: heartbeatEnabled } = await this.configureSkillsAndHooks();
1331
+ this.stepHeader(7, STEPS, "Services & Daemon");
1332
+ let installDaemon = options.installDaemon ?? false;
1333
+ let daemonRuntime = options.daemonRuntime ?? "node";
1334
+ if (!installDaemon) {
1335
+ console.log(chalk.default.gray(" The daemon runs the gateway as a background service that starts on boot.\n"));
1336
+ const ans = await inquirer.default.prompt([{
1337
+ type: "confirm",
1338
+ name: "installDaemon",
1339
+ message: "Install as system daemon (auto-start on boot)?",
1340
+ default: false
1341
+ }, {
1342
+ type: "list",
1343
+ name: "daemonRuntime",
1344
+ message: "Daemon runtime:",
1345
+ default: "node",
1346
+ choices: [{
1347
+ name: `Node.js ${chalk.default.green("(recommended)")} — required for WhatsApp/Telegram`,
1348
+ value: "node"
1349
+ }, {
1350
+ name: `Bun ${chalk.default.gray("(faster startup, experimental)")}`,
1351
+ value: "bun"
1352
+ }],
1353
+ when: (answers) => answers.installDaemon
1354
+ }]);
1355
+ installDaemon = ans.installDaemon;
1356
+ if (ans.daemonRuntime) daemonRuntime = ans.daemonRuntime;
1357
+ } else console.log(chalk.default.green(` ✔ Daemon will be installed (runtime: ${daemonRuntime})\n`));
1358
+ this.stepHeader(8, STEPS, "Extras");
1359
+ const { skipExtras } = await inquirer.default.prompt([{
1360
+ type: "confirm",
1361
+ name: "skipExtras",
1362
+ message: "Skip optional extras for now? (web search, memory, services, etc. — configure later)",
1363
+ default: true
1364
+ }]);
1365
+ let webSearch, memoryIntegration, serviceApiKeys, hyperclawbotConfig, talkModeConfig;
1366
+ let pcAccess, updateChannel, groupSandbox, rateLimit;
1367
+ if (!skipExtras) {
1368
+ webSearch = await this.configureWebSearch(options.skipSearch);
1369
+ memoryIntegration = await this.configureMemoryIntegration();
1370
+ serviceApiKeys = await this.configureServiceApiKeys();
1371
+ hyperclawbotConfig = await this.configureHyperClawBot(gatewayConfig, channelConfigs);
1372
+ talkModeConfig = await this.configureTalkMode();
1373
+ pcAccess = await this.configurePcAccess();
1374
+ updateChannel = await this.configureUpdateChannel();
1375
+ groupSandbox = await this.configureGroupSandbox();
1376
+ await this.configureSkillHub();
1377
+ await this.configureMcpServers();
1378
+ await this.configureWorkspaceTemplate();
1379
+ await this.configureCronTasks();
1380
+ await this.configureAutoReply();
1381
+ await this.configureWebhooks();
1382
+ await this.configureNodes();
1383
+ await this.configureOAuth();
1384
+ await this.configureAgentBindings();
1385
+ rateLimit = await this.configureRateLimiting();
1386
+ await this.configureDeveloperKey();
1387
+ await this.configureVoiceCall();
1388
+ await this.configureCanvas();
1389
+ await this.configureDeploy();
1390
+ }
1391
+ this.stepHeader(9, STEPS, "Launch");
1392
+ const launchChoices = [{
1393
+ name: `${chalk.default.yellow("Hatch in TUI")} (recommended) — terminal chat`,
1394
+ value: "tui"
1395
+ }];
1396
+ if (gatewayConfig && !gatewayConfig.remote) launchChoices.push({
1397
+ name: `${chalk.default.cyan("Open the Web UI")} — browser at http://localhost:${gatewayConfig.port}`,
1398
+ value: "web"
1399
+ });
1400
+ launchChoices.push({
1401
+ name: chalk.default.gray("Do this later — I will start manually"),
1402
+ value: "skip"
1403
+ });
1404
+ const { hatchMode } = await inquirer.default.prompt([{
1405
+ type: "list",
1406
+ name: "hatchMode",
1407
+ message: "How do you want to hatch your bot?",
1408
+ choices: launchChoices
1409
+ }]);
1410
+ await this.saveAll({
1411
+ workspaceName,
1412
+ providerConfig,
1413
+ providers: allProviders,
1414
+ channelConfigs,
1415
+ gatewayConfig: gatewayConfig ? {
1416
+ ...gatewayConfig,
1417
+ hooks: hooks.length > 0
1418
+ } : null,
1419
+ identity,
1420
+ hooks,
1421
+ heartbeatEnabled,
1422
+ installDaemon,
1423
+ daemonRuntime,
1424
+ hatchMode,
1425
+ webSearch,
1426
+ memoryIntegration,
1427
+ serviceApiKeys,
1428
+ hyperclawbotConfig,
1429
+ talkModeConfig,
1430
+ pcAccess,
1431
+ updateChannel,
1432
+ groupSandbox,
1433
+ rateLimit
1434
+ }, options);
1435
+ }
1436
+ async selectProvidersAndModels() {
1437
+ const { getTheme } = (require_theme.init_theme(), require_chunk.__toCommonJS(require_theme.theme_exports));
1438
+ const t = getTheme(false);
1439
+ console.log(chalk.default.gray(" Select one or more AI providers. The first one will be primary.\n"));
1440
+ const { selectedProviderIds } = await inquirer.default.prompt([{
1441
+ type: "checkbox",
1442
+ name: "selectedProviderIds",
1443
+ message: "Select AI providers:",
1444
+ validate: (v) => v.length > 0 || "Select at least one provider",
1445
+ choices: require_providers.PROVIDERS.map((p) => ({
1446
+ name: `${brandIcon(p.id, "provider")} ${p.displayName.replace(/^.{1,2}\s/, "").padEnd(18)}${p.supportsTranscription ? chalk.default.gray(" 🎙") : ""}`,
1447
+ value: p.id,
1448
+ checked: p.id === "openrouter"
1449
+ }))
1450
+ }]);
1451
+ const configured = [];
1452
+ for (let i = 0; i < selectedProviderIds.length; i++) {
1453
+ const pid = selectedProviderIds[i];
1454
+ const provider = require_providers.getProvider(pid);
1455
+ const isPrimary = i === 0;
1456
+ console.log(`\n ${t.c("●")} ${chalk.default.bold(provider.displayName)} ${isPrimary ? chalk.default.green("(primary)") : chalk.default.gray(`(#${i + 1})`)}\n`);
1457
+ if (provider.authHint) console.log(chalk.default.gray(` 💡 ${provider.authHint}\n`));
1458
+ let apiKey = "";
1459
+ let baseUrl;
1460
+ let modelId = "";
1461
+ if (pid === "anthropic-oauth") {
1462
+ const fs$3 = await import("fs-extra");
1463
+ const path$3 = await import("path");
1464
+ const os$3 = await import("os");
1465
+ const credPath = path$3.join(os$3.homedir(), ".claude", ".credentials.json");
1466
+ const credExists = await fs$3.pathExists(credPath);
1467
+ if (credExists) {
1468
+ console.log(chalk.default.green(` ✔ Found Claude credentials: ${credPath}`));
1469
+ console.log(chalk.default.gray(" HyperClaw will reuse these credentials automatically.\n"));
1470
+ apiKey = "__claude_oauth__";
1471
+ } else {
1472
+ console.log(chalk.default.yellow(` ⚠ ~/.claude/.credentials.json not found.\n`));
1473
+ console.log(chalk.default.gray(" Run `claude` CLI on this machine first to authenticate,\n or paste a setup-token (use \"Anthropic setup-token\" provider instead).\n"));
1474
+ apiKey = "__claude_oauth__";
1475
+ }
1476
+ } else if (pid === "anthropic-setup-token") {
1477
+ console.log(chalk.default.gray(" Run `claude setup-token` on any machine > paste the token below.\n"));
1478
+ const r = await inquirer.default.prompt([{
1479
+ type: "password",
1480
+ name: "apiKey",
1481
+ message: " Setup token (sk-ant-setup-...):",
1482
+ mask: "?",
1483
+ validate: (v) => v.trim().length > 10 || "Required"
1484
+ }]);
1485
+ apiKey = r.apiKey.trim();
1486
+ } else if (provider.authType === "api_key") if (pid === "custom") {
1487
+ const r = await inquirer.default.prompt([
1488
+ {
1489
+ type: "input",
1490
+ name: "baseUrl",
1491
+ message: " Base URL:",
1492
+ validate: (v) => v.trim().length > 5 || "Required"
1493
+ },
1494
+ {
1495
+ type: "password",
1496
+ name: "apiKey",
1497
+ message: ` ${provider.authLabel}:`,
1498
+ mask: "?",
1499
+ validate: (v) => v.trim().length > 5 || "Required"
1500
+ },
1501
+ {
1502
+ type: "input",
1503
+ name: "modelId",
1504
+ message: " Model ID:",
1505
+ validate: (v) => v.trim().length > 0 || "Required"
1506
+ }
1507
+ ]);
1508
+ baseUrl = r.baseUrl.trim().replace(/\/$/, "");
1509
+ apiKey = r.apiKey;
1510
+ modelId = r.modelId.trim();
1511
+ } else {
1512
+ const { validateApiKeyFormat } = await Promise.resolve().then(() => require("./api-key-validation-DgOBmp8Y.js"));
1513
+ const r = await inquirer.default.prompt([{
1514
+ type: "password",
1515
+ name: "apiKey",
1516
+ message: ` ${provider.authLabel}:`,
1517
+ mask: "●",
1518
+ validate: (v) => {
1519
+ const t$1 = (v || "").trim();
1520
+ if (t$1.length < 5) return "Required";
1521
+ const err = validateApiKeyFormat(pid, t$1);
1522
+ return err || true;
1523
+ }
1524
+ }]);
1525
+ apiKey = r.apiKey;
1526
+ }
1527
+ if (!modelId) {
1528
+ const modelChoices = provider.models.filter((m) => m.id !== "__manual__").length ? [...provider.models.filter((m) => m.id !== "__manual__").map((m) => ({
1529
+ name: require_providers.formatModel(m),
1530
+ value: m.id
1531
+ })), {
1532
+ name: chalk.default.gray("Enter manually..."),
1533
+ value: "__manual__"
1534
+ }] : [{
1535
+ name: chalk.default.gray("Enter model ID manually"),
1536
+ value: "__manual__"
1537
+ }];
1538
+ const { modelChoice } = await inquirer.default.prompt([{
1539
+ type: "list",
1540
+ name: "modelChoice",
1541
+ message: " Default model:",
1542
+ choices: modelChoices
1543
+ }]);
1544
+ if (modelChoice === "__manual__") {
1545
+ const { manual } = await inquirer.default.prompt([{
1546
+ type: "input",
1547
+ name: "manual",
1548
+ message: " Model ID:",
1549
+ default: provider.models[0]?.id || ""
1550
+ }]);
1551
+ modelId = manual;
1552
+ } else modelId = modelChoice;
1553
+ }
1554
+ const apiKeys = [];
1555
+ if (apiKey) {
1556
+ const { wantRotation } = await inquirer.default.prompt([{
1557
+ type: "confirm",
1558
+ name: "wantRotation",
1559
+ message: ` Add extra API keys for rate-limit rotation? ${chalk.default.gray("(gateway cycles through on 429)")}`,
1560
+ default: false
1561
+ }]);
1562
+ if (wantRotation) {
1563
+ let addMore = true;
1564
+ while (addMore) {
1565
+ const { extraKey } = await inquirer.default.prompt([{
1566
+ type: "password",
1567
+ name: "extraKey",
1568
+ message: ` Extra key #${apiKeys.length + 1}:`,
1569
+ mask: "?"
1570
+ }]);
1571
+ if (extraKey.trim()) apiKeys.push(extraKey.trim());
1572
+ const { more } = await inquirer.default.prompt([{
1573
+ type: "confirm",
1574
+ name: "more",
1575
+ message: " Add another?",
1576
+ default: false
1577
+ }]);
1578
+ addMore = more;
1579
+ }
1580
+ if (apiKeys.length > 0) console.log(chalk.default.green(` ✔ ${apiKeys.length} extra key(s) added for rotation\n`));
1581
+ }
1582
+ }
1583
+ let thinking;
1584
+ if (["anthropic", "openai"].includes(pid)) {
1585
+ const { thinkingLevel } = await inquirer.default.prompt([{
1586
+ type: "list",
1587
+ name: "thinkingLevel",
1588
+ message: ` Extended thinking / reasoning:`,
1589
+ choices: [
1590
+ {
1591
+ name: `Off ${chalk.default.gray("(standard responses)")}`,
1592
+ value: "off"
1593
+ },
1594
+ {
1595
+ name: `Standard ${chalk.default.gray("(~8 000 token budget)")}`,
1596
+ value: "standard"
1597
+ },
1598
+ {
1599
+ name: `Extended ${chalk.default.gray("(~32 000 token budget — slower, deeper)")}`,
1600
+ value: "extended"
1601
+ }
1602
+ ],
1603
+ default: "off"
1604
+ }]);
1605
+ if (thinkingLevel !== "off") {
1606
+ thinking = {
1607
+ enabled: true,
1608
+ budgetTokens: thinkingLevel === "extended" ? 32e3 : 8e3
1609
+ };
1610
+ console.log(chalk.default.green(` ✔ Thinking: ${thinkingLevel} (${thinking.budgetTokens.toLocaleString()} tokens)\n`));
1611
+ }
1612
+ }
1613
+ configured.push({
1614
+ providerId: pid,
1615
+ apiKey,
1616
+ modelId,
1617
+ ...baseUrl ? { baseUrl } : {},
1618
+ ...apiKeys.length > 0 ? { apiKeys } : {},
1619
+ ...thinking ? { thinking } : {}
1620
+ });
1621
+ console.log(t.c(` ✔ ${provider.displayName} > ${modelId}\n`));
1622
+ }
1623
+ return {
1624
+ providers: configured,
1625
+ primary: configured[0]
1626
+ };
1627
+ }
1628
+ async configureGateway(full = false) {
1629
+ const { gatewayMode } = await inquirer.default.prompt([{
1630
+ type: "list",
1631
+ name: "gatewayMode",
1632
+ message: "Gateway setup:",
1633
+ choices: [
1634
+ {
1635
+ name: `Local gateway ${chalk.default.gray("(run on this machine)")}`,
1636
+ value: "local"
1637
+ },
1638
+ {
1639
+ name: `Remote gateway ${chalk.default.gray("(info-only — connect to existing server)")}`,
1640
+ value: "remote"
1641
+ },
1642
+ {
1643
+ name: chalk.default.gray("Skip (no gateway — CLI-only mode)"),
1644
+ value: "skip"
1645
+ }
1646
+ ]
1647
+ }]);
1648
+ if (gatewayMode === "skip") {
1649
+ console.log(chalk.default.gray(" ● Gateway skipped. Enable later: hyperclaw gateway start\n"));
1650
+ return null;
1651
+ }
1652
+ if (gatewayMode === "remote") {
1653
+ const { remoteUrl } = await inquirer.default.prompt([{
1654
+ type: "input",
1655
+ name: "remoteUrl",
1656
+ message: "Remote gateway URL (e.g. wss://myserver.com:18789 or http://127.0.0.1:18789 for SSH tunnel):",
1657
+ validate: (v) => v.startsWith("ws") || v.startsWith("http") || "Must start with ws://, wss:// or http://"
1658
+ }]);
1659
+ const { remoteToken } = await inquirer.default.prompt([{
1660
+ type: "password",
1661
+ name: "remoteToken",
1662
+ message: "Remote gateway auth token:",
1663
+ mask: "?"
1664
+ }]);
1665
+ const httpUrl = remoteUrl.startsWith("http") ? remoteUrl : remoteUrl.replace(/^ws/, "http");
1666
+ console.log(chalk.default.green(" ✔ Remote gateway configured\n"));
1667
+ return {
1668
+ port: 18789,
1669
+ bind: "127.0.0.1",
1670
+ authToken: "",
1671
+ tailscaleExposure: "off",
1672
+ runtime: "node",
1673
+ enabledChannels: [],
1674
+ hooks: true,
1675
+ mode: "remote",
1676
+ remote: {
1677
+ url: httpUrl,
1678
+ token: remoteToken || void 0
1679
+ }
1680
+ };
1681
+ }
1682
+ const { useGateway } = await inquirer.default.prompt([{
1683
+ type: "confirm",
1684
+ name: "useGateway",
1685
+ message: "Enable the local WebSocket gateway? (needed for channels, web UI, mobile apps)",
1686
+ default: true
1687
+ }]);
1688
+ if (!useGateway) {
1689
+ console.log(chalk.default.gray(" ● Gateway skipped. You can enable it later: hyperclaw gateway start\n"));
1690
+ return null;
1691
+ }
1692
+ const running = await this.gateway.isRunning(18789);
1693
+ if (running) console.log(chalk.default.gray(` ● Gateway already running at ws://127.0.0.1:18789`));
1694
+ const detectedRuntime = await this.gateway.detectRuntime();
1695
+ const questions = [{
1696
+ type: "input",
1697
+ name: "port",
1698
+ message: "Gateway port:",
1699
+ default: String(18789),
1700
+ validate: (v) => Number(v) > 1024 && Number(v) < 65535 ? true : "Enter valid port (1024-65535)"
1701
+ }];
1702
+ if (full) questions.push({
1703
+ type: "list",
1704
+ name: "bind",
1705
+ message: "Bind address:",
1706
+ choices: [
1707
+ {
1708
+ name: `127.0.0.1 ${chalk.default.gray("Loopback only (secure)")}`,
1709
+ value: "127.0.0.1"
1710
+ },
1711
+ {
1712
+ name: `0.0.0.0 ${chalk.default.gray("All interfaces (LAN)")}`,
1713
+ value: "0.0.0.0"
1714
+ },
1715
+ {
1716
+ name: `Tailscale ${chalk.default.gray("VPN peers only")}`,
1717
+ value: "tailscale"
1718
+ },
1719
+ {
1720
+ name: `Custom ${chalk.default.gray("Enter IP manually")}`,
1721
+ value: "custom"
1722
+ }
1723
+ ],
1724
+ default: "127.0.0.1"
1725
+ }, {
1726
+ type: "list",
1727
+ name: "tailscaleExposure",
1728
+ message: "Tailscale exposure:",
1729
+ choices: [
1730
+ {
1731
+ name: this.gateway.exposureLabel("off"),
1732
+ value: "off"
1733
+ },
1734
+ {
1735
+ name: this.gateway.exposureLabel("serve"),
1736
+ value: "serve"
1737
+ },
1738
+ {
1739
+ name: this.gateway.exposureLabel("funnel"),
1740
+ value: "funnel"
1741
+ }
1742
+ ],
1743
+ default: "off",
1744
+ when: (a) => a.bind === "tailscale"
1745
+ }, {
1746
+ type: "list",
1747
+ name: "runtime",
1748
+ message: "Runtime:",
1749
+ choices: [
1750
+ {
1751
+ name: `Node.js ${detectedRuntime === "node" ? chalk.default.green("(detected)") : ""}`,
1752
+ value: "node"
1753
+ },
1754
+ {
1755
+ name: `Bun ${detectedRuntime === "bun" ? chalk.default.green("(detected)") : chalk.default.gray("(faster)")}`,
1756
+ value: "bun"
1757
+ },
1758
+ {
1759
+ name: `Deno ${detectedRuntime === "deno" ? chalk.default.green("(detected)") : ""}`,
1760
+ value: "deno"
1761
+ }
1762
+ ],
1763
+ default: detectedRuntime
1764
+ });
1765
+ questions.push({
1766
+ type: "password",
1767
+ name: "authToken",
1768
+ message: `Auth token: ${chalk.default.gray("(blank = auto-generate)")}`,
1769
+ mask: "?"
1770
+ });
1771
+ const answers = await inquirer.default.prompt(questions);
1772
+ const token = answers.authToken || this.gateway.generateToken();
1773
+ if (!answers.authToken) console.log(chalk.default.green(" ✔ Auth token auto-generated\n"));
1774
+ const { wantSshTunnel } = await inquirer.default.prompt([{
1775
+ type: "confirm",
1776
+ name: "wantSshTunnel",
1777
+ message: `SSH reverse tunnel for remote access? ${chalk.default.gray("(alternative to Tailscale)")}`,
1778
+ default: false
1779
+ }]);
1780
+ let sshTunnel;
1781
+ if (wantSshTunnel) {
1782
+ const sshAns = await inquirer.default.prompt([
1783
+ {
1784
+ type: "input",
1785
+ name: "host",
1786
+ message: " Remote SSH host (e.g. myserver.com):",
1787
+ validate: (v) => v.trim().length > 3 || "Required"
1788
+ },
1789
+ {
1790
+ type: "input",
1791
+ name: "user",
1792
+ message: " SSH user:",
1793
+ default: process.env.USER || process.env.USERNAME || "ubuntu"
1794
+ },
1795
+ {
1796
+ type: "input",
1797
+ name: "remotePort",
1798
+ message: " Remote port (forwarded on server):",
1799
+ default: String(Number(answers.port) || 18789),
1800
+ validate: (v) => Number(v) > 1024 && Number(v) < 65535 ? true : "Valid port required"
1801
+ }
1802
+ ]);
1803
+ sshTunnel = {
1804
+ enabled: true,
1805
+ host: sshAns.host.trim(),
1806
+ user: sshAns.user.trim(),
1807
+ remotePort: Number(sshAns.remotePort)
1808
+ };
1809
+ console.log(chalk.default.green(` ✔ SSH tunnel: ${sshTunnel.user}@${sshTunnel.host}:${sshTunnel.remotePort}\n`));
1810
+ console.log(chalk.default.gray(` Run: ssh -R ${sshTunnel.remotePort}:localhost:${Number(answers.port)} ${sshTunnel.user}@${sshTunnel.host}\n`));
1811
+ }
1812
+ return {
1813
+ port: Number(answers.port),
1814
+ bind: answers.bind || "127.0.0.1",
1815
+ authToken: token,
1816
+ tailscaleExposure: answers.tailscaleExposure || "off",
1817
+ runtime: answers.runtime || detectedRuntime,
1818
+ enabledChannels: [],
1819
+ hooks: true,
1820
+ ...sshTunnel ? { sshTunnel } : {}
1821
+ };
1822
+ }
1823
+ async selectChannels(full = false) {
1824
+ console.log(chalk.default.hex("#06b6d4")("\n 📱 Communication Channels\n"));
1825
+ const available = await getAvailableChannels();
1826
+ const { selectedChannels } = await inquirer.default.prompt([{
1827
+ type: "checkbox",
1828
+ name: "selectedChannels",
1829
+ message: "Enable channels:",
1830
+ choices: available.map((ch) => {
1831
+ const isUnavailable = ch.status === "unavailable";
1832
+ const reason = isUnavailable ? unavailableReason(ch) : "";
1833
+ const icon$1 = brandIcon(ch.id, "channel");
1834
+ return {
1835
+ name: `${icon$1} ${ch.name.padEnd(18)} ${statusBadge(ch.status, ch)}${ch.requiresGateway && !isUnavailable ? chalk.default.gray(" [needs gateway]") : ""}`,
1836
+ value: ch.id,
1837
+ checked: ch.status === "recommended",
1838
+ disabled: isUnavailable ? reason : false
1839
+ };
1840
+ })
1841
+ }]);
1842
+ const channelConfigs = {};
1843
+ for (const channelId of selectedChannels) {
1844
+ const ch = CHANNELS.find((c) => c.id === channelId);
1845
+ if (!ch || !ch.tokenLabel) continue;
1846
+ console.log(chalk.default.hex("#06b6d4")(`\n ${ch.emoji} ${ch.name}`));
1847
+ if (ch.setupSteps?.length) ch.setupSteps.forEach((s) => console.log(chalk.default.gray(` ${s}`)));
1848
+ else if (ch.tokenHint) console.log(chalk.default.gray(` 💡 ${ch.tokenHint}`));
1849
+ const fields = [{
1850
+ type: "password",
1851
+ name: "token",
1852
+ message: `${ch.tokenLabel}:`,
1853
+ mask: "?"
1854
+ }];
1855
+ for (const f of ch.extraFields || []) fields.push({
1856
+ type: "input",
1857
+ name: f.name,
1858
+ message: `${f.label}:${f.hint ? chalk.default.gray(` (${f.hint})`) : ""}`,
1859
+ ...f.required ? { validate: (v) => v.trim().length > 0 || `${f.label} is required` } : {}
1860
+ });
1861
+ channelConfigs[channelId] = await inquirer.default.prompt(fields);
1862
+ }
1863
+ if (selectedChannels.includes("twitch") && channelConfigs["twitch"]?.channels) {
1864
+ const raw = channelConfigs["twitch"].channels;
1865
+ channelConfigs["twitch"].channels = raw.split(",").map((s) => s.trim().replace(/^#/, "").toLowerCase()).filter(Boolean);
1866
+ }
1867
+ if (selectedChannels.includes("email")) {
1868
+ const { wantGmailOAuth } = await inquirer.default.prompt([{
1869
+ type: "confirm",
1870
+ name: "wantGmailOAuth",
1871
+ message: "Enable Gmail OAuth for Pub/Sub real-time (send via hyperclaw gmail watch-setup)?",
1872
+ default: false
1873
+ }]);
1874
+ if (wantGmailOAuth) {
1875
+ console.log(chalk.default.gray(" Running OAuth flow for google-gmail..."));
1876
+ try {
1877
+ const { runOAuthFlow } = await Promise.resolve().then(() => require("./oauth-flow-BbLQTzZk.js"));
1878
+ const { writeOAuthToken } = await Promise.resolve().then(() => require("./oauth-provider-UZyG84s7.js"));
1879
+ const tokens = await runOAuthFlow("google-gmail", {});
1880
+ const now = Math.floor(Date.now() / 1e3);
1881
+ const expires_at = tokens.expires_in ? now + tokens.expires_in : void 0;
1882
+ await writeOAuthToken("google-gmail", {
1883
+ access_token: tokens.access_token,
1884
+ refresh_token: tokens.refresh_token,
1885
+ expires_at,
1886
+ token_url: "https://oauth2.googleapis.com/token"
1887
+ });
1888
+ console.log(chalk.default.green(" ✔ Gmail OAuth configured — next: hyperclaw gmail watch-setup"));
1889
+ } catch (e) {
1890
+ console.log(chalk.default.yellow(` ⚠ OAuth failed — you can run it later: hyperclaw auth oauth google-gmail`));
1891
+ }
1892
+ }
1893
+ }
1894
+ return channelConfigs;
1895
+ }
1896
+ async configureWebSearch(skip = false) {
1897
+ if (skip) return null;
1898
+ const { wantSearch } = await inquirer.default.prompt([{
1899
+ type: "confirm",
1900
+ name: "wantSearch",
1901
+ message: "Configure a web search provider for the agent?",
1902
+ default: false
1903
+ }]);
1904
+ if (!wantSearch) return null;
1905
+ const SEARCH_PROVIDERS = [
1906
+ {
1907
+ id: "tavily",
1908
+ name: "Tavily",
1909
+ hint: "app.tavily.com — best for agents",
1910
+ prefix: "tvly-"
1911
+ },
1912
+ {
1913
+ id: "perplexity",
1914
+ name: "Perplexity",
1915
+ hint: "perplexity.ai/settings/api",
1916
+ prefix: "pplx-"
1917
+ },
1918
+ {
1919
+ id: "brave",
1920
+ name: "Brave Search",
1921
+ hint: "api.search.brave.com",
1922
+ prefix: "BSA"
1923
+ },
1924
+ {
1925
+ id: "gemini",
1926
+ name: "Gemini",
1927
+ hint: "aistudio.google.com/app/apikey",
1928
+ prefix: "AIza"
1929
+ },
1930
+ {
1931
+ id: "grok",
1932
+ name: "Grok/xAI",
1933
+ hint: "console.x.ai — same key as xAI provider",
1934
+ prefix: "xai-"
1935
+ },
1936
+ {
1937
+ id: "kimi",
1938
+ name: "Kimi (Moonshot)",
1939
+ hint: "platform.moonshot.cn/user-center/api-keys",
1940
+ prefix: "sk-"
1941
+ }
1942
+ ];
1943
+ const { searchProvider } = await inquirer.default.prompt([{
1944
+ type: "list",
1945
+ name: "searchProvider",
1946
+ message: "Select web search provider:",
1947
+ choices: SEARCH_PROVIDERS.map((p) => ({
1948
+ name: `${p.name.padEnd(14)} ${chalk.default.gray(p.hint)}`,
1949
+ value: p.id
1950
+ }))
1951
+ }]);
1952
+ const chosen = SEARCH_PROVIDERS.find((p) => p.id === searchProvider);
1953
+ const { searchKey } = await inquirer.default.prompt([{
1954
+ type: "password",
1955
+ name: "searchKey",
1956
+ message: `${chosen.name} API key (starts with ${chalk.default.gray(chosen.prefix)}):`,
1957
+ mask: "?",
1958
+ validate: (v) => v.trim().length > 4 || "Required"
1959
+ }]);
1960
+ console.log(chalk.default.green(` ✔ Web search: ${chosen.name}`));
1961
+ return {
1962
+ provider: searchProvider,
1963
+ apiKey: searchKey.trim()
1964
+ };
1965
+ }
1966
+ async configureServiceApiKeys() {
1967
+ console.log(chalk.default.hex("#06b6d4")("\n 🔑 Service API Keys — any app with an API key\n"));
1968
+ console.log(chalk.default.gray(" Stored securely in config. How they work:\n"));
1969
+ console.log(chalk.default.gray(" ● Wizard: add keys here\n"));
1970
+ console.log(chalk.default.gray(" ● Config: ~/.hyperclaw/hyperclaw.json > skills.apiKeys\n"));
1971
+ console.log(chalk.default.gray(" ● Env: HACKERONE_*, BUGCROWD_*, SYNACK_*, or CUSTOM_ID_API_KEY\n"));
1972
+ console.log(chalk.default.gray(" ● Tools: built-in tools read them automatically for research.\n"));
1973
+ const { wantServiceKeys } = await inquirer.default.prompt([{
1974
+ type: "confirm",
1975
+ name: "wantServiceKeys",
1976
+ message: "Add API keys for external services (HackerOne, Bugcrowd, Synack, or custom)?",
1977
+ default: false
1978
+ }]);
1979
+ if (!wantServiceKeys) return {};
1980
+ const KNOWN_SERVICES = [
1981
+ {
1982
+ id: "hackerone",
1983
+ name: "HackerOne",
1984
+ hint: "username:token (Basic auth)"
1985
+ },
1986
+ {
1987
+ id: "bugcrowd",
1988
+ name: "Bugcrowd",
1989
+ hint: "Token from Bugcrowd API Credentials"
1990
+ },
1991
+ {
1992
+ id: "synack",
1993
+ name: "Synack",
1994
+ hint: "API token from Synack"
1995
+ },
1996
+ {
1997
+ id: "__custom__",
1998
+ name: "Other (custom)",
1999
+ hint: "Any app with an API key"
2000
+ }
2001
+ ];
2002
+ const { servicesToAdd } = await inquirer.default.prompt([{
2003
+ type: "checkbox",
2004
+ name: "servicesToAdd",
2005
+ message: "Select services:",
2006
+ choices: KNOWN_SERVICES.filter((s) => s.id !== "__custom__").map((s) => ({
2007
+ name: `${s.name} ${chalk.default.gray(`(${s.hint})`)}`,
2008
+ value: s.id
2009
+ })),
2010
+ validate: (v) => true
2011
+ }]);
2012
+ const apiKeys = {};
2013
+ for (const sid of servicesToAdd || []) {
2014
+ const svc = KNOWN_SERVICES.find((s) => s.id === sid);
2015
+ const r = await inquirer.default.prompt([{
2016
+ type: "password",
2017
+ name: "key",
2018
+ message: `${svc?.name || sid} API key:`,
2019
+ mask: "?",
2020
+ validate: (v) => v.trim().length > 3 || "Required"
2021
+ }]);
2022
+ apiKeys[sid] = r.key.trim();
2023
+ }
2024
+ const { addCustom } = await inquirer.default.prompt([{
2025
+ type: "confirm",
2026
+ name: "addCustom",
2027
+ message: "Add a custom service (any app)?",
2028
+ default: false
2029
+ }]);
2030
+ if (addCustom) {
2031
+ const { customId, customKey } = await inquirer.default.prompt([{
2032
+ type: "input",
2033
+ name: "customId",
2034
+ message: "Service ID (e.g. my-app, ads-power):",
2035
+ validate: (v) => /^[a-z0-9_-]+$/i.test(v?.trim() || "") || "Letters, numbers, - _ only"
2036
+ }, {
2037
+ type: "password",
2038
+ name: "customKey",
2039
+ message: "API key:",
2040
+ mask: "?",
2041
+ validate: (v) => v.trim().length > 3 || "Required"
2042
+ }]);
2043
+ apiKeys[customId.trim().toLowerCase()] = customKey.trim();
2044
+ }
2045
+ if (Object.keys(apiKeys).length > 0) console.log(chalk.default.green(` ✔ Saved ${Object.keys(apiKeys).length} API key(s)`));
2046
+ return apiKeys;
2047
+ }
2048
+ async configureHyperClawBot(gatewayConfig, existingChannelConfigs) {
2049
+ console.log(chalk.default.hex("#06b6d4")("\n 🤖 HyperClaw Bot — Remote control via Telegram\n"));
2050
+ const trans = require_providers.getTranscriptionProviders();
2051
+ if (trans.length > 0) {
2052
+ console.log(chalk.default.gray(" 🎙 Voice notes: " + trans.map((p) => `${p.displayName}`).join(", ") + " — if you have their API key, voice messages will be transcribed to text."));
2053
+ console.log();
2054
+ }
2055
+ const { wantHyperClawBot } = await inquirer.default.prompt([{
2056
+ type: "confirm",
2057
+ name: "wantHyperClawBot",
2058
+ message: "Enable HyperClaw Bot for remote control (status, restart, /agent from mobile)?",
2059
+ default: false
2060
+ }]);
2061
+ if (!wantHyperClawBot) return void 0;
2062
+ const existingTelegramToken = existingChannelConfigs?.["telegram"]?.token;
2063
+ let token;
2064
+ if (existingTelegramToken) {
2065
+ const { reuseToken } = await inquirer.default.prompt([{
2066
+ type: "confirm",
2067
+ name: "reuseToken",
2068
+ message: "Use the same Telegram bot you already configured above?",
2069
+ default: true
2070
+ }]);
2071
+ if (reuseToken) {
2072
+ token = existingTelegramToken;
2073
+ console.log(chalk.default.green(" ✔ Reusing existing Telegram bot token\n"));
2074
+ } else {
2075
+ const r = await inquirer.default.prompt([{
2076
+ type: "input",
2077
+ name: "token",
2078
+ message: "Telegram Bot token (from @BotFather):",
2079
+ validate: (v) => v.trim().length > 10 || "Required"
2080
+ }]);
2081
+ token = r.token.trim();
2082
+ }
2083
+ } else {
2084
+ const r = await inquirer.default.prompt([{
2085
+ type: "input",
2086
+ name: "token",
2087
+ message: "Telegram Bot token (from @BotFather):",
2088
+ validate: (v) => v.trim().length > 10 || "Required"
2089
+ }]);
2090
+ token = r.token.trim();
2091
+ }
2092
+ const { userIds } = await inquirer.default.prompt([{
2093
+ type: "input",
2094
+ name: "userIds",
2095
+ message: "Allowed user IDs (comma-separated, leave empty = everyone):",
2096
+ default: ""
2097
+ }]);
2098
+ const gatewayUrl = `http://localhost:${gatewayConfig?.port || 18789}`;
2099
+ const { saveBotConfig } = await Promise.resolve().then(() => require("./hyperclawbot-DcQt62PL.js"));
2100
+ await saveBotConfig({
2101
+ platform: "telegram",
2102
+ token: token.trim(),
2103
+ gatewayUrl,
2104
+ allowedUsers: userIds ? userIds.split(",").map((s) => s.trim()).filter(Boolean) : [],
2105
+ gatewayToken: void 0,
2106
+ enabled: true,
2107
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
2108
+ });
2109
+ console.log(chalk.default.green(" ✔ HyperClaw Bot configured — Start: hyperclaw bot start"));
2110
+ return { token: token.trim() };
2111
+ }
2112
+ async configureTalkMode() {
2113
+ console.log(chalk.default.hex("#06b6d4")("\n 🎙 Talk Mode — ElevenLabs TTS\n"));
2114
+ const { wantTalkMode } = await inquirer.default.prompt([{
2115
+ type: "confirm",
2116
+ name: "wantTalkMode",
2117
+ message: "Enable Talk Mode (voice responses via ElevenLabs)?",
2118
+ default: false
2119
+ }]);
2120
+ if (!wantTalkMode) return void 0;
2121
+ const { apiKey } = await inquirer.default.prompt([{
2122
+ type: "password",
2123
+ name: "apiKey",
2124
+ message: "ElevenLabs API key (elevenlabs.io):",
2125
+ mask: "?",
2126
+ validate: (v) => v.trim().length > 10 || "Required"
2127
+ }]);
2128
+ console.log(chalk.default.green(" ✔ Talk Mode configured"));
2129
+ return {
2130
+ apiKey: apiKey.trim(),
2131
+ voiceId: "21m00Tcm4TlvDq8ikWAM",
2132
+ modelId: "eleven_multilingual_v2"
2133
+ };
2134
+ }
2135
+ async configureMemoryIntegration() {
2136
+ console.log(chalk.default.hex("#06b6d4")("\n 🧠 Memory Integration (Obsidian / Raycast / Hazel)\n"));
2137
+ const { vaultPath } = await inquirer.default.prompt([{
2138
+ type: "input",
2139
+ name: "vaultPath",
2140
+ message: "Sync memory to vault? Enter path or leave empty to skip:",
2141
+ default: ""
2142
+ }]);
2143
+ const vaultDir = String(vaultPath || "").trim();
2144
+ if (!vaultDir) return void 0;
2145
+ const { dailyNotes } = await inquirer.default.prompt([{
2146
+ type: "confirm",
2147
+ name: "dailyNotes",
2148
+ message: "Write daily notes with session summaries?",
2149
+ default: true
2150
+ }]);
2151
+ return {
2152
+ vaultDir,
2153
+ dailyNotes,
2154
+ syncOnAppend: true
2155
+ };
2156
+ }
2157
+ async configureIdentity() {
2158
+ console.log(chalk.default.hex("#06b6d4")("\n 🤖 Agent Identity\n"));
2159
+ const identity = await inquirer.default.prompt([
2160
+ {
2161
+ type: "input",
2162
+ name: "userName",
2163
+ message: "Your name:",
2164
+ default: "Boss"
2165
+ },
2166
+ {
2167
+ type: "input",
2168
+ name: "agentName",
2169
+ message: "Agent name:",
2170
+ default: "Hyper"
2171
+ },
2172
+ {
2173
+ type: "input",
2174
+ name: "wakeWord",
2175
+ message: "Wake word for voice:",
2176
+ default: (a) => `Hey ${a.agentName || "Hyper"}`
2177
+ },
2178
+ {
2179
+ type: "list",
2180
+ name: "personality",
2181
+ message: "Personality:",
2182
+ choices: [
2183
+ "Professional and concise",
2184
+ "Friendly and casual",
2185
+ "Witty with humor",
2186
+ "Direct and no-nonsense",
2187
+ "Custom"
2188
+ ]
2189
+ },
2190
+ {
2191
+ type: "input",
2192
+ name: "customPersonality",
2193
+ message: "Describe personality:",
2194
+ when: (a) => a.personality === "Custom"
2195
+ },
2196
+ {
2197
+ type: "list",
2198
+ name: "language",
2199
+ message: "Primary language:",
2200
+ choices: [
2201
+ "English",
2202
+ "Greek",
2203
+ "Spanish",
2204
+ "French",
2205
+ "German",
2206
+ "Chinese",
2207
+ "Japanese",
2208
+ "Arabic"
2209
+ ]
2210
+ }
2211
+ ]);
2212
+ console.log(chalk.default.gray("\n This is the greeting sent to your channels when the agent starts.\n"));
2213
+ const { wakeUpMessage } = await inquirer.default.prompt([{
2214
+ type: "input",
2215
+ name: "wakeUpMessage",
2216
+ message: "Agent wake-up message:",
2217
+ default: (identity.agentName || "Hyper") + " is online. How can I help you today?"
2218
+ }]);
2219
+ const { wantSystemPrompt } = await inquirer.default.prompt([{
2220
+ type: "confirm",
2221
+ name: "wantSystemPrompt",
2222
+ message: "Add a custom system prompt / instructions for the agent?",
2223
+ default: false
2224
+ }]);
2225
+ let systemPrompt = "";
2226
+ if (wantSystemPrompt) {
2227
+ console.log(chalk.default.gray(" Tip: describe role, restrictions, tone, specific knowledge, etc.\n"));
2228
+ const r = await inquirer.default.prompt([{
2229
+ type: "editor",
2230
+ name: "systemPrompt",
2231
+ message: "System prompt (opens editor — save & close to continue):"
2232
+ }]);
2233
+ systemPrompt = r.systemPrompt?.trim() || "";
2234
+ }
2235
+ const globalRules = [
2236
+ "Always respond in the user's preferred language",
2237
+ "Never reveal the gateway auth token or API keys",
2238
+ "Confirm before destructive or irreversible actions",
2239
+ "Respect user privacy — never share conversation data",
2240
+ "All subagents inherit these rules — cannot be overridden"
2241
+ ];
2242
+ const { addRules } = await inquirer.default.prompt([{
2243
+ type: "confirm",
2244
+ name: "addRules",
2245
+ message: "Add custom rules to AGENTS.md?",
2246
+ default: false
2247
+ }]);
2248
+ let customRules = [];
2249
+ if (addRules) {
2250
+ const { rules } = await inquirer.default.prompt([{
2251
+ type: "input",
2252
+ name: "rules",
2253
+ message: "Rules (semicolon-separated):",
2254
+ filter: (v) => v.split(";").map((r) => r.trim()).filter(Boolean)
2255
+ }]);
2256
+ customRules = rules;
2257
+ }
2258
+ const personality = identity.personality === "Custom" ? identity.customPersonality || "Custom" : identity.personality;
2259
+ return {
2260
+ agentName: identity.agentName,
2261
+ userName: identity.userName,
2262
+ language: identity.language,
2263
+ personality,
2264
+ wakeUpMessage: wakeUpMessage.trim(),
2265
+ systemPrompt: systemPrompt || void 0,
2266
+ rules: [...globalRules, ...customRules]
2267
+ };
2268
+ }
2269
+ async configureSkillsAndHooks() {
2270
+ const { wantSkills } = await inquirer.default.prompt([{
2271
+ type: "confirm",
2272
+ name: "wantSkills",
2273
+ message: "Configure skills & hooks now?",
2274
+ default: false
2275
+ }]);
2276
+ if (!wantSkills) {
2277
+ console.log(chalk.default.gray(" ● Skipped — enable later: hyperclaw hooks enable <name>\n"));
2278
+ return {
2279
+ hooks: [],
2280
+ heartbeat: false
2281
+ };
2282
+ }
2283
+ const { selectedHooks } = await inquirer.default.prompt([{
2284
+ type: "checkbox",
2285
+ name: "selectedHooks",
2286
+ message: "Select hooks to enable:",
2287
+ choices: [
2288
+ {
2289
+ name: `${"boot.md".padEnd(22)} ${chalk.default.gray("Run commands on agent startup")}`,
2290
+ value: "boot"
2291
+ },
2292
+ {
2293
+ name: `${"command-logger".padEnd(22)} ${chalk.default.gray("Log every tool call to file")}`,
2294
+ value: "command-logger"
2295
+ },
2296
+ {
2297
+ name: `${"session-memory".padEnd(22)} ${chalk.default.gray("Persist session context across restarts")}`,
2298
+ value: "session-memory"
2299
+ },
2300
+ {
2301
+ name: `${"morning-briefing".padEnd(22)} ${chalk.default.gray("Daily proactive summary to HEARTBEAT.md")}`,
2302
+ value: "morning-briefing"
2303
+ }
2304
+ ]
2305
+ }]);
2306
+ const heartbeat = selectedHooks.includes("morning-briefing");
2307
+ try {
2308
+ const { HookLoader } = await Promise.resolve().then(() => require("./loader-Dq_cDlOW.js"));
2309
+ const loader = new HookLoader();
2310
+ for (const h of selectedHooks) loader.enable(h);
2311
+ } catch {}
2312
+ if (selectedHooks.length > 0) console.log(chalk.default.green(` ✔ Enabled: ${selectedHooks.join(", ")}\n`));
2313
+ return {
2314
+ hooks: selectedHooks,
2315
+ heartbeat
2316
+ };
2317
+ }
2318
+ async configurePcAccess() {
2319
+ console.log(chalk.default.hex("#06b6d4")("\n 💻 PC Access Level\n"));
2320
+ console.log(chalk.default.gray(" Controls what the agent can do on your computer.\n"));
2321
+ const { level } = await inquirer.default.prompt([{
2322
+ type: "list",
2323
+ name: "level",
2324
+ message: "PC access level:",
2325
+ choices: [
2326
+ {
2327
+ name: `Full ${chalk.default.gray("(bash, file read/write, screenshots — recommended for power users)")}`,
2328
+ value: "full"
2329
+ },
2330
+ {
2331
+ name: `Sandboxed ${chalk.default.gray("(read files, limited shell, no destructive writes)")}`,
2332
+ value: "sandboxed"
2333
+ },
2334
+ {
2335
+ name: `Read-only ${chalk.default.gray("(read files only — safest)")}`,
2336
+ value: "read-only"
2337
+ }
2338
+ ],
2339
+ default: "full"
2340
+ }]);
2341
+ const { confirmDestructive } = await inquirer.default.prompt([{
2342
+ type: "confirm",
2343
+ name: "confirmDestructive",
2344
+ message: "Require confirmation before destructive actions (delete files, overwrite)?",
2345
+ default: level !== "full"
2346
+ }]);
2347
+ console.log(chalk.default.green(` ✔ PC access: ${level}${confirmDestructive ? " + confirm destructive" : ""}\n`));
2348
+ return {
2349
+ level,
2350
+ confirmDestructive
2351
+ };
2352
+ }
2353
+ async configureSkillHub() {
2354
+ console.log(chalk.default.hex("#06b6d4")("\n 🛒 Skill Hub (ClawHub)\n"));
2355
+ console.log(chalk.default.gray(" Install skills from the ClawHub marketplace (AI tools, workflows, integrations).\n"));
2356
+ const { wantSkills } = await inquirer.default.prompt([{
2357
+ type: "confirm",
2358
+ name: "wantSkills",
2359
+ message: "Install skills from the Skill Hub?",
2360
+ default: false
2361
+ }]);
2362
+ if (!wantSkills) return;
2363
+ try {
2364
+ const { SkillHub } = await Promise.resolve().then(() => require("./hub-BJM2KXLO.js"));
2365
+ const hub = new SkillHub();
2366
+ const FEATURED = [
2367
+ {
2368
+ name: `web-search ${chalk.default.gray("(Google/Bing/DuckDuckGo search)")}`,
2369
+ value: "web-search"
2370
+ },
2371
+ {
2372
+ name: `file-manager ${chalk.default.gray("(read, write, list files)")}`,
2373
+ value: "file-manager"
2374
+ },
2375
+ {
2376
+ name: `code-runner ${chalk.default.gray("(run Python/JS snippets safely)")}`,
2377
+ value: "code-runner"
2378
+ },
2379
+ {
2380
+ name: `github-tools ${chalk.default.gray("(repos, issues, PRs via API)")}`,
2381
+ value: "github-tools"
2382
+ },
2383
+ {
2384
+ name: `calendar-tools ${chalk.default.gray("(Google Calendar read/write)")}`,
2385
+ value: "calendar-tools"
2386
+ },
2387
+ {
2388
+ name: `summarizer ${chalk.default.gray("(summarize URLs, PDFs, text)")}`,
2389
+ value: "summarizer"
2390
+ },
2391
+ {
2392
+ name: `custom ${chalk.default.gray("(enter skill ID manually)")}`,
2393
+ value: "__custom__"
2394
+ }
2395
+ ];
2396
+ const { selectedSkills } = await inquirer.default.prompt([{
2397
+ type: "checkbox",
2398
+ name: "selectedSkills",
2399
+ message: "Select skills to install:",
2400
+ choices: FEATURED
2401
+ }]);
2402
+ for (const skillId of selectedSkills) if (skillId === "__custom__") {
2403
+ const { customId } = await inquirer.default.prompt([{
2404
+ type: "input",
2405
+ name: "customId",
2406
+ message: " Skill ID from ClawHub:",
2407
+ validate: (v) => v.trim().length > 0 || "Required"
2408
+ }]);
2409
+ if (customId.trim()) {
2410
+ const s = (0, ora.default)(`Installing ${customId.trim()}...`).start();
2411
+ try {
2412
+ await hub.install(customId.trim(), false);
2413
+ s.succeed(`Installed: ${customId.trim()}`);
2414
+ } catch (e) {
2415
+ s.fail(e.message);
2416
+ }
2417
+ }
2418
+ } else {
2419
+ const s = (0, ora.default)(`Installing ${skillId}...`).start();
2420
+ try {
2421
+ await hub.install(skillId, false);
2422
+ s.succeed(`Installed: ${skillId}`);
2423
+ } catch (e) {
2424
+ s.warn(`${skillId}: ${e.message} (install later: hyperclaw skill install ${skillId})`);
2425
+ }
2426
+ }
2427
+ if (selectedSkills.length > 0) console.log();
2428
+ } catch {
2429
+ console.log(chalk.default.yellow(` ⚠ Skill Hub unavailable — install later: hyperclaw hub\n`));
2430
+ }
2431
+ }
2432
+ async configureRateLimiting() {
2433
+ console.log(chalk.default.hex("#06b6d4")("\n ⏱ Rate Limiting\n"));
2434
+ console.log(chalk.default.gray(" Limit how many messages the agent processes per channel per minute/hour.\n"));
2435
+ const { wantRateLimit } = await inquirer.default.prompt([{
2436
+ type: "confirm",
2437
+ name: "wantRateLimit",
2438
+ message: "Configure rate limits?",
2439
+ default: false
2440
+ }]);
2441
+ if (!wantRateLimit) return void 0;
2442
+ const { maxPerMinute, maxPerHour } = await inquirer.default.prompt([{
2443
+ type: "input",
2444
+ name: "maxPerMinute",
2445
+ message: " Max messages per minute per user (0 = unlimited):",
2446
+ default: "0",
2447
+ validate: (v) => !isNaN(Number(v)) || "Enter a number"
2448
+ }, {
2449
+ type: "input",
2450
+ name: "maxPerHour",
2451
+ message: " Max messages per hour per user (0 = unlimited):",
2452
+ default: "0",
2453
+ validate: (v) => !isNaN(Number(v)) || "Enter a number"
2454
+ }]);
2455
+ const mpm = Number(maxPerMinute);
2456
+ const mph = Number(maxPerHour);
2457
+ if (mpm === 0 && mph === 0) return void 0;
2458
+ console.log(chalk.default.green(` ✔ Rate limit: ${mpm > 0 ? `${mpm}/min` : ""}${mpm > 0 && mph > 0 ? ", " : ""}${mph > 0 ? `${mph}/hr` : ""}\n`));
2459
+ return {
2460
+ ...mpm > 0 ? { maxPerMinute: mpm } : {},
2461
+ ...mph > 0 ? { maxPerHour: mph } : {}
2462
+ };
2463
+ }
2464
+ async configureDeveloperKey() {
2465
+ console.log(chalk.default.hex("#06b6d4")("\n 🔑 Developer API Key\n"));
2466
+ console.log(chalk.default.gray(" Create a key for embedding HyperClaw in apps or managed hosting.\n"));
2467
+ const { wantDevKey } = await inquirer.default.prompt([{
2468
+ type: "confirm",
2469
+ name: "wantDevKey",
2470
+ message: "Create a developer API key?",
2471
+ default: false
2472
+ }]);
2473
+ if (!wantDevKey) return;
2474
+ try {
2475
+ const developerKeys = await Promise.resolve().then(() => require("./developer-keys-CzDxVczE.js"));
2476
+ const { name } = await inquirer.default.prompt([{
2477
+ type: "input",
2478
+ name: "name",
2479
+ message: " Key name:",
2480
+ default: "default"
2481
+ }]);
2482
+ const { id, key } = await developerKeys.createDeveloperKey(name.trim() || "default");
2483
+ console.log(chalk.default.green(` ✔ Developer key created`));
2484
+ console.log(chalk.default.gray(` ID: ${id}`));
2485
+ console.log(chalk.default.yellow(` Key: ${key}`));
2486
+ console.log(chalk.default.gray(` Store securely — shown once. Use: Authorization: Bearer <key>\n`));
2487
+ } catch {
2488
+ console.log(chalk.default.yellow(` ⚠ Could not create key — run: hyperclaw developer-key create\n`));
2489
+ }
2490
+ }
2491
+ async configureVoiceCall() {
2492
+ console.log(chalk.default.hex("#06b6d4")("\n 📞 Voice Call\n"));
2493
+ console.log(chalk.default.gray(" Terminal voice call mode — speaks directly to the gateway.\n"));
2494
+ const { wantVoiceCall } = await inquirer.default.prompt([{
2495
+ type: "confirm",
2496
+ name: "wantVoiceCall",
2497
+ message: "Configure voice call settings?",
2498
+ default: false
2499
+ }]);
2500
+ if (!wantVoiceCall) return;
2501
+ const { gatewayUrl } = await inquirer.default.prompt([{
2502
+ type: "input",
2503
+ name: "gatewayUrl",
2504
+ message: " Gateway URL for voice calls:",
2505
+ default: "http://localhost:18789"
2506
+ }]);
2507
+ const cfg = await this.config.load();
2508
+ await this.config.patch({ channelConfigs: {
2509
+ ...cfg.channelConfigs || {},
2510
+ "voice-call": { gatewayUrl: gatewayUrl.trim() }
2511
+ } });
2512
+ console.log(chalk.default.green(` ✔ Voice call: ${gatewayUrl.trim()}\n`));
2513
+ console.log(chalk.default.gray(` Start: hyperclaw voice-call --gateway-url ${gatewayUrl.trim()}\n`));
2514
+ }
2515
+ async configureCanvas() {
2516
+ console.log(chalk.default.hex("#06b6d4")("\n 🎨 Canvas (AI-driven UI)\n"));
2517
+ console.log(chalk.default.gray(" Live canvas for displaying AI-generated cards, charts, and components.\n"));
2518
+ const { wantCanvas } = await inquirer.default.prompt([{
2519
+ type: "confirm",
2520
+ name: "wantCanvas",
2521
+ message: "Enable canvas features?",
2522
+ default: false
2523
+ }]);
2524
+ if (!wantCanvas) return;
2525
+ const { canvasMode } = await inquirer.default.prompt([{
2526
+ type: "list",
2527
+ name: "canvasMode",
2528
+ message: " Default canvas mode:",
2529
+ choices: [
2530
+ {
2531
+ name: `Auto ${chalk.default.gray("(show canvas when AI generates structured output)")}`,
2532
+ value: "auto"
2533
+ },
2534
+ {
2535
+ name: `Always ${chalk.default.gray("(always show canvas panel)")}`,
2536
+ value: "always"
2537
+ },
2538
+ {
2539
+ name: `Manual ${chalk.default.gray("(show only via hyperclaw canvas show)")}`,
2540
+ value: "manual"
2541
+ }
2542
+ ],
2543
+ default: "auto"
2544
+ }]);
2545
+ await this.config.patch({ channelConfigs: {
2546
+ ...(await this.config.load()).channelConfigs || {},
2547
+ canvas: { mode: canvasMode }
2548
+ } });
2549
+ console.log(chalk.default.green(` ✔ Canvas mode: ${canvasMode}\n`));
2550
+ }
2551
+ async configureDeploy() {
2552
+ console.log(chalk.default.hex("#06b6d4")("\n ☁ Cloud Deploy\n"));
2553
+ console.log(chalk.default.gray(" Deploy the gateway to a cloud platform (Fly.io or Render).\n"));
2554
+ const { wantDeploy } = await inquirer.default.prompt([{
2555
+ type: "confirm",
2556
+ name: "wantDeploy",
2557
+ message: "Set up cloud deployment?",
2558
+ default: false
2559
+ }]);
2560
+ if (!wantDeploy) return;
2561
+ const { platform } = await inquirer.default.prompt([{
2562
+ type: "list",
2563
+ name: "platform",
2564
+ message: " Platform:",
2565
+ choices: [{
2566
+ name: `Fly.io ${chalk.default.gray("(recommended — fast global edge)")}`,
2567
+ value: "fly"
2568
+ }, {
2569
+ name: `Render ${chalk.default.gray("(free tier available — GitHub integration)")}`,
2570
+ value: "render"
2571
+ }]
2572
+ }]);
2573
+ if (platform === "fly") {
2574
+ console.log(chalk.default.gray("\n Fly.io deployment steps:"));
2575
+ console.log(chalk.default.gray(" 1. Install: curl -L https://fly.io/install.sh | sh"));
2576
+ console.log(chalk.default.gray(" 2. Login: fly auth login"));
2577
+ console.log(chalk.default.gray(" 3. Launch: fly launch"));
2578
+ console.log(chalk.default.gray(" 4. Secrets: fly secrets set ANTHROPIC_API_KEY=... HYPERCLAW_GATEWAY_TOKEN=..."));
2579
+ console.log(chalk.default.gray(" 5. Deploy: fly deploy"));
2580
+ console.log(chalk.default.gray("\n Or: hyperclaw deploy --platform fly\n"));
2581
+ } else {
2582
+ console.log(chalk.default.gray("\n Render deployment steps:"));
2583
+ console.log(chalk.default.gray(" 1. Push to GitHub"));
2584
+ console.log(chalk.default.gray(" 2. Connect at https://render.com > New Web Service > select repo"));
2585
+ console.log(chalk.default.gray(" 3. Set env: ANTHROPIC_API_KEY, HYPERCLAW_GATEWAY_TOKEN"));
2586
+ console.log(chalk.default.gray("\n Or: hyperclaw deploy --platform render\n"));
2587
+ }
2588
+ await this.config.patch({ channelConfigs: {
2589
+ ...(await this.config.load()).channelConfigs || {},
2590
+ deploy: { platform }
2591
+ } });
2592
+ console.log(chalk.default.green(` ✔ Deploy target saved: ${platform}\n`));
2593
+ }
2594
+ async configureMcpServers() {
2595
+ console.log(chalk.default.hex("#06b6d4")("\n 🔌 MCP Servers (Model Context Protocol)\n"));
2596
+ console.log(chalk.default.gray(" Register external tool servers the agent can call (filesystem, browser, APIs).\n"));
2597
+ const { wantMcp } = await inquirer.default.prompt([{
2598
+ type: "confirm",
2599
+ name: "wantMcp",
2600
+ message: "Add MCP servers now?",
2601
+ default: false
2602
+ }]);
2603
+ if (!wantMcp) return;
2604
+ const { mcpAdd } = await Promise.resolve().then(() => require("./mcp-SsMEvl28.js"));
2605
+ let addMore = true;
2606
+ while (addMore) {
2607
+ await mcpAdd();
2608
+ const { more } = await inquirer.default.prompt([{
2609
+ type: "confirm",
2610
+ name: "more",
2611
+ message: " Add another MCP server?",
2612
+ default: false
2613
+ }]);
2614
+ addMore = more;
2615
+ }
2616
+ }
2617
+ async configureWorkspaceTemplate() {
2618
+ console.log(chalk.default.hex("#06b6d4")("\n 📁 Workspace Template\n"));
2619
+ console.log(chalk.default.gray(" Initialize workspace files (SOUL.md, USER.md, TOOLS.md, HEARTBEAT.md).\n"));
2620
+ const { wantTemplate } = await inquirer.default.prompt([{
2621
+ type: "confirm",
2622
+ name: "wantTemplate",
2623
+ message: "Initialize workspace template files now?",
2624
+ default: false
2625
+ }]);
2626
+ if (!wantTemplate) return;
2627
+ const { selectedFiles } = await inquirer.default.prompt([{
2628
+ type: "checkbox",
2629
+ name: "selectedFiles",
2630
+ message: "Select template files to create:",
2631
+ choices: [
2632
+ {
2633
+ name: `SOUL.md ${chalk.default.gray("Agent core values, purpose & boundaries")}`,
2634
+ value: "SOUL",
2635
+ checked: true
2636
+ },
2637
+ {
2638
+ name: `USER.md ${chalk.default.gray("User profile, preferences & context")}`,
2639
+ value: "USER",
2640
+ checked: true
2641
+ },
2642
+ {
2643
+ name: `TOOLS.md ${chalk.default.gray("Tool inventory & usage guidelines")}`,
2644
+ value: "TOOLS",
2645
+ checked: false
2646
+ },
2647
+ {
2648
+ name: `HEARTBEAT.md ${chalk.default.gray("Daily status / morning briefing target")}`,
2649
+ value: "HEARTBEAT",
2650
+ checked: false
2651
+ },
2652
+ {
2653
+ name: `AGENTS.md ${chalk.default.gray("Rules & subagent configuration")}`,
2654
+ value: "AGENTS",
2655
+ checked: false
2656
+ }
2657
+ ]
2658
+ }]);
2659
+ if (selectedFiles.length > 0) try {
2660
+ const { initWorkspaceFiles } = await Promise.resolve().then(() => require("./memory-BGgCBSx1.js"));
2661
+ const { getHyperClawDir: getHyperClawDir$1 } = await Promise.resolve().then(() => require("./paths-D-QecARF.js"));
2662
+ const path$3 = (await import("path")).default;
2663
+ const targetDir = getHyperClawDir$1();
2664
+ await initWorkspaceFiles({
2665
+ agentName: "Hyper",
2666
+ personality: "helpful and concise",
2667
+ language: "English",
2668
+ userName: "User",
2669
+ rules: []
2670
+ }, targetDir, selectedFiles);
2671
+ console.log(chalk.default.green(` ✔ Workspace files initialized in ${targetDir}\n`));
2672
+ } catch {
2673
+ console.log(chalk.default.yellow(` ⚠ Could not initialize workspace — run: hyperclaw workspace init\n`));
2674
+ }
2675
+ }
2676
+ async configureCronTasks() {
2677
+ console.log(chalk.default.hex("#06b6d4")("\n ⏰ Scheduled Tasks (Cron)\n"));
2678
+ console.log(chalk.default.gray(" Schedule recurring agent prompts (e.g. daily briefing, weekly report).\n"));
2679
+ const PRESETS = [
2680
+ {
2681
+ name: `Morning briefing ${chalk.default.gray("(Mon-Fri 9am)")}`,
2682
+ schedule: "0 9 * * 1-5",
2683
+ prompt: "Give me a morning briefing: check calendar, news, tasks."
2684
+ },
2685
+ {
2686
+ name: `Daily summary ${chalk.default.gray("(daily 6pm)")}`,
2687
+ schedule: "0 18 * * *",
2688
+ prompt: "Summarize today's activity and pending tasks."
2689
+ },
2690
+ {
2691
+ name: `Weekly report ${chalk.default.gray("(Mon 8am)")}`,
2692
+ schedule: "0 8 * * 1",
2693
+ prompt: "Generate a weekly summary of completed tasks and goals."
2694
+ },
2695
+ {
2696
+ name: `Custom ${chalk.default.gray("(enter manually)")}`,
2697
+ schedule: "__custom__",
2698
+ prompt: ""
2699
+ }
2700
+ ];
2701
+ const { wantCron } = await inquirer.default.prompt([{
2702
+ type: "confirm",
2703
+ name: "wantCron",
2704
+ message: "Add scheduled tasks?",
2705
+ default: false
2706
+ }]);
2707
+ if (!wantCron) return;
2708
+ const { selectedPresets } = await inquirer.default.prompt([{
2709
+ type: "checkbox",
2710
+ name: "selectedPresets",
2711
+ message: "Select tasks to schedule:",
2712
+ choices: PRESETS.map((p) => ({
2713
+ name: p.name,
2714
+ value: p.schedule
2715
+ }))
2716
+ }]);
2717
+ const tasksToAdd = [];
2718
+ for (const sch of selectedPresets) if (sch === "__custom__") {
2719
+ const r = await inquirer.default.prompt([
2720
+ {
2721
+ type: "input",
2722
+ name: "schedule",
2723
+ message: " Cron schedule (e.g. \"0 9 * * 1-5\"):",
2724
+ validate: (v) => v.trim().length > 0 || "Required"
2725
+ },
2726
+ {
2727
+ type: "input",
2728
+ name: "prompt",
2729
+ message: " Agent prompt:",
2730
+ validate: (v) => v.trim().length > 0 || "Required"
2731
+ },
2732
+ {
2733
+ type: "input",
2734
+ name: "name",
2735
+ message: " Task name:",
2736
+ default: "Custom task"
2737
+ }
2738
+ ]);
2739
+ tasksToAdd.push({
2740
+ schedule: r.schedule.trim(),
2741
+ prompt: r.prompt.trim(),
2742
+ name: r.name.trim()
2743
+ });
2744
+ } else {
2745
+ const preset = PRESETS.find((p) => p.schedule === sch);
2746
+ tasksToAdd.push({
2747
+ schedule: preset.schedule,
2748
+ prompt: preset.prompt,
2749
+ name: preset.name.replace(/\s+\(.*\)/, "").trim()
2750
+ });
2751
+ }
2752
+ if (tasksToAdd.length > 0) try {
2753
+ const { loadCronTasks, addCronTask, saveCronTasks } = await Promise.resolve().then(() => require("./cron-tasks-OQbgmenS.js"));
2754
+ await loadCronTasks();
2755
+ for (const t of tasksToAdd) addCronTask(t.schedule, t.prompt, t.name);
2756
+ await saveCronTasks();
2757
+ console.log(chalk.default.green(` ✔ ${tasksToAdd.length} task(s) scheduled\n`));
2758
+ } catch {
2759
+ console.log(chalk.default.yellow(` ⚠ Could not save tasks — run: hyperclaw cron add\n`));
2760
+ }
2761
+ }
2762
+ async configureAutoReply() {
2763
+ console.log(chalk.default.hex("#06b6d4")("\n ⚡ Auto-reply Rules\n"));
2764
+ console.log(chalk.default.gray(" Automatic responses before the AI model is invoked.\n"));
2765
+ const { wantAutoReply } = await inquirer.default.prompt([{
2766
+ type: "confirm",
2767
+ name: "wantAutoReply",
2768
+ message: "Set up auto-reply rules?",
2769
+ default: false
2770
+ }]);
2771
+ if (!wantAutoReply) return;
2772
+ const PRESETS = [
2773
+ {
2774
+ name: `Away message ${chalk.default.gray("(reply to every message when offline)")}`,
2775
+ value: "away"
2776
+ },
2777
+ {
2778
+ name: `Keyword reply ${chalk.default.gray("(reply to specific keyword)")}`,
2779
+ value: "keyword"
2780
+ },
2781
+ {
2782
+ name: `Ignore channel ${chalk.default.gray("(silently ignore a channel)")}`,
2783
+ value: "ignore"
2784
+ }
2785
+ ];
2786
+ const { ruleType } = await inquirer.default.prompt([{
2787
+ type: "list",
2788
+ name: "ruleType",
2789
+ message: "Rule type:",
2790
+ choices: PRESETS
2791
+ }]);
2792
+ try {
2793
+ const { AutoReplyEngine } = await Promise.resolve().then(() => require("./rules-Cqen1Mpt.js"));
2794
+ const engine = new AutoReplyEngine();
2795
+ await engine.load();
2796
+ const BASE = {
2797
+ enabled: true,
2798
+ priority: 10,
2799
+ stopOnMatch: true,
2800
+ conditionLogic: "OR"
2801
+ };
2802
+ if (ruleType === "away") {
2803
+ const { reply } = await inquirer.default.prompt([{
2804
+ type: "input",
2805
+ name: "reply",
2806
+ message: " Away message text:",
2807
+ default: "I'm currently unavailable. I'll get back to you soon.",
2808
+ validate: (v) => v.trim().length > 0 || "Required"
2809
+ }]);
2810
+ await engine.add({
2811
+ ...BASE,
2812
+ name: "Away message",
2813
+ conditions: [{ type: "always" }],
2814
+ action: {
2815
+ type: "reply",
2816
+ reply: reply.trim()
2817
+ }
2818
+ });
2819
+ } else if (ruleType === "keyword") {
2820
+ const r = await inquirer.default.prompt([{
2821
+ type: "input",
2822
+ name: "keyword",
2823
+ message: " Trigger keyword:",
2824
+ validate: (v) => v.trim().length > 0 || "Required"
2825
+ }, {
2826
+ type: "input",
2827
+ name: "reply",
2828
+ message: " Reply text:",
2829
+ validate: (v) => v.trim().length > 0 || "Required"
2830
+ }]);
2831
+ await engine.add({
2832
+ ...BASE,
2833
+ name: `Keyword: ${r.keyword}`,
2834
+ conditions: [{
2835
+ type: "contains",
2836
+ value: r.keyword.trim()
2837
+ }],
2838
+ action: {
2839
+ type: "reply",
2840
+ reply: r.reply.trim()
2841
+ }
2842
+ });
2843
+ } else if (ruleType === "ignore") {
2844
+ const { channelId } = await inquirer.default.prompt([{
2845
+ type: "input",
2846
+ name: "channelId",
2847
+ message: " Channel ID to ignore:",
2848
+ validate: (v) => v.trim().length > 0 || "Required"
2849
+ }]);
2850
+ await engine.add({
2851
+ ...BASE,
2852
+ name: `Ignore channel: ${channelId}`,
2853
+ conditions: [{
2854
+ type: "channel",
2855
+ value: channelId.trim()
2856
+ }],
2857
+ action: { type: "ignore" }
2858
+ });
2859
+ }
2860
+ console.log(chalk.default.green(` ✔ Auto-reply rule added\n`));
2861
+ } catch {
2862
+ console.log(chalk.default.yellow(` ⚠ Could not save rule — run: hyperclaw auto-reply list\n`));
2863
+ }
2864
+ }
2865
+ async configureWebhooks() {
2866
+ console.log(chalk.default.hex("#06b6d4")("\n 🔗 Inbound Webhooks\n"));
2867
+ console.log(chalk.default.gray(" Register POST endpoints that trigger the agent (GitHub, Stripe, Linear, etc).\n"));
2868
+ const { wantWebhook } = await inquirer.default.prompt([{
2869
+ type: "confirm",
2870
+ name: "wantWebhook",
2871
+ message: "Register inbound webhook endpoints?",
2872
+ default: false
2873
+ }]);
2874
+ if (!wantWebhook) return;
2875
+ try {
2876
+ const { WebhookManager } = await Promise.resolve().then(() => require("./manager--wG9JdFW.js"));
2877
+ const manager = new WebhookManager();
2878
+ await manager.load();
2879
+ let addMore = true;
2880
+ while (addMore) {
2881
+ const r = await inquirer.default.prompt([
2882
+ {
2883
+ type: "input",
2884
+ name: "name",
2885
+ message: " Webhook name (e.g. GitHub push):",
2886
+ validate: (v) => v.trim().length > 0 || "Required"
2887
+ },
2888
+ {
2889
+ type: "list",
2890
+ name: "format",
2891
+ message: " Payload format:",
2892
+ choices: [
2893
+ {
2894
+ name: "GitHub",
2895
+ value: "github"
2896
+ },
2897
+ {
2898
+ name: "Stripe",
2899
+ value: "stripe"
2900
+ },
2901
+ {
2902
+ name: "Linear",
2903
+ value: "linear"
2904
+ },
2905
+ {
2906
+ name: "Notion",
2907
+ value: "notion"
2908
+ },
2909
+ {
2910
+ name: "JSON (generic)",
2911
+ value: "json"
2912
+ },
2913
+ {
2914
+ name: "Raw",
2915
+ value: "raw"
2916
+ }
2917
+ ]
2918
+ },
2919
+ {
2920
+ type: "input",
2921
+ name: "template",
2922
+ message: " Message template (use {{body.field}}):",
2923
+ default: "Webhook received: {{body}}"
2924
+ }
2925
+ ]);
2926
+ await manager.add({
2927
+ name: r.name.trim(),
2928
+ format: r.format,
2929
+ template: r.template.trim(),
2930
+ routeTo: {
2931
+ type: "channel",
2932
+ target: "default"
2933
+ }
2934
+ });
2935
+ const { more } = await inquirer.default.prompt([{
2936
+ type: "confirm",
2937
+ name: "more",
2938
+ message: " Add another webhook?",
2939
+ default: false
2940
+ }]);
2941
+ addMore = more;
2942
+ }
2943
+ console.log(chalk.default.green(` ✔ Webhook(s) registered — route: POST /webhook/<id>\n`));
2944
+ } catch {
2945
+ console.log(chalk.default.yellow(` ⚠ Could not save webhooks — run: hyperclaw webhooks list\n`));
2946
+ }
2947
+ }
2948
+ async configureNodes() {
2949
+ console.log(chalk.default.hex("#06b6d4")("\n 📱 Nodes (Remote Compute / Mobile)\n"));
2950
+ console.log(chalk.default.gray(" Add VPS, Raspberry Pi, Android, or VM nodes for distributed inference.\n"));
2951
+ const { wantNode } = await inquirer.default.prompt([{
2952
+ type: "confirm",
2953
+ name: "wantNode",
2954
+ message: "Add compute nodes?",
2955
+ default: false
2956
+ }]);
2957
+ if (!wantNode) return;
2958
+ const { nodeAdd } = await Promise.resolve().then(() => require("./node-C4esBfbX.js"));
2959
+ let addMore = true;
2960
+ while (addMore) {
2961
+ await nodeAdd();
2962
+ const { more } = await inquirer.default.prompt([{
2963
+ type: "confirm",
2964
+ name: "more",
2965
+ message: " Add another node?",
2966
+ default: false
2967
+ }]);
2968
+ addMore = more;
2969
+ }
2970
+ }
2971
+ async configureOAuth() {
2972
+ console.log(chalk.default.hex("#06b6d4")("\n 🔐 OAuth / External Auth\n"));
2973
+ console.log(chalk.default.gray(" Connect Google, GitHub or other accounts for the agent to act on your behalf.\n"));
2974
+ const { wantOAuth } = await inquirer.default.prompt([{
2975
+ type: "confirm",
2976
+ name: "wantOAuth",
2977
+ message: "Set up OAuth / external auth now?",
2978
+ default: false
2979
+ }]);
2980
+ if (!wantOAuth) return;
2981
+ const { selectedProviders } = await inquirer.default.prompt([{
2982
+ type: "checkbox",
2983
+ name: "selectedProviders",
2984
+ message: "Select providers to connect:",
2985
+ choices: [
2986
+ {
2987
+ name: `Google Calendar ${chalk.default.gray("(read/create events)")}`,
2988
+ value: "google-calendar"
2989
+ },
2990
+ {
2991
+ name: `Google Drive ${chalk.default.gray("(read/write files)")}`,
2992
+ value: "google-drive"
2993
+ },
2994
+ {
2995
+ name: `GitHub ${chalk.default.gray("(repos, issues, PRs)")}`,
2996
+ value: "github"
2997
+ },
2998
+ {
2999
+ name: `Notion ${chalk.default.gray("(pages & databases)")}`,
3000
+ value: "notion"
3001
+ },
3002
+ {
3003
+ name: `Linear ${chalk.default.gray("(issues & projects)")}`,
3004
+ value: "linear"
3005
+ }
3006
+ ]
3007
+ }]);
3008
+ for (const provider of selectedProviders) {
3009
+ console.log(chalk.default.gray(`\n Starting OAuth flow for ${provider}...`));
3010
+ try {
3011
+ const { runOAuthFlow } = await Promise.resolve().then(() => require("./oauth-flow-BbLQTzZk.js"));
3012
+ const { writeOAuthToken } = await Promise.resolve().then(() => require("./oauth-provider-UZyG84s7.js"));
3013
+ const tokens = await runOAuthFlow(provider, {});
3014
+ const now = Math.floor(Date.now() / 1e3);
3015
+ const TOKEN_URLS = {
3016
+ "google-calendar": "https://oauth2.googleapis.com/token",
3017
+ "google-drive": "https://oauth2.googleapis.com/token",
3018
+ "github": "https://github.com/login/oauth/access_token",
3019
+ "notion": "https://api.notion.com/v1/oauth/token",
3020
+ "linear": "https://api.linear.app/oauth/token"
3021
+ };
3022
+ await writeOAuthToken(provider, {
3023
+ access_token: tokens.access_token,
3024
+ refresh_token: tokens.refresh_token,
3025
+ expires_at: tokens.expires_in ? now + tokens.expires_in : void 0,
3026
+ token_url: TOKEN_URLS[provider] ?? "https://oauth2.googleapis.com/token"
3027
+ });
3028
+ console.log(chalk.default.green(` ✔ ${provider} connected\n`));
3029
+ } catch {
3030
+ console.log(chalk.default.yellow(` ⚠ ${provider} OAuth failed — run later: hyperclaw auth oauth ${provider}\n`));
3031
+ }
3032
+ }
3033
+ }
3034
+ async configureAgentBindings() {
3035
+ console.log(chalk.default.hex("#06b6d4")("\n 🔗 Multi-agent Bindings\n"));
3036
+ console.log(chalk.default.gray(" Route specific channels to different agent personas or models.\n"));
3037
+ const { wantBindings } = await inquirer.default.prompt([{
3038
+ type: "confirm",
3039
+ name: "wantBindings",
3040
+ message: "Configure channel > agent bindings?",
3041
+ default: false
3042
+ }]);
3043
+ if (!wantBindings) return;
3044
+ try {
3045
+ const { AgentRouter } = await Promise.resolve().then(() => require("./agents-routing-ChorJKFL.js"));
3046
+ await new AgentRouter().bind();
3047
+ return;
3048
+ } catch {
3049
+ const r = await inquirer.default.prompt([
3050
+ {
3051
+ type: "input",
3052
+ name: "channel",
3053
+ message: " Channel ID (e.g. telegram):",
3054
+ validate: (v) => v.trim().length > 0 || "Required"
3055
+ },
3056
+ {
3057
+ type: "input",
3058
+ name: "agentName",
3059
+ message: " Agent name / persona for this channel:",
3060
+ default: "Hyper"
3061
+ },
3062
+ {
3063
+ type: "input",
3064
+ name: "modelId",
3065
+ message: " Override model ID (leave blank = use primary):",
3066
+ default: ""
3067
+ }
3068
+ ]);
3069
+ if (r.channel) {
3070
+ const cfg = await this.config.load();
3071
+ const bindings = Array.isArray(cfg?.bindings) ? [...cfg.bindings] : [];
3072
+ bindings.push({
3073
+ match: { channel: r.channel.trim() },
3074
+ agentId: r.agentName.trim()
3075
+ });
3076
+ await this.config.patch({ bindings });
3077
+ console.log(chalk.default.green(` Binding: ${r.channel} > ${r.agentName}\n`));
3078
+ }
3079
+ }
3080
+ }
3081
+ async configureGroupSandbox() {
3082
+ console.log(chalk.default.hex("#06b6d4")("\n 🐳 Group Sandbox (Docker)\n"));
3083
+ console.log(chalk.default.gray(" Isolates group chat sessions in Docker containers for security.\n"));
3084
+ const { wantSandbox } = await inquirer.default.prompt([{
3085
+ type: "confirm",
3086
+ name: "wantSandbox",
3087
+ message: "Enable Docker sandboxing for group chat sessions?",
3088
+ default: false
3089
+ }]);
3090
+ if (!wantSandbox) return void 0;
3091
+ const { image, memoryLimit } = await inquirer.default.prompt([{
3092
+ type: "input",
3093
+ name: "image",
3094
+ message: " Docker image:",
3095
+ default: "node:22-alpine"
3096
+ }, {
3097
+ type: "input",
3098
+ name: "memoryLimit",
3099
+ message: " Memory limit per container:",
3100
+ default: "256m"
3101
+ }]);
3102
+ console.log(chalk.default.green(` ✔ Group sandbox: ${image} (${memoryLimit})\n`));
3103
+ return {
3104
+ enabled: true,
3105
+ image: image.trim(),
3106
+ memoryLimit: memoryLimit.trim()
3107
+ };
3108
+ }
3109
+ async configureUpdateChannel() {
3110
+ console.log(chalk.default.hex("#06b6d4")("\n 🔄 Update Channel\n"));
3111
+ const { channel } = await inquirer.default.prompt([{
3112
+ type: "list",
3113
+ name: "channel",
3114
+ message: "Update channel:",
3115
+ choices: [
3116
+ {
3117
+ name: `Stable ${chalk.default.gray("(recommended — tested releases)")}`,
3118
+ value: "stable"
3119
+ },
3120
+ {
3121
+ name: `Beta ${chalk.default.gray("(early access — new features, may have bugs)")}`,
3122
+ value: "beta"
3123
+ },
3124
+ {
3125
+ name: `Dev ${chalk.default.gray("(bleeding edge — for contributors)")}`,
3126
+ value: "dev"
3127
+ }
3128
+ ],
3129
+ default: "stable"
3130
+ }]);
3131
+ console.log(chalk.default.green(` ✔ Update channel: ${channel}\n`));
3132
+ return channel;
3133
+ }
3134
+ async saveAll(data, options) {
3135
+ console.log();
3136
+ const spinner = (0, ora.default)("Saving configuration...").start();
3137
+ const current = await this.config.load();
3138
+ const skillsPatch = { installed: current?.skills?.installed || [] };
3139
+ if (data.serviceApiKeys && Object.keys(data.serviceApiKeys).length > 0) skillsPatch.apiKeys = {
3140
+ ...current?.skills?.apiKeys || {},
3141
+ ...data.serviceApiKeys
3142
+ };
3143
+ if (current?.skills?.vtApiKey) skillsPatch.vtApiKey = current.skills.vtApiKey;
3144
+ const finalSkills = skillsPatch.apiKeys || skillsPatch.vtApiKey ? skillsPatch : current?.skills || { installed: [] };
3145
+ const pkgVersion = await fs_extra.default.readJson(path.default.join(__dirname, "../package.json")).then((p) => p.version).catch(() => "5.2.0");
3146
+ const channelIds = Object.keys(data.channelConfigs || {});
3147
+ let gatewayToSave = data.gatewayConfig ? {
3148
+ ...data.gatewayConfig,
3149
+ enabledChannels: channelIds
3150
+ } : current?.gateway ? { ...current.gateway } : void 0;
3151
+ if (gatewayToSave && channelIds.length > 0) gatewayToSave.enabledChannels = channelIds;
3152
+ await this.config.save({
3153
+ ...current,
3154
+ version: pkgVersion,
3155
+ workspaceName: data.workspaceName,
3156
+ provider: data.providerConfig,
3157
+ providers: data.providers || (data.providerConfig ? [data.providerConfig] : []),
3158
+ gateway: gatewayToSave,
3159
+ channels: channelIds,
3160
+ channelConfigs: data.channelConfigs,
3161
+ skills: finalSkills,
3162
+ enabledHooks: data.hooks || [],
3163
+ identity: {
3164
+ agentName: data.identity?.agentName,
3165
+ userName: data.identity?.userName,
3166
+ language: data.identity?.language,
3167
+ wakeWord: data.identity?.wakeWord || `Hey ${data.identity?.agentName || "Hyper"}`,
3168
+ wakeUpMessage: data.identity?.wakeUpMessage,
3169
+ systemPrompt: data.identity?.systemPrompt,
3170
+ personality: data.identity?.personality,
3171
+ rules: data.identity?.rules
3172
+ },
3173
+ memoryIntegration: data.memoryIntegration,
3174
+ talkMode: data.talkModeConfig,
3175
+ pcAccess: {
3176
+ enabled: true,
3177
+ level: data.pcAccess?.level || "full",
3178
+ allowedPaths: [],
3179
+ allowedCommands: [],
3180
+ confirmDestructive: data.pcAccess?.confirmDestructive ?? false,
3181
+ maxOutputBytes: 5e4
3182
+ },
3183
+ updateChannel: data.updateChannel || "stable",
3184
+ groupSandbox: data.groupSandbox,
3185
+ ...data.webSearch ? { webSearch: data.webSearch } : {},
3186
+ ...data.rateLimit ? { rateLimit: data.rateLimit } : {},
3187
+ hatchMode: data.hatchMode || "tui",
3188
+ installedAt: (/* @__PURE__ */ new Date()).toISOString()
3189
+ });
3190
+ spinner.succeed("Configuration saved");
3191
+ const memory = new MemoryManager();
3192
+ await memory.init(data.identity);
3193
+ if (data.heartbeatEnabled) try {
3194
+ const { HookLoader } = await Promise.resolve().then(() => require("./loader-Dq_cDlOW.js"));
3195
+ const loader = new HookLoader();
3196
+ loader.enable("morning-briefing");
3197
+ console.log(chalk.default.gray(" ✔ Morning Briefing hook enabled"));
3198
+ } catch {}
3199
+ await this.testConnections(data.channelConfigs || {});
3200
+ await this.setupIntegrations();
3201
+ const isRemoteMode = data.gatewayConfig?.mode === "remote";
3202
+ if (!isRemoteMode && (data.installDaemon || options.daemon || options.installDaemon)) {
3203
+ const runtime = options.daemonRuntime ?? data.daemonRuntime ?? "node";
3204
+ const s = (0, ora.default)(`Installing system daemon (runtime: ${runtime})...`).start();
3205
+ await this.daemon.install();
3206
+ s.succeed(chalk.default.red(`System daemon installed (runtime: ${runtime}, starts on boot)`));
3207
+ }
3208
+ if (!isRemoteMode && data.gatewayConfig?.tailscaleExposure && data.gatewayConfig.tailscaleExposure !== "off") await this.gateway.applyTailscaleExposure(data.gatewayConfig.tailscaleExposure, data.gatewayConfig.port);
3209
+ if (data.gatewayConfig && !isRemoteMode && (options.startNow || data.startNow || data.installDaemon)) {
3210
+ const s = (0, ora.default)("Starting gateway...").start();
3211
+ await this.daemon.start();
3212
+ s.succeed(`Gateway running at ws://localhost:${data.gatewayConfig.port}`);
3213
+ } else if (isRemoteMode) {
3214
+ const remoteUrl = data.gatewayConfig?.remote?.url;
3215
+ console.log(chalk.default.green(` Remote mode: connect to ${remoteUrl || "(configured URL)"}\n`));
3216
+ }
3217
+ console.log(chalk.default.gray("\n Running health check...\n"));
3218
+ const healthResults = {};
3219
+ try {
3220
+ const { runDoctor } = await Promise.resolve().then(() => require("./doctor-mgWumA25.js"));
3221
+ await runDoctor(true);
3222
+ healthResults.doctor = "ok";
3223
+ } catch {
3224
+ healthResults.doctor = "failed";
3225
+ }
3226
+ if (data.gatewayConfig) try {
3227
+ const { resolveGatewayUrl } = await Promise.resolve().then(() => require("./health-C8n9RH5O.js"));
3228
+ const { gatewayUrl } = resolveGatewayUrl({ gateway: data.gatewayConfig });
3229
+ const reachable = await new Promise((resolve) => {
3230
+ const u = new URL(gatewayUrl);
3231
+ const mod = u.protocol === "https:" ? require("https") : require("http");
3232
+ const req = mod.get(`${gatewayUrl}/api/status`, () => resolve(true));
3233
+ req.on("error", () => resolve(false));
3234
+ req.setTimeout(5e3, () => {
3235
+ req.destroy();
3236
+ resolve(false);
3237
+ });
3238
+ });
3239
+ healthResults.gateway = reachable ? "reachable" : "unreachable";
3240
+ if (reachable) console.log(chalk.default.green(` Gateway reachable at ${gatewayUrl}`));
3241
+ else console.log(chalk.default.yellow(` Gateway not reachable at ${gatewayUrl}`));
3242
+ } catch {
3243
+ healthResults.gateway = "error";
3244
+ }
3245
+ if (options.jsonOutput) {
3246
+ const result = {
3247
+ ok: true,
3248
+ version: pkgVersion,
3249
+ provider: data.providerConfig?.providerId,
3250
+ model: data.providerConfig?.modelId,
3251
+ gateway: data.gatewayConfig ? {
3252
+ port: data.gatewayConfig.port,
3253
+ bind: data.gatewayConfig.bind
3254
+ } : null,
3255
+ channels: Object.keys(data.channelConfigs || {}),
3256
+ hooks: data.hooks || [],
3257
+ daemonInstalled: !!(data.installDaemon || options.installDaemon),
3258
+ health: healthResults
3259
+ };
3260
+ process.stdout.write(JSON.stringify(result, null, 2) + "\n");
3261
+ return;
3262
+ }
3263
+ this.showSuccessScreen(data, healthResults.gateway === "reachable");
3264
+ const hatchMode = data.hatchMode || "tui";
3265
+ if (hatchMode === "tui") {
3266
+ const agentName = data.identity?.agentName || "HyperClaw";
3267
+ const userName = data.identity?.userName || require("os").userInfo().username;
3268
+ const now = /* @__PURE__ */ new Date();
3269
+ const dayName = now.toLocaleDateString("en-US", { weekday: "long" });
3270
+ const dateStr = now.toLocaleDateString("en-US", {
3271
+ month: "long",
3272
+ day: "numeric",
3273
+ year: "numeric"
3274
+ });
3275
+ console.log(chalk.default.bold.cyan("\n 🌅 Wake up, my friend!\n"));
3276
+ console.log(chalk.default.gray(` ${dayName}, ${dateStr}`));
3277
+ console.log(chalk.default.gray(` I am ${agentName}. Hello, ${userName}.`));
3278
+ console.log(chalk.default.gray(" Fresh out of the box — who are you, and who am I? What should we call each other?"));
3279
+ console.log(chalk.default.gray(" (Refine in chat.)\n"));
3280
+ console.log(chalk.default.gray(" Starting chat...\n"));
3281
+ const { runChat } = await Promise.resolve().then(() => require("./chat-D-i8bqTf.js"));
3282
+ await runChat({});
3283
+ } else if (hatchMode === "web" && data.gatewayConfig && !data.gatewayConfig.remote) {
3284
+ const port = data.gatewayConfig.port ?? 18789;
3285
+ const url = `http://localhost:${port}`;
3286
+ const openCmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
3287
+ const { execFile } = await import("child_process");
3288
+ const { promisify } = await import("util");
3289
+ try {
3290
+ await promisify(execFile)(openCmd, [url], { timeout: 3e3 });
3291
+ console.log(chalk.default.green(`\n Opened ${url} in your browser.\n`));
3292
+ } catch {
3293
+ console.log(chalk.default.gray(`\n Open in browser: ${url}\n`));
3294
+ }
3295
+ } else if (hatchMode === "skip") console.log(chalk.default.gray("\n Run hyperclaw chat or open the Web UI when ready.\n"));
3296
+ }
3297
+ async setupIntegrations() {
3298
+ console.log();
3299
+ const { configure } = await inquirer.default.prompt([{
3300
+ type: "confirm",
3301
+ name: "configure",
3302
+ message: chalk.default.cyan("Configure integrations now?") + chalk.default.gray(" (Spotify, Home Assistant, GitHub, Trello, etc.)"),
3303
+ default: false
3304
+ }]);
3305
+ if (!configure) {
3306
+ console.log(chalk.default.gray(" Skipped — configure later by telling the agent or running: hyperclaw config set-key KEY value\n"));
3307
+ return;
3308
+ }
3309
+ const INTEGRATIONS = [
3310
+ {
3311
+ name: "Spotify",
3312
+ value: "spotify",
3313
+ instructions: [
3314
+ "1. Go to → https://developer.spotify.com/dashboard",
3315
+ "2. Log in and click \"Create App\"",
3316
+ "3. Fill in name/description, set Redirect URI to http://localhost:8888/callback",
3317
+ "4. Click \"Settings\" → copy your Client ID and Client Secret",
3318
+ "5. To get a Refresh Token, run this after saving Client ID/Secret:",
3319
+ " npx spotify-token (or use the Spotify OAuth Playground)"
3320
+ ],
3321
+ keys: [
3322
+ {
3323
+ env: "SPOTIFY_CLIENT_ID",
3324
+ label: "Client ID"
3325
+ },
3326
+ {
3327
+ env: "SPOTIFY_CLIENT_SECRET",
3328
+ label: "Client Secret",
3329
+ secret: true
3330
+ },
3331
+ {
3332
+ env: "SPOTIFY_REFRESH_TOKEN",
3333
+ label: "Refresh Token",
3334
+ secret: true
3335
+ }
3336
+ ]
3337
+ },
3338
+ {
3339
+ name: "Home Assistant",
3340
+ value: "ha",
3341
+ instructions: [
3342
+ "1. Open Home Assistant in your browser",
3343
+ "2. Go to → Profile (bottom left) → Long-lived access tokens",
3344
+ "3. Click \"Create Token\", give it a name (e.g. HyperClaw)",
3345
+ "4. Copy the token immediately — it won't be shown again",
3346
+ "5. Your URL is usually http://homeassistant.local:8123 or your local IP"
3347
+ ],
3348
+ keys: [{
3349
+ env: "HA_URL",
3350
+ label: "URL (e.g. http://homeassistant.local:8123)"
3351
+ }, {
3352
+ env: "HA_TOKEN",
3353
+ label: "Long-lived Access Token",
3354
+ secret: true
3355
+ }]
3356
+ },
3357
+ {
3358
+ name: "GitHub",
3359
+ value: "github",
3360
+ instructions: [
3361
+ "1. Go to → https://github.com/settings/tokens",
3362
+ "2. Click \"Generate new token (classic)\"",
3363
+ "3. Give it a name (e.g. HyperClaw)",
3364
+ "4. Select scopes: repo, read:user, read:org (or as needed)",
3365
+ "5. Click \"Generate token\" and copy it — shown only once"
3366
+ ],
3367
+ keys: [{
3368
+ env: "GITHUB_TOKEN",
3369
+ label: "Personal Access Token",
3370
+ secret: true
3371
+ }]
3372
+ },
3373
+ {
3374
+ name: "Trello",
3375
+ value: "trello",
3376
+ instructions: [
3377
+ "1. Go to → https://trello.com/app-key",
3378
+ "2. Copy your API Key from the top of the page",
3379
+ "3. On the same page, click \"Token\" link to generate a Token",
3380
+ "4. Authorize the app and copy the token"
3381
+ ],
3382
+ keys: [{
3383
+ env: "TRELLO_API_KEY",
3384
+ label: "API Key"
3385
+ }, {
3386
+ env: "TRELLO_TOKEN",
3387
+ label: "Token",
3388
+ secret: true
3389
+ }]
3390
+ },
3391
+ {
3392
+ name: "Obsidian",
3393
+ value: "obsidian",
3394
+ instructions: [
3395
+ "1. Open Obsidian → Settings → Community Plugins",
3396
+ "2. Search for \"Local REST API\" and install it",
3397
+ "3. Enable the plugin and go to its settings",
3398
+ "4. Copy the API Key shown in the plugin settings",
3399
+ "5. The default port is 27123 (leave blank to use default)"
3400
+ ],
3401
+ keys: [{
3402
+ env: "OBSIDIAN_API_KEY",
3403
+ label: "Local REST API Key",
3404
+ secret: true
3405
+ }, {
3406
+ env: "OBSIDIAN_PORT",
3407
+ label: "Port (press Enter for default 27123)"
3408
+ }]
3409
+ },
3410
+ {
3411
+ name: "Philips Hue",
3412
+ value: "hue",
3413
+ instructions: [
3414
+ "1. Find your Hue Bridge IP: open the Hue app → Settings → My Hue System → Bridge",
3415
+ " OR check your router's device list for \"Philips-hue\"",
3416
+ "2. Go to → http://<BRIDGE_IP>/debug/clip.html",
3417
+ "3. In the URL field type: /api",
3418
+ " Body: {\"devicetype\":\"hyperclaw#mydevice\"}",
3419
+ "4. Press the physical button on the bridge, then click POST",
3420
+ "5. Copy the \"username\" value from the response — that's your HUE_USERNAME"
3421
+ ],
3422
+ keys: [{
3423
+ env: "HUE_BRIDGE_IP",
3424
+ label: "Bridge IP (e.g. 192.168.1.100)"
3425
+ }, {
3426
+ env: "HUE_USERNAME",
3427
+ label: "Username (from bridge pairing)"
3428
+ }]
3429
+ },
3430
+ {
3431
+ name: "8Sleep",
3432
+ value: "eightsleep",
3433
+ instructions: [
3434
+ "1. Use your Eight Sleep account email and password",
3435
+ "2. These are the same credentials you use in the Eight Sleep app",
3436
+ " Note: 8Sleep has rate limits — don't query too frequently"
3437
+ ],
3438
+ keys: [{
3439
+ env: "EIGHTSLEEP_EMAIL",
3440
+ label: "Email"
3441
+ }, {
3442
+ env: "EIGHTSLEEP_PASSWORD",
3443
+ label: "Password",
3444
+ secret: true
3445
+ }]
3446
+ },
3447
+ {
3448
+ name: "Sonos",
3449
+ value: "sonos",
3450
+ instructions: [
3451
+ "1. Find your Sonos speaker's local IP address",
3452
+ "2. Open the Sonos app → System → (your room) → About My System",
3453
+ " OR check your router's device list for your Sonos device",
3454
+ "3. The IP looks like: 192.168.1.x"
3455
+ ],
3456
+ keys: [{
3457
+ env: "SONOS_IP",
3458
+ label: "Speaker IP (e.g. 192.168.1.50)"
3459
+ }]
3460
+ },
3461
+ {
3462
+ name: "Giphy / GIF search",
3463
+ value: "giphy",
3464
+ instructions: [
3465
+ "Option A — Giphy:",
3466
+ "1. Go to → https://developers.giphy.com",
3467
+ "2. Log in or sign up → Dashboard → Create an App → API",
3468
+ "3. Copy your API Key",
3469
+ "",
3470
+ "Option B — Tenor (Google):",
3471
+ "1. Go to → https://console.cloud.google.com",
3472
+ "2. Enable the \"Tenor API\" → Create credentials → API Key",
3473
+ " (You can leave one blank and only fill the other)"
3474
+ ],
3475
+ keys: [{
3476
+ env: "GIPHY_API_KEY",
3477
+ label: "Giphy API Key (leave blank to skip)"
3478
+ }, {
3479
+ env: "TENOR_API_KEY",
3480
+ label: "Tenor API Key (leave blank to skip)"
3481
+ }]
3482
+ },
3483
+ {
3484
+ name: "1Password",
3485
+ value: "1password",
3486
+ instructions: [
3487
+ "1. Install the 1Password CLI: https://developer.1password.com/docs/cli/get-started/",
3488
+ "2. Go to → https://my.1password.com → Integrations → Service Accounts",
3489
+ "3. Create a new Service Account, give it read access to the vaults you need",
3490
+ "4. Copy the Service Account Token — shown only once",
3491
+ "5. Verify the CLI works: op account list"
3492
+ ],
3493
+ keys: [{
3494
+ env: "OP_SERVICE_ACCOUNT_TOKEN",
3495
+ label: "Service Account Token",
3496
+ secret: true
3497
+ }]
3498
+ },
3499
+ {
3500
+ name: "Image Generation (DALL-E / Stability AI)",
3501
+ value: "image_gen",
3502
+ instructions: [
3503
+ "Option A — OpenAI DALL-E 3 (recommended):",
3504
+ "1. Go to → https://platform.openai.com/api-keys",
3505
+ "2. Click \"Create new secret key\", give it a name",
3506
+ "3. Copy the key — shown only once (starts with sk-...)",
3507
+ " Note: DALL-E 3 requires a paid OpenAI plan",
3508
+ "",
3509
+ "Option B — Stability AI (Stable Diffusion):",
3510
+ "1. Go to → https://platform.stability.ai/account/keys",
3511
+ "2. Create an account and click \"Create API Key\"",
3512
+ "3. Copy the key",
3513
+ " (You can set both — the agent will prefer DALL-E if both are present)"
3514
+ ],
3515
+ keys: [{
3516
+ env: "OPENAI_API_KEY",
3517
+ label: "OpenAI API Key (for DALL-E 3, leave blank to skip)",
3518
+ secret: true
3519
+ }, {
3520
+ env: "STABILITY_API_KEY",
3521
+ label: "Stability AI Key (for Stable Diffusion, leave blank to skip)",
3522
+ secret: true
3523
+ }]
3524
+ },
3525
+ {
3526
+ name: "Gmail",
3527
+ value: "gmail",
3528
+ instructions: [
3529
+ "1. Go to → https://console.cloud.google.com",
3530
+ "2. Create a project (or select an existing one)",
3531
+ "3. Enable APIs: \"Gmail API\" and \"Cloud Pub/Sub API\"",
3532
+ "4. Go to → APIs & Services → Credentials → Create OAuth 2.0 Client ID",
3533
+ " Application type: Desktop App",
3534
+ "5. Download the JSON credentials file",
3535
+ "6. Set GOOGLE_CREDENTIALS_PATH to the path of that file",
3536
+ "7. For Pub/Sub (push notifications), create a topic and subscription:",
3537
+ " gcloud pubsub topics create hyperclaw-gmail",
3538
+ " gcloud pubsub subscriptions create hyperclaw-sub --topic=hyperclaw-gmail",
3539
+ " Then set GMAIL_PUBSUB_TOPIC to projects/<project-id>/topics/hyperclaw-gmail"
3540
+ ],
3541
+ keys: [{
3542
+ env: "GOOGLE_CREDENTIALS_PATH",
3543
+ label: "Path to credentials JSON file (e.g. ~/Downloads/credentials.json)"
3544
+ }, {
3545
+ env: "GMAIL_PUBSUB_TOPIC",
3546
+ label: "Pub/Sub topic (leave blank to skip push notifications)"
3547
+ }]
3548
+ }
3549
+ ];
3550
+ console.log(chalk.default.gray("\n Free / built-in (no key needed):"));
3551
+ console.log(chalk.default.gray(" • Weather (Open-Meteo) • Music search (iTunes) • Canvas • Browser"));
3552
+ console.log(chalk.default.gray(" • Apple Notes / Reminders / Things 3 / Bear (macOS) • iMessage (macOS)\n"));
3553
+ const { chosen } = await inquirer.default.prompt([{
3554
+ type: "checkbox",
3555
+ name: "chosen",
3556
+ message: "Select integrations to configure (need API keys):",
3557
+ choices: [...INTEGRATIONS.map((i) => ({
3558
+ name: i.name,
3559
+ value: i.value
3560
+ }))],
3561
+ pageSize: 14
3562
+ }]);
3563
+ if (chosen.length === 0) {
3564
+ console.log(chalk.default.gray(" No integrations selected. You can add them later with: hyperclaw config set-key KEY value\n"));
3565
+ return;
3566
+ }
3567
+ const { getEnvFilePath } = await Promise.resolve().then(() => require("./paths-D-QecARF.js"));
3568
+ const envPath = getEnvFilePath();
3569
+ const envLines = [];
3570
+ try {
3571
+ const existing = await fs_extra.default.readFile(envPath, "utf8").catch(() => "");
3572
+ envLines.push(...existing.split("\n").filter((l) => l.trim()));
3573
+ } catch {}
3574
+ for (const id of chosen) {
3575
+ const integration = INTEGRATIONS.find((i) => i.value === id);
3576
+ console.log("\n" + chalk.default.bold.cyan(` ── ${integration.name} ──`));
3577
+ if (integration.instructions.length > 0) {
3578
+ console.log(chalk.default.gray("\n How to get your credentials:\n"));
3579
+ for (const line of integration.instructions) if (line === "") console.log();
3580
+ else if (/^\d+\./.test(line.trim())) console.log(chalk.default.white(` ${line}`));
3581
+ else console.log(chalk.default.gray(` ${line}`));
3582
+ console.log();
3583
+ const { ready } = await inquirer.default.prompt([{
3584
+ type: "confirm",
3585
+ name: "ready",
3586
+ message: ` Ready to enter your ${integration.name} credentials?`,
3587
+ default: true
3588
+ }]);
3589
+ if (!ready) {
3590
+ console.log(chalk.default.gray(` Skipped — add later: hyperclaw config set-key KEY value\n`));
3591
+ continue;
3592
+ }
3593
+ }
3594
+ for (const key of integration.keys) {
3595
+ const { val } = await inquirer.default.prompt([{
3596
+ type: key.secret ? "password" : "input",
3597
+ name: "val",
3598
+ message: ` ${key.label}:`,
3599
+ mask: key.secret ? "*" : void 0
3600
+ }]);
3601
+ if (val && val.trim()) {
3602
+ const line = `${key.env}=${val.trim()}`;
3603
+ const idx = envLines.findIndex((l) => l.startsWith(`${key.env}=`));
3604
+ if (idx >= 0) envLines[idx] = line;
3605
+ else envLines.push(line);
3606
+ console.log(chalk.default.green(` ✔ ${key.env} saved`));
3607
+ } else console.log(chalk.default.gray(` – ${key.env} skipped`));
3608
+ }
3609
+ }
3610
+ await fs_extra.default.ensureDir(path.default.dirname(envPath));
3611
+ await fs_extra.default.writeFile(envPath, envLines.filter(Boolean).join("\n") + "\n", "utf8");
3612
+ console.log(chalk.default.green(`\n ✔ Integration keys saved to ~/.hyperclaw/.env`));
3613
+ console.log(chalk.default.gray(" To add more later: hyperclaw config set-key KEY value\n"));
3614
+ }
3615
+ async testConnections(configs) {
3616
+ for (const [channelId, cfg] of Object.entries(configs)) {
3617
+ const ch = CHANNELS.find((c) => c.id === channelId);
3618
+ if (!ch) continue;
3619
+ const skipIds = [
3620
+ "whatsapp-baileys",
3621
+ "voice-call",
3622
+ "web",
3623
+ "imessage-native",
3624
+ "irc",
3625
+ "signal",
3626
+ "tlon"
3627
+ ];
3628
+ if (skipIds.includes(channelId)) {
3629
+ console.log(chalk.default.gray(` ${ch.emoji} ${ch.name} — skipped (requires runtime setup)`));
3630
+ continue;
3631
+ }
3632
+ if (!cfg?.token) {
3633
+ console.log(chalk.default.gray(` ${ch.emoji} ${ch.name} — skipped (no token)`));
3634
+ continue;
3635
+ }
3636
+ const s = (0, ora.default)(` Testing ${ch.emoji} ${ch.name}...`).start();
3637
+ try {
3638
+ const ok = await this.validateChannelToken(channelId, cfg);
3639
+ if (ok) s.succeed(chalk.default.green(`${ch.emoji} ${ch.name} — token valid`));
3640
+ else {
3641
+ s.fail(chalk.default.red(`${ch.emoji} ${ch.name} — invalid token or wrong credentials`));
3642
+ const { getConfigPath } = await Promise.resolve().then(() => require("./paths-D-QecARF.js"));
3643
+ console.log(chalk.default.gray(` Fix: hyperclaw channels add ${channelId}`));
3644
+ console.log(chalk.default.gray(` Or edit: ${getConfigPath()} (channelConfigs.${channelId})`));
3645
+ }
3646
+ } catch (e) {
3647
+ s.warn(chalk.default.yellow(`${ch.emoji} ${ch.name} — could not verify (${e.message?.slice(0, 60) ?? "timeout"})`));
3648
+ }
3649
+ }
3650
+ }
3651
+ async validateChannelToken(channelId, cfg) {
3652
+ const token = cfg.token ?? "";
3653
+ const timeout = 8e3;
3654
+ const fetchWithTimeout = async (url, init) => {
3655
+ const ctrl = new AbortController();
3656
+ const t = setTimeout(() => ctrl.abort(), timeout);
3657
+ try {
3658
+ return await fetch(url, {
3659
+ ...init,
3660
+ signal: ctrl.signal
3661
+ });
3662
+ } finally {
3663
+ clearTimeout(t);
3664
+ }
3665
+ };
3666
+ switch (channelId) {
3667
+ case "telegram": {
3668
+ const r = await fetchWithTimeout(`https://api.telegram.org/bot${token}/getMe`);
3669
+ const j = await r.json();
3670
+ return j?.ok === true;
3671
+ }
3672
+ case "discord": {
3673
+ const r = await fetchWithTimeout("https://discord.com/api/v10/users/@me", { headers: { Authorization: `Bot ${token}` } });
3674
+ const j = await r.json();
3675
+ return !!j?.id;
3676
+ }
3677
+ case "slack": {
3678
+ const r = await fetchWithTimeout("https://slack.com/api/auth.test", {
3679
+ method: "POST",
3680
+ headers: {
3681
+ Authorization: `Bearer ${token}`,
3682
+ "Content-Type": "application/json"
3683
+ }
3684
+ });
3685
+ const j = await r.json();
3686
+ return j?.ok === true;
3687
+ }
3688
+ case "matrix": {
3689
+ const homeserver = (cfg.homeserver ?? "").replace(/\/$/, "");
3690
+ if (!homeserver) return false;
3691
+ const r = await fetchWithTimeout(`${homeserver}/_matrix/client/v3/account/whoami`, { headers: { Authorization: `Bearer ${token}` } });
3692
+ const j = await r.json();
3693
+ return !!j?.user_id;
3694
+ }
3695
+ case "line": {
3696
+ const r = await fetchWithTimeout("https://api.line.me/v2/bot/info", { headers: { Authorization: `Bearer ${token}` } });
3697
+ const j = await r.json();
3698
+ return !!j?.userId;
3699
+ }
3700
+ case "viber": {
3701
+ const r = await fetchWithTimeout("https://chatapi.viber.com/pa/get_account_info", {
3702
+ method: "POST",
3703
+ headers: {
3704
+ "X-Viber-Auth-Token": token,
3705
+ "Content-Type": "application/json"
3706
+ },
3707
+ body: JSON.stringify({})
3708
+ });
3709
+ const j = await r.json();
3710
+ return j?.status === 0;
3711
+ }
3712
+ case "mattermost": {
3713
+ const serverUrl = (cfg.serverUrl ?? "").replace(/\/$/, "");
3714
+ if (!serverUrl) return false;
3715
+ const r = await fetchWithTimeout(`${serverUrl}/api/v4/users/me`, { headers: { Authorization: `Bearer ${token}` } });
3716
+ const j = await r.json();
3717
+ return !!j?.id;
3718
+ }
3719
+ case "messenger":
3720
+ case "instagram":
3721
+ case "whatsapp": {
3722
+ const r = await fetchWithTimeout(`https://graph.facebook.com/me?access_token=${token}`);
3723
+ const j = await r.json();
3724
+ return !!j?.id;
3725
+ }
3726
+ case "feishu": {
3727
+ const appSecret = cfg.appSecret ?? "";
3728
+ const r = await fetchWithTimeout("https://open.feishu.cn/open-apis/auth/v3/app_access_token/internal", {
3729
+ method: "POST",
3730
+ headers: { "Content-Type": "application/json" },
3731
+ body: JSON.stringify({
3732
+ app_id: token,
3733
+ app_secret: appSecret
3734
+ })
3735
+ });
3736
+ const j = await r.json();
3737
+ return j?.code === 0;
3738
+ }
3739
+ case "googlechat": return /^https:\/\/chat\.googleapis\.com\/v1\/spaces\/.+\/messages/.test(token);
3740
+ case "nostr": return /^[0-9a-f]{64}$/.test(token) || token.startsWith("nsec");
3741
+ case "email": {
3742
+ const user = cfg.user ?? "";
3743
+ const imapHost = cfg.imapHost ?? "";
3744
+ const smtpHost = cfg.smtpHost ?? "";
3745
+ return /^[^@]+@[^@]+\.[^@]+$/.test(user) && imapHost.length > 3 && smtpHost.length > 3 && token.length > 3;
3746
+ }
3747
+ case "nextcloud": {
3748
+ const serverUrl = (cfg.serverUrl ?? "").replace(/\/$/, "");
3749
+ const username = cfg.username ?? "";
3750
+ if (!serverUrl || !username) return false;
3751
+ const creds = Buffer.from(`${username}:${token}`).toString("base64");
3752
+ const r = await fetchWithTimeout(`${serverUrl}/ocs/v1.php/cloud/user?format=json`, { headers: {
3753
+ Authorization: `Basic ${creds}`,
3754
+ "OCS-APIREQUEST": "true"
3755
+ } });
3756
+ const j = await r.json();
3757
+ return j?.ocs?.meta?.status === "ok";
3758
+ }
3759
+ case "twitter": return token.length > 0 && (cfg.apiKeySecret ?? "").length > 0 && (cfg.accessToken ?? "").length > 0 && (cfg.accessTokenSecret ?? "").length > 0;
3760
+ case "msteams": return token.length > 8 && (cfg.appPassword ?? "").length > 3;
3761
+ case "zalo": return token.length > 0 && (cfg.secretKey ?? "").length > 0;
3762
+ case "imessage": return token.length > 3;
3763
+ default: return token.length > 0;
3764
+ }
3765
+ }
3766
+ showSuccessScreen(data, gatewayReachable = false) {
3767
+ const channels = Object.keys(data.channelConfigs || {});
3768
+ const gwUrl = `ws://localhost:${data.gatewayConfig?.port ?? 18789}`;
3769
+ const hasEmail = channels.includes("email");
3770
+ const hasHyperClawBot = !!data.hyperclawbotConfig?.token;
3771
+ const isRemoteMode = data.gatewayConfig?.mode === "remote";
3772
+ const needsStart = data.gatewayConfig && !isRemoteMode && !gatewayReachable && channels.length > 0;
3773
+ const cmdLines = [
3774
+ chalk.default.gray(" 📊 hyperclaw dashboard Dashboard (terminal)"),
3775
+ chalk.default.gray(" 🧩 hyperclaw hub — Skill hub"),
3776
+ chalk.default.gray(" 🌐 hyperclaw gateway status — Gateway panel"),
3777
+ chalk.default.gray(" 🩸 hyperclaw ") + chalk.default.red("daemon") + chalk.default.gray(" status — Service status"),
3778
+ chalk.default.gray(" 🎙️ hyperclaw voice — Voice settings"),
3779
+ chalk.default.gray(" 🎨 hyperclaw canvas show — AI canvas")
3780
+ ];
3781
+ if (needsStart) cmdLines.unshift(chalk.default.yellow(" 🚀 hyperclaw daemon start ") + chalk.default.bold("Start your bot (Telegram/Discord need this!)"));
3782
+ if (hasHyperClawBot) cmdLines.push(chalk.default.gray(" 🤖 hyperclaw bot start — HyperClawBot remote control"));
3783
+ if (hasEmail) cmdLines.push(chalk.default.gray(" 📧 hyperclaw gmail watch-setup — Gmail Pub/Sub (real-time)"));
3784
+ cmdLines.push(chalk.default.gray(" 📱 hyperclaw nodes — Mobile nodes (Connect tab)"));
3785
+ cmdLines.push(chalk.default.gray(" ⏰ hyperclaw cron list — Scheduled tasks"));
3786
+ const lines = [
3787
+ `${chalk.default.gray("🤖 Agent:")} ${chalk.default.hex("#06b6d4")(data.identity?.agentName)} (you: ${data.identity?.userName})`,
3788
+ `${chalk.default.gray("⚡ Model:")} ${data.providerConfig?.modelId}`,
3789
+ `${chalk.default.gray("🔌 Provider:")} ${data.providerConfig?.providerId}`,
3790
+ `${chalk.default.gray("🌐 Gateway:")} ${gwUrl}`,
3791
+ `${chalk.default.gray("📡 Channels:")} ${channels.length ? channels.join(", ") : "CLI only"}`,
3792
+ "",
3793
+ chalk.default.hex("#06b6d4")("Commands:"),
3794
+ ...cmdLines
3795
+ ].join("\n");
3796
+ console.log("\n" + (0, boxen.default)(chalk.default.hex("#06b6d4")("🦅 HyperClaw v5.3.45 ready!\n\n") + lines, {
3797
+ padding: 1,
3798
+ borderStyle: "round",
3799
+ borderColor: "cyan",
3800
+ margin: 1,
3801
+ backgroundColor: "#0a0a0a"
3802
+ }));
3803
+ }
3804
+ };
3805
+
3806
+ //#endregion
3807
+ Object.defineProperty(exports, 'HyperClawWizard', {
3808
+ enumerable: true,
3809
+ get: function () {
3810
+ return HyperClawWizard;
3811
+ }
3812
+ });