hyperclaw 4.0.2 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/README.md +54 -3
  2. package/dist/a2ui-protocol-CfBI44-Q.js +75 -0
  3. package/dist/agents-routing-ChHiZp36.js +327 -0
  4. package/dist/agents-routing-ChqZ6l2S.js +4 -0
  5. package/dist/api-keys-guide-BCcOl0Q7.js +149 -0
  6. package/dist/audit-BaIiyWFu.js +441 -0
  7. package/dist/bounty-tools-DWudyZie.js +211 -0
  8. package/dist/browser-tools-BsTeGMnX.js +5 -0
  9. package/dist/browser-tools-D8_rLe2p.js +179 -0
  10. package/dist/claw-tasks-CgTsiNE8.js +80 -0
  11. package/dist/connector-5N0-X_xs.js +194 -0
  12. package/dist/connector-B3v0qcXg.js +425 -0
  13. package/dist/connector-B8R3iBY1.js +280 -0
  14. package/dist/connector-BAM-08NN.js +189 -0
  15. package/dist/connector-BC8FIVu4.js +181 -0
  16. package/dist/connector-BDmwwaVc.js +213 -0
  17. package/dist/connector-BGjbBy69.js +225 -0
  18. package/dist/connector-BO2SRzfG.js +218 -0
  19. package/dist/connector-BfXky0L3.js +167 -0
  20. package/dist/connector-BiiSJpx3.js +192 -0
  21. package/dist/connector-BnDmIhIu.js +85 -0
  22. package/dist/connector-C1HSoUyk.js +189 -0
  23. package/dist/connector-CKQHZOXg.js +568 -0
  24. package/dist/connector-CRl-iidy.js +239 -0
  25. package/dist/connector-Ci9glMD-.js +340 -0
  26. package/dist/connector-CjtZIEDj.js +181 -0
  27. package/dist/connector-Ck6JtOsX.js +531 -0
  28. package/dist/connector-D8Kelee0.js +286 -0
  29. package/dist/connector-DAnRJ0oP.js +162 -0
  30. package/dist/connector-DXTp5PE8.js +508 -0
  31. package/dist/connector-Dih6dUPP.js +173 -0
  32. package/dist/connector-DqTH_tPX.js +182 -0
  33. package/dist/connector-DrnEiiyP.js +419 -0
  34. package/dist/connector-DtR5GGTX.js +167 -0
  35. package/dist/connector-Tky_qS_K.js +350 -0
  36. package/dist/connector-ZSc3oTTy.js +305 -0
  37. package/dist/connector-sW5yhU1m.js +498 -0
  38. package/dist/connector-u3ICd3Ic.js +552 -0
  39. package/dist/cost-tracker-DD9wtWsr.js +103 -0
  40. package/dist/credentials-store-C6ir0Dae.js +4 -0
  41. package/dist/credentials-store-H13LqOwJ.js +77 -0
  42. package/dist/cron-tasks-Bli7Kzd2.js +82 -0
  43. package/dist/daemon-Bg4GtCmc.js +318 -0
  44. package/dist/daemon-DhmwY8k4.js +5 -0
  45. package/dist/delivery-BmIYy9VQ.js +4 -0
  46. package/dist/delivery-pWUPBp1F.js +95 -0
  47. package/dist/destructive-gate-D6vWOdEl.js +101 -0
  48. package/dist/developer-keys-CPWT7Q6S.js +8 -0
  49. package/dist/developer-keys-DrrcUqFa.js +127 -0
  50. package/dist/doctor-BvCe8BBk.js +230 -0
  51. package/dist/doctor-CxyPLYsJ.js +6 -0
  52. package/dist/engine-CEDSqXfw.js +256 -0
  53. package/dist/engine-Da4JMNpI.js +7 -0
  54. package/dist/env-resolve-CiXbWYwe.js +10 -0
  55. package/dist/env-resolve-CmGWhWXJ.js +115 -0
  56. package/dist/extraction-tools-HOZstZ0y.js +91 -0
  57. package/dist/extraction-tools-m4lmAv7l.js +5 -0
  58. package/dist/form_data-Cz040rio.js +8657 -0
  59. package/dist/gmail-watch-setup-Du7DVV7S.js +40 -0
  60. package/dist/health-B-asI__D.js +6 -0
  61. package/dist/health-Ds2YlpTB.js +152 -0
  62. package/dist/heartbeat-engine-BYT5ayQH.js +83 -0
  63. package/dist/hub-D0XwdjM-.js +515 -0
  64. package/dist/hub-LiD5Iztb.js +6 -0
  65. package/dist/hyperclawbot-zvczQgKx.js +505 -0
  66. package/dist/inference-BKVkBREb.js +6 -0
  67. package/dist/inference-DCXH4Q3x.js +922 -0
  68. package/dist/knowledge-graph-iBG76fvm.js +131 -0
  69. package/dist/loader-CC45xGpC.js +4 -0
  70. package/dist/loader-CnEdOyjT.js +400 -0
  71. package/dist/logger-ybOp7VOC.js +83 -0
  72. package/dist/manager-03ipO9R0.js +105 -0
  73. package/dist/manager-BpDfbDjg.js +117 -0
  74. package/dist/manager-Bxl0sqlh.js +4 -0
  75. package/dist/manager-CrVDn6eN.js +6 -0
  76. package/dist/manager-FCgF1plu.js +218 -0
  77. package/dist/manager-rgCsaWT1.js +40 -0
  78. package/dist/mcp-CfoSU4Uz.js +139 -0
  79. package/dist/mcp-loader-DkRBsLpk.js +94 -0
  80. package/dist/memory-BlHL7JCO.js +4 -0
  81. package/dist/memory-DsS_eFvJ.js +270 -0
  82. package/dist/memory-auto-BkvtSFUw.js +5 -0
  83. package/dist/memory-auto-Bnz_-1wP.js +306 -0
  84. package/dist/memory-integration-cSYkZyEo.js +91 -0
  85. package/dist/moltbook-BtLDZTfM.js +81 -0
  86. package/dist/node-Dw2Gi-cP.js +222 -0
  87. package/dist/nodes-registry-B8dmrlLv.js +52 -0
  88. package/dist/oauth-flow-DQPvMHRH.js +150 -0
  89. package/dist/oauth-provider-Uo4Nib_c.js +110 -0
  90. package/dist/observability-BV-Yx0V9.js +89 -0
  91. package/dist/onboard-0WoDxbv_.js +10 -0
  92. package/dist/onboard-BXNXCQp4.js +4070 -0
  93. package/dist/orchestrator-DmnEvMaL.js +189 -0
  94. package/dist/orchestrator-RI3bpqqc.js +6 -0
  95. package/dist/pairing-6iM27aD8.js +196 -0
  96. package/dist/pairing-dGoiGepK.js +4 -0
  97. package/dist/pc-access-CgCsYrpt.js +8 -0
  98. package/dist/pc-access-_iH2aorG.js +819 -0
  99. package/dist/pending-approval-CUXjysAo.js +22 -0
  100. package/dist/reminders-store-Drjed_-h.js +58 -0
  101. package/dist/renderer-BVQrd0_g.js +225 -0
  102. package/dist/rules-BE4GV6cV.js +103 -0
  103. package/dist/run-main.js +1607 -443
  104. package/dist/runner-DatMMYYE.js +1271 -0
  105. package/dist/sdk/index.js +2 -2
  106. package/dist/sdk/index.mjs +2 -2
  107. package/dist/security-BqNyT4ID.js +4 -0
  108. package/dist/security-tpgqPWWH.js +73 -0
  109. package/dist/server-D4wVHiX9.js +4 -0
  110. package/dist/server-Dh3JlBFB.js +1255 -0
  111. package/dist/session-store-BUiPz0Vv.js +5 -0
  112. package/dist/session-store-is4B6qmD.js +113 -0
  113. package/dist/sessions-tools-CbUTFe4i.js +5 -0
  114. package/dist/sessions-tools-CeqD7iil.js +95 -0
  115. package/dist/skill-loader-BaNLVmJy.js +7 -0
  116. package/dist/skill-loader-HgpF6Vqs.js +159 -0
  117. package/dist/skill-runtime-CJN24QPW.js +102 -0
  118. package/dist/skill-runtime-w1ig_lcw.js +5 -0
  119. package/dist/src-BxPHKO5x.js +63 -0
  120. package/dist/src-DIc-L2IG.js +20 -0
  121. package/dist/src-g_rNx5rh.js +458 -0
  122. package/dist/sub-agent-tools-CHQoHz9c.js +39 -0
  123. package/dist/theme-DcxwcUgZ.js +180 -0
  124. package/dist/theme-cx0fkgWC.js +8 -0
  125. package/dist/tool-policy-CNT-mF2Z.js +189 -0
  126. package/dist/tts-elevenlabs-BRosZv-f.js +61 -0
  127. package/dist/update-check-C2Dz85wJ.js +81 -0
  128. package/dist/vision-BMmiIKy7.js +121 -0
  129. package/dist/vision-tools-DVuYc17I.js +51 -0
  130. package/dist/vision-tools-U3YC4L-g.js +5 -0
  131. package/dist/voice-transcription-B555DbWR.js +138 -0
  132. package/dist/website-watch-tools-DFMrJU-R.js +139 -0
  133. package/dist/website-watch-tools-Du3W5sN7.js +5 -0
  134. package/package.json +1 -1
package/dist/run-main.js CHANGED
@@ -1,20 +1,22 @@
1
1
  const require_chunk = require('./chunk-jS-bbMI5.js');
2
2
  require('./paths-AIyBxIzm.js');
3
3
  require('./paths-DPovhojT.js');
4
- require('./env-resolve-szSWl0UF.js');
5
- const require_onboard = require('./onboard-Bd_wsYdi.js');
6
- require('./server-BRlCEjyT.js');
7
- require('./theme-LUTKWUWd.js');
8
- const require_hub = require('./hub-FrPTA33j.js');
9
- const require_manager = require('./manager-Dm8nrMFx.js');
10
- const require_manager$1 = require('./manager-Cwzj7w5R.js');
11
- const require_memory = require('./memory-BVFGkxxK.js');
12
- const require_loader = require('./loader-CISCqBto.js');
13
- const require_agents_routing = require('./agents-routing-683Q2JGp.js');
14
- const require_pairing = require('./pairing-DU0_J28n.js');
15
- const require_doctor = require('./doctor-3oi89QIc.js');
16
- const require_security = require('./security-C-5URby1.js');
17
- const require_developer_keys = require('./developer-keys-JaJK3T27.js');
4
+ require('./env-resolve-CmGWhWXJ.js');
5
+ const require_onboard = require('./onboard-BXNXCQp4.js');
6
+ require('./server-Dh3JlBFB.js');
7
+ const require_daemon = require('./daemon-Bg4GtCmc.js');
8
+ require('./theme-DcxwcUgZ.js');
9
+ const require_hub = require('./hub-D0XwdjM-.js');
10
+ const require_manager = require('./manager-rgCsaWT1.js');
11
+ const require_manager$1 = require('./manager-03ipO9R0.js');
12
+ const require_memory = require('./memory-DsS_eFvJ.js');
13
+ const require_loader = require('./loader-CnEdOyjT.js');
14
+ const require_agents_routing = require('./agents-routing-ChHiZp36.js');
15
+ const require_pairing = require('./pairing-6iM27aD8.js');
16
+ const require_doctor = require('./doctor-BvCe8BBk.js');
17
+ const require_health = require('./health-Ds2YlpTB.js');
18
+ const require_security = require('./security-tpgqPWWH.js');
19
+ const require_developer_keys = require('./developer-keys-DrrcUqFa.js');
18
20
  const commander = require_chunk.__toESM(require("commander"));
19
21
  const chalk = require_chunk.__toESM(require("chalk"));
20
22
  const inquirer = require_chunk.__toESM(require("inquirer"));
@@ -22,10 +24,13 @@ const ora = require_chunk.__toESM(require("ora"));
22
24
  const fs_extra = require_chunk.__toESM(require("fs-extra"));
23
25
  const path = require_chunk.__toESM(require("path"));
24
26
  const os = require_chunk.__toESM(require("os"));
27
+ const crypto = require_chunk.__toESM(require("crypto"));
25
28
  const child_process = require_chunk.__toESM(require("child_process"));
26
29
  const util = require_chunk.__toESM(require("util"));
30
+ const http = require_chunk.__toESM(require("http"));
27
31
  const fs = require_chunk.__toESM(require("fs"));
28
32
  const readline = require_chunk.__toESM(require("readline"));
33
+ const net = require_chunk.__toESM(require("net"));
29
34
 
30
35
  //#region src/cli/dashboard.ts
31
36
  var Dashboard = class {
@@ -59,7 +64,7 @@ var Dashboard = class {
59
64
  return c(`║ `) + content + " ".repeat(pad) + c(`║`);
60
65
  };
61
66
  console.log(c(`╔${line}╗`));
62
- console.log(c(`║`) + chalk.default.bold.hex("#06b6d4")(`${"🦅 HYPERCLAW v4.0.2 — GATEWAY DASHBOARD".padStart(45).padEnd(w)}`) + c(`║`));
67
+ console.log(c(`║`) + chalk.default.bold.hex("#06b6d4")(`${"🦅 HYPERCLAW v5.0.0 — GATEWAY DASHBOARD".padStart(45).padEnd(w)}`) + c(`║`));
63
68
  console.log(c(`╠${line}╣`));
64
69
  console.log(row(`${statusDot} Gateway ${statusText} ${chalk.default.gray("│")} ws://localhost:${port} ${chalk.default.gray("│")} Agent: ${c(agent)}`));
65
70
  console.log(row(`${c("◆")} Model ${chalk.default.gray(model.slice(0, 30))} ${chalk.default.gray("│")} User: ${c(user)}`));
@@ -140,7 +145,7 @@ async function recordAudio(outFile, seconds) {
140
145
  async function transcribeWhisper(filePath, lang) {
141
146
  const apiKey = process.env.OPENAI_API_KEY || process.env.ANTHROPIC_API_KEY;
142
147
  if (!apiKey) throw new Error("No OPENAI_API_KEY set");
143
- const FormData = (await Promise.resolve().then(() => require_chunk.__toDynamicImportESM()(require("./form_data-B_hIUrxU.js"))).catch(() => null))?.default;
148
+ const FormData = (await Promise.resolve().then(() => require_chunk.__toDynamicImportESM()(require("./form_data-Cz040rio.js"))).catch(() => null))?.default;
144
149
  if (!FormData) throw new Error("form-data not installed");
145
150
  const form = new FormData();
146
151
  form.append("file", fs.createReadStream(filePath), {
@@ -284,6 +289,185 @@ var VoiceEngine = class {
284
289
  }
285
290
  };
286
291
 
292
+ //#endregion
293
+ //#region src/infra/device-pairing.ts
294
+ const DEVICES_DIR = path.default.join(os.default.homedir(), ".hyperclaw", "devices");
295
+ const PENDING_FILE = path.default.join(DEVICES_DIR, "pending.json");
296
+ const PAIRED_FILE = path.default.join(DEVICES_DIR, "paired.json");
297
+ const PENDING_EXPIRY_MS = 10 * 60 * 1e3;
298
+ const TOKEN_BYTES = 16;
299
+ async function readPending() {
300
+ try {
301
+ const data = await fs_extra.default.readJson(PENDING_FILE);
302
+ return Array.isArray(data) ? data : [];
303
+ } catch {
304
+ return [];
305
+ }
306
+ }
307
+ async function writePending(entries) {
308
+ await fs_extra.default.ensureDir(DEVICES_DIR);
309
+ await fs_extra.default.writeJson(PENDING_FILE, entries, {
310
+ spaces: 2,
311
+ mode: 384
312
+ });
313
+ }
314
+ async function readPaired() {
315
+ try {
316
+ const data = await fs_extra.default.readJson(PAIRED_FILE);
317
+ return Array.isArray(data) ? data : [];
318
+ } catch {
319
+ return [];
320
+ }
321
+ }
322
+ async function writePaired(devices) {
323
+ await fs_extra.default.ensureDir(DEVICES_DIR);
324
+ await fs_extra.default.writeJson(PAIRED_FILE, devices, {
325
+ spaces: 2,
326
+ mode: 384
327
+ });
328
+ }
329
+ var DevicePairingStore = class {
330
+ /**
331
+
332
+ * Creates a pending pairing request and returns:
333
+
334
+ * - requestId: show to user for approve/reject
335
+
336
+ * - setupCode: base64-encoded JSON {url, token} — send to device
337
+
338
+ */
339
+ async createRequest(gatewayUrl, opts) {
340
+ const now = Date.now();
341
+ const all = (await readPending()).filter((e) => new Date(e.expiresAt).getTime() > now);
342
+ const requestId = crypto.default.randomBytes(6).toString("hex");
343
+ const token = crypto.default.randomBytes(TOKEN_BYTES).toString("hex");
344
+ const expiresAt = new Date(now + PENDING_EXPIRY_MS).toISOString();
345
+ const entry = {
346
+ requestId,
347
+ token,
348
+ expiresAt,
349
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
350
+ ...opts?.deviceName ? { deviceName: opts.deviceName } : {},
351
+ ...opts?.platform ? { platform: opts.platform } : {}
352
+ };
353
+ all.push(entry);
354
+ await writePending(all);
355
+ const payload = {
356
+ url: gatewayUrl,
357
+ token
358
+ };
359
+ const setupCode = Buffer.from(JSON.stringify(payload)).toString("base64");
360
+ return {
361
+ requestId,
362
+ setupCode,
363
+ expiresAt
364
+ };
365
+ }
366
+ async listPending() {
367
+ const now = Date.now();
368
+ const all = await readPending();
369
+ return all.filter((e) => new Date(e.expiresAt).getTime() > now);
370
+ }
371
+ async listPaired() {
372
+ return readPaired();
373
+ }
374
+ async approve(requestId) {
375
+ const now = Date.now();
376
+ const pending = await readPending();
377
+ const idx = pending.findIndex((e) => e.requestId === requestId && new Date(e.expiresAt).getTime() > now);
378
+ if (idx === -1) return null;
379
+ const entry = pending[idx];
380
+ pending.splice(idx, 1);
381
+ await writePending(pending);
382
+ const deviceId = `device-${crypto.default.randomBytes(4).toString("hex")}`;
383
+ const longToken = crypto.default.randomBytes(32).toString("hex");
384
+ const device = {
385
+ deviceId,
386
+ requestId: entry.requestId,
387
+ token: longToken,
388
+ pairedAt: (/* @__PURE__ */ new Date()).toISOString(),
389
+ ...entry.deviceName ? { deviceName: entry.deviceName } : {},
390
+ ...entry.platform ? { platform: entry.platform } : {}
391
+ };
392
+ const paired = await readPaired();
393
+ paired.push(device);
394
+ await writePaired(paired);
395
+ return device;
396
+ }
397
+ async reject(requestId) {
398
+ const pending = await readPending();
399
+ const idx = pending.findIndex((e) => e.requestId === requestId);
400
+ if (idx === -1) return false;
401
+ pending.splice(idx, 1);
402
+ await writePending(pending);
403
+ return true;
404
+ }
405
+ async unpair(deviceId) {
406
+ const paired = await readPaired();
407
+ const idx = paired.findIndex((d) => d.deviceId === deviceId);
408
+ if (idx === -1) return false;
409
+ paired.splice(idx, 1);
410
+ await writePaired(paired);
411
+ return true;
412
+ }
413
+ async verifyToken(token) {
414
+ const now = Date.now();
415
+ const pending = await readPending();
416
+ const pendingMatch = pending.find((e) => e.token === token && new Date(e.expiresAt).getTime() > now);
417
+ if (pendingMatch) return this.approve(pendingMatch.requestId);
418
+ const paired = await readPaired();
419
+ return paired.find((d) => d.token === token) ?? null;
420
+ }
421
+ async touchDevice(deviceId) {
422
+ const paired = await readPaired();
423
+ const device = paired.find((d) => d.deviceId === deviceId);
424
+ if (device) {
425
+ device.lastSeenAt = (/* @__PURE__ */ new Date()).toISOString();
426
+ await writePaired(paired);
427
+ }
428
+ }
429
+ static parseSetupCode(setupCode) {
430
+ try {
431
+ const json = Buffer.from(setupCode, "base64").toString("utf8");
432
+ const parsed = JSON.parse(json);
433
+ if (typeof parsed.url === "string" && typeof parsed.token === "string") return parsed;
434
+ return null;
435
+ } catch {
436
+ return null;
437
+ }
438
+ }
439
+ async showCLI() {
440
+ const now = Date.now();
441
+ const pending = (await readPending()).filter((e) => new Date(e.expiresAt).getTime() > now);
442
+ const paired = await readPaired();
443
+ console.log(chalk.default.bold.cyan("\n 📱 DEVICE PAIRING\n"));
444
+ if (pending.length > 0) {
445
+ console.log(chalk.default.yellow(" Pending requests:\n"));
446
+ for (const e of pending) {
447
+ const expiresIn = Math.round((new Date(e.expiresAt).getTime() - now) / 6e4);
448
+ const name = e.deviceName ? chalk.default.white(` "${e.deviceName}"`) : "";
449
+ const plat = e.platform ? chalk.default.gray(` (${e.platform})`) : "";
450
+ console.log(` ${chalk.default.yellow("○")} ${chalk.default.bold(e.requestId)}${name}${plat} ${chalk.default.gray(`expires in ${expiresIn}m`)}`);
451
+ }
452
+ console.log();
453
+ }
454
+ if (paired.length > 0) {
455
+ console.log(chalk.default.green(" Paired devices:\n"));
456
+ for (const d of paired) {
457
+ const name = d.deviceName ? chalk.default.white(` "${d.deviceName}"`) : "";
458
+ const plat = d.platform ? chalk.default.gray(` (${d.platform})`) : "";
459
+ const seen = d.lastSeenAt ? chalk.default.gray(` last seen: ${new Date(d.lastSeenAt).toLocaleDateString()}`) : "";
460
+ console.log(` ${chalk.default.green("●")} ${chalk.default.bold(d.deviceId)}${name}${plat} ${chalk.default.gray(`paired: ${new Date(d.pairedAt).toLocaleDateString()}`)}${seen}`);
461
+ }
462
+ console.log();
463
+ }
464
+ if (pending.length === 0 && paired.length === 0) {
465
+ console.log(chalk.default.gray(" No pending or paired devices."));
466
+ console.log(chalk.default.gray(" In Telegram, message your bot: /pair\n"));
467
+ }
468
+ }
469
+ };
470
+
287
471
  //#endregion
288
472
  //#region src/commands/message-send.ts
289
473
  async function sendMessage(opts) {
@@ -345,17 +529,28 @@ const CHANNELS = [
345
529
  platforms: ["all"],
346
530
  tokenLabel: "Telegram Bot Token",
347
531
  tokenHint: "Get from @BotFather → /newbot",
532
+ extraFields: [{
533
+ name: "dmPolicy",
534
+ label: "DM policy",
535
+ hint: "pairing (default) | allowlist | open | disabled",
536
+ required: false
537
+ }, {
538
+ name: "groupActivation",
539
+ label: "Group activation",
540
+ hint: "mention (default) | always",
541
+ required: false
542
+ }],
348
543
  setupSteps: [
349
- "1. Open Telegram and search for @BotFather (the official bot for creating bots).",
350
- "2. Start a conversation with /start and type /newbot to create a new bot.",
351
- "3. Give the bot a name (e.g. \"My HyperClaw Bot\") and a username ending in \"bot\" (e.g. my_hyperclaw_bot).",
352
- "4. @BotFather will send you the Bot Token a string starting with 7xxxxxx:AAH... Keep it secret!",
353
- "5. Copy the token and paste it below.",
544
+ "1. Open Telegram @BotFather /newbot. Save the token.",
545
+ "2. Config: channels.telegram.botToken, dmPolicy (default: pairing), groups.",
546
+ "3. Start gateway, approve first DM: hyperclaw pairing approve telegram <CODE>",
547
+ "4. Add bot to groups; set groups[\"*\"].requireMention for mention gating.",
354
548
  "",
355
- " 🔗 t.me/BotFather"
549
+ " 🔗 docs/telegram.md — full setup"
356
550
  ],
357
551
  status: "recommended",
358
- npmPackage: "node-telegram-bot-api"
552
+ npmPackage: "node-telegram-bot-api",
553
+ notes: "DMs + groups. Pairing, allowlist, voice notes. Long polling default."
359
554
  },
360
555
  {
361
556
  id: "discord",
@@ -367,23 +562,37 @@ const CHANNELS = [
367
562
  tokenLabel: "Discord Bot Token",
368
563
  tokenHint: "discord.com/developers/applications",
369
564
  setupSteps: [
370
- "1. Go to Discord Developer Portal: https://discord.com/developers/applications",
371
- "2. Click \"New Application\", give it a name and create it.",
372
- "3. Left menu: Bot Add Bot.",
373
- "4. Click \"Reset Token\" and copy the token (keep it secret!).",
374
- "5. Settings OAuth2 General, copy the Application ID (Client ID).",
375
- "6. Optional: To add the bot to a server, Bot OAuth2 URL Generator, scope: bot.",
565
+ "1. Discord Developer Portal → New Application → Bot → Reset Token.",
566
+ "2. Enable Message Content Intent (and Server Members if needed).",
567
+ "3. OAuth2 URL Generator: scope bot + applications.commands, permissions: View Channels, Send Messages, Read Message History.",
568
+ "4. Add bot to server, enable Developer Mode, copy Server ID and User ID.",
569
+ "5. Enable DMs from server members (right‑click server Privacy Settings).",
570
+ "6. hyperclaw gateway DM the bot → hyperclaw pairing approve discord <CODE>",
376
571
  "",
377
- " 🔗 discord.com/developers/applications"
572
+ " 🔗 docs/discord-setup.md — full setup guide"
573
+ ],
574
+ extraFields: [
575
+ {
576
+ name: "listenGuildIds",
577
+ label: "Guild IDs to listen in",
578
+ hint: "[] = all. Add Server IDs to restrict.",
579
+ required: false
580
+ },
581
+ {
582
+ name: "requireMentionInGuild",
583
+ label: "Require @mention in guild",
584
+ hint: "true (default) | false",
585
+ required: false
586
+ },
587
+ {
588
+ name: "dmPolicy",
589
+ label: "DM policy",
590
+ hint: "\"pairing\" (default) | \"allowlist\" | \"open\" | \"none\"",
591
+ required: false
592
+ }
378
593
  ],
379
- extraFields: [{
380
- name: "clientId",
381
- label: "Client ID (Application ID)",
382
- hint: "From OAuth2 → General",
383
- required: true
384
- }],
385
594
  status: "recommended",
386
- npmPackage: "discord.js"
595
+ npmPackage: "ws"
387
596
  },
388
597
  {
389
598
  id: "whatsapp",
@@ -392,18 +601,38 @@ const CHANNELS = [
392
601
  requiresGateway: true,
393
602
  supportsDM: true,
394
603
  platforms: ["all"],
395
- tokenLabel: "WhatsApp Business API key",
604
+ tokenLabel: "Access Token",
396
605
  tokenHint: "business.whatsapp.com",
606
+ extraFields: [
607
+ {
608
+ name: "phoneNumberId",
609
+ label: "Phone Number ID",
610
+ hint: "From Meta API Setup",
611
+ required: true
612
+ },
613
+ {
614
+ name: "verifyToken",
615
+ label: "Webhook verify token",
616
+ hint: "Any string for webhook verification",
617
+ required: false
618
+ },
619
+ {
620
+ name: "dmPolicy",
621
+ label: "DM policy",
622
+ hint: "pairing (default) | allowlist | open | disabled",
623
+ required: false
624
+ }
625
+ ],
397
626
  setupSteps: [
398
- "1. Go to Meta for Developers: https://developers.facebook.com/",
399
- "2. My Apps Create App Business type.",
400
- "3. Add product: WhatsApp Get started.",
401
- "4. WhatsApp API Setup: copy the Temporary access token or create a permanent one.",
402
- "5. You also need a Phone Number ID and WhatsApp Business Account ID.",
627
+ "1. Meta for Developers → Create App → Business → Add WhatsApp.",
628
+ "2. API Setup: copy Phone Number ID and Access Token.",
629
+ "3. Webhook: https://<host>/webhook/whatsapp, subscribe to messages.",
630
+ "4. Start gateway. Approve DMs: hyperclaw pairing approve whatsapp <CODE>",
403
631
  "",
404
- " 🔗 developers.facebook.combusiness.whatsapp.com"
632
+ " 🔗 docs/whatsapp.mdfull setup"
405
633
  ],
406
634
  status: "available",
635
+ notes: "Meta Business API. Webhook required.",
407
636
  npmPackage: void 0
408
637
  },
409
638
  {
@@ -413,42 +642,87 @@ const CHANNELS = [
413
642
  requiresGateway: true,
414
643
  supportsDM: true,
415
644
  platforms: ["all"],
645
+ extraFields: [{
646
+ name: "dmPolicy",
647
+ label: "DM policy",
648
+ hint: "pairing (default) | allowlist | open | disabled",
649
+ required: false
650
+ }],
416
651
  setupSteps: [
417
- "1. No Meta Business API needed — uses WhatsApp Web.",
418
- "2. Make sure you have installed: npm install @whiskeysockets/baileys",
419
- "3. Start the gateway. On first connection a QR code will appear.",
420
- "4. Scan the QR with your phone (WhatsApp → Linked Devices → Link a device).",
421
- "5. After connecting, the session is saved — no QR needed again.",
652
+ "1. No Meta API — uses WhatsApp Web. Install: npm install @whiskeysockets/baileys",
653
+ "2. hyperclaw channels add whatsapp-baileys, then hyperclaw gateway",
654
+ "3. Scan QR (WhatsApp Linked Devices Link a device)",
655
+ "4. Approve first DM: hyperclaw pairing approve whatsapp-baileys <CODE>",
422
656
  "",
423
- " 📖 docs: github.com/WhiskeySockets/Baileys"
657
+ " 🔗 docs/whatsapp.md — full setup"
424
658
  ],
425
659
  status: "available",
426
- notes: "WhatsApp Web via Baileys no Meta Business. Scan QR on first run.",
660
+ notes: "WhatsApp Web via Baileys. No Meta Business. Pairing, voice notes.",
427
661
  npmPackage: "@whiskeysockets/baileys"
428
662
  },
429
663
  {
430
664
  id: "slack",
431
665
  name: "Slack",
432
666
  emoji: "💼",
433
- requiresGateway: false,
667
+ requiresGateway: true,
434
668
  supportsDM: true,
435
669
  platforms: ["all"],
436
670
  tokenLabel: "Slack Bot Token (xoxb-...)",
437
- extraFields: [{
438
- name: "signingSecret",
439
- label: "Signing Secret",
440
- required: true
441
- }],
671
+ extraFields: [
672
+ {
673
+ name: "appToken",
674
+ label: "App Token (xapp-...)",
675
+ hint: "Required for Socket Mode (default) — connections:write scope",
676
+ required: false
677
+ },
678
+ {
679
+ name: "signingSecret",
680
+ label: "Signing Secret",
681
+ hint: "Required for HTTP Events API mode only",
682
+ required: false
683
+ },
684
+ {
685
+ name: "mode",
686
+ label: "Connection mode",
687
+ hint: "socket (default) | http",
688
+ required: false
689
+ },
690
+ {
691
+ name: "userToken",
692
+ label: "User Token (xoxp-...)",
693
+ hint: "Optional — for read operations",
694
+ required: false
695
+ },
696
+ {
697
+ name: "ackReaction",
698
+ label: "Ack reaction emoji",
699
+ hint: "Shortcode without colons, e.g. eyes",
700
+ required: false
701
+ },
702
+ {
703
+ name: "typingReaction",
704
+ label: "Typing reaction emoji",
705
+ hint: "Shortcode, e.g. hourglass_flowing_sand",
706
+ required: false
707
+ }
708
+ ],
442
709
  setupSteps: [
443
710
  "1. Go to api.slack.com/apps → Create New App → From scratch.",
444
- "2. Give it a name and choose a workspace.",
445
- "3. OAuth & Permissions: Add Bot Token Scopes (chat:write, users:read, im:read, im:history, etc.).",
446
- "4. Install App to workspace copy the \"Bot User OAuth Token\" (starts with xoxb-).",
447
- "5. Basic Information App Credentials Signing Secret copy it.",
711
+ "2. Socket Mode (default no public URL needed):",
712
+ " a. Settings Socket Mode Enable Socket Mode.",
713
+ " b. Settings Basic Information App-Level Tokens Generate Token (connections:write) copy xapp-...",
714
+ " c. OAuth & Permissions: add bot scopes (chat:write, im:read, im:history, channels:history, etc.).",
715
+ " d. Install App → copy Bot Token (xoxb-...).",
716
+ "3. HTTP mode (alternative):",
717
+ " a. Event Subscriptions → Request URL: https://<host>/webhook/slack.",
718
+ " b. Basic Information → Signing Secret — copy it.",
719
+ "4. Subscribe to bot events: app_mention, message.im, message.channels, message.groups, message.mpim, reaction_added.",
720
+ "5. App Home → Messages Tab → Enable.",
448
721
  "",
449
722
  " 🔗 api.slack.com/apps"
450
723
  ],
451
- status: "available",
724
+ status: "recommended",
725
+ notes: "Socket Mode (default) requires appToken. HTTP mode requires signingSecret. Supports DMs, channels, threads, reactions, streaming.",
452
726
  npmPackage: "@slack/bolt"
453
727
  },
454
728
  {
@@ -458,38 +732,160 @@ const CHANNELS = [
458
732
  requiresGateway: true,
459
733
  supportsDM: true,
460
734
  platforms: ["linux", "darwin"],
461
- tokenLabel: "Signal phone number",
462
- tokenHint: "Requires signal-cli installed",
735
+ tokenLabel: "Bot phone number (E.164)",
736
+ tokenHint: "e.g. +15551234567 — use a dedicated bot number",
463
737
  setupSteps: [
464
738
  "1. Install signal-cli: https://github.com/AsamK/signal-cli",
465
- "2. Register number: signal-cli -a +1XXXXXXXXX register",
466
- "3. Verify electronically (if available) or via SMS code.",
467
- "4. Enter your phone number here (e.g. +1XXXXXXXXX).",
468
739
  "",
469
- " 🔗 github.com/AsamK/signal-cli"
740
+ " Path A — Link existing Signal account (QR):",
741
+ " signal-cli link -n \"HyperClaw\" then scan in Signal.",
742
+ "",
743
+ " Path B — Register dedicated bot number (SMS):",
744
+ " signal-cli -a +<BOT_NUMBER> register",
745
+ " signal-cli -a +<BOT_NUMBER> register --captcha '<URL>' (if captcha required)",
746
+ " signal-cli -a +<BOT_NUMBER> verify <CODE>",
747
+ "",
748
+ "2. Set account: \"+<BOT_NUMBER>\" in config.",
749
+ "3. autoStart=true (default) spawns daemon automatically.",
750
+ " Or run daemon yourself and set httpUrl: \"http://127.0.0.1:8080\".",
751
+ "",
752
+ " Access control:",
753
+ " dmPolicy=pairing (default) — senders get pairing code (expires 1h)",
754
+ " groupPolicy=allowlist (default) — only groupAllowFrom senders trigger bot",
755
+ "",
756
+ " Chunking: textChunkLimit=4000, chunkMode=length|newline",
757
+ " Reactions: actions.reactions=true, reactionLevel=off|ack|minimal|extensive",
758
+ "",
759
+ " 🔗 github.com/AsamK/signal-cli",
760
+ " 🔗 github.com/AsamK/signal-cli/wiki/Registration-with-captcha"
761
+ ],
762
+ extraFields: [
763
+ {
764
+ name: "account",
765
+ label: "Bot number (E.164)",
766
+ hint: "+15551234567",
767
+ required: true
768
+ },
769
+ {
770
+ name: "cliPath",
771
+ label: "signal-cli path",
772
+ hint: "signal-cli (if on PATH)",
773
+ required: false
774
+ },
775
+ {
776
+ name: "httpUrl",
777
+ label: "Daemon URL (external)",
778
+ hint: "http://127.0.0.1:8080 — skips autoStart",
779
+ required: false
780
+ },
781
+ {
782
+ name: "httpPort",
783
+ label: "Daemon port",
784
+ hint: "8080 (default)",
785
+ required: false
786
+ },
787
+ {
788
+ name: "autoStart",
789
+ label: "Auto-spawn daemon",
790
+ hint: "true (default) / false",
791
+ required: false
792
+ },
793
+ {
794
+ name: "startupTimeoutMs",
795
+ label: "Startup timeout (ms)",
796
+ hint: "15000 default, max 120000",
797
+ required: false
798
+ },
799
+ {
800
+ name: "dmPolicy",
801
+ label: "DM policy",
802
+ hint: "\"pairing\" (default) | \"allowlist\" | \"open\" | \"disabled\"",
803
+ required: false
804
+ },
805
+ {
806
+ name: "groupPolicy",
807
+ label: "Group policy",
808
+ hint: "\"allowlist\" (default) | \"open\" | \"disabled\"",
809
+ required: false
810
+ },
811
+ {
812
+ name: "textChunkLimit",
813
+ label: "Text chunk limit (chars)",
814
+ hint: "4000 default",
815
+ required: false
816
+ },
817
+ {
818
+ name: "chunkMode",
819
+ label: "Chunk mode",
820
+ hint: "\"length\" (default) | \"newline\"",
821
+ required: false
822
+ }
470
823
  ],
471
824
  status: "available",
472
- notes: "Requires signal-cli to be installed and registered"
825
+ notes: "signal-cli HTTP daemon + SSE events. DMs, groups, typing, reactions, chunking, multi-account."
473
826
  },
474
827
  {
475
828
  id: "imessage",
476
- name: "iMessage",
829
+ name: "iMessage (BlueBubbles)",
477
830
  emoji: "💬",
478
831
  requiresGateway: true,
479
832
  supportsDM: true,
480
833
  platforms: ["darwin"],
834
+ tokenLabel: "BlueBubbles server URL",
835
+ extraFields: [{
836
+ name: "password",
837
+ label: "BlueBubbles password",
838
+ hint: "Set in BlueBubbles server settings",
839
+ required: true
840
+ }, {
841
+ name: "dmPolicy",
842
+ label: "DM policy",
843
+ hint: "pairing (default) | allowlist | open | disabled",
844
+ required: false
845
+ }],
481
846
  setupSteps: [
482
- "1. macOS only. You need BlueBubbles (bluebubbles.app) or Beeper bridge.",
483
- "2. BlueBubbles: install on Mac, check server URL and API key.",
484
- "3. Or Beeper: connect to iMessage via Beeper desktop app.",
485
- "4. Set server URL and token in the channel configuration.",
847
+ "1. macOS only. Install BlueBubbles server: bluebubbles.app",
848
+ "2. BlueBubbles → Settings: enable web API, set password, note Server URL.",
849
+ "3. Add channel (imessage or bluebubbles), enter serverUrl + password.",
850
+ "4. hyperclaw gateway hyperclaw pairing approve bluebubbles <CODE>",
486
851
  "",
487
- " 🔗 bluebubbles.appbeeper.com"
852
+ " 🔗 docs/bluebubbles.mdbluebubbles.app"
488
853
  ],
489
- status: os.default.platform() === "darwin" ? "available" : "unavailable",
490
- notes: "macOS only uses BlueBubbles or Beeper bridge",
854
+ status: os.default.platform() === "darwin" ? "recommended" : "unavailable",
855
+ notes: "BlueBubbles server on Mac. DMs, pairing. Groups planned.",
491
856
  npmPackage: "bluebubbles-api"
492
857
  },
858
+ {
859
+ id: "imessage-native",
860
+ name: "iMessage (imsg CLI — legacy)",
861
+ emoji: "💬",
862
+ requiresGateway: true,
863
+ supportsDM: true,
864
+ platforms: ["darwin"],
865
+ extraFields: [{
866
+ name: "cliPath",
867
+ label: "imsg binary path",
868
+ hint: "Default: imsg (must be in PATH)",
869
+ required: false
870
+ }, {
871
+ name: "dbPath",
872
+ label: "Messages DB path",
873
+ hint: "Default: ~/Library/Messages/chat.db",
874
+ required: false
875
+ }],
876
+ setupSteps: [
877
+ "1. macOS only. Install imsg: brew install steipete/tap/imsg",
878
+ "2. Verify: imsg rpc --help",
879
+ "3. Grant Full Disk Access + Automation to Terminal/Node (one-time: imsg chats --limit 1).",
880
+ "4. No token needed — imsg runs locally via JSON-RPC on stdio.",
881
+ "5. (Optional) Set cliPath if imsg is not in PATH.",
882
+ "",
883
+ " ⚠️ Legacy integration — for new setups use iMessage (BlueBubbles)",
884
+ " 🔗 github.com/steipete/imsg"
885
+ ],
886
+ status: os.default.platform() === "darwin" ? "available" : "unavailable",
887
+ notes: "Legacy — gateway spawns imsg rpc over JSON-RPC stdio. For new setups prefer BlueBubbles."
888
+ },
493
889
  {
494
890
  id: "matrix",
495
891
  name: "Matrix",
@@ -497,26 +893,84 @@ const CHANNELS = [
497
893
  requiresGateway: true,
498
894
  supportsDM: true,
499
895
  platforms: ["all"],
500
- tokenLabel: void 0,
501
- extraFields: [{
502
- name: "homeserver",
503
- label: "Homeserver URL",
504
- hint: "e.g. https://matrix.org",
505
- required: true
506
- }, {
507
- name: "accessToken",
508
- label: "Access Token",
509
- required: true
510
- }],
896
+ tokenLabel: "Access Token (syt_...)",
897
+ tokenHint: "Or set userId + password instead",
898
+ extraFields: [
899
+ {
900
+ name: "homeserver",
901
+ label: "Homeserver URL",
902
+ hint: "e.g. https://matrix.example.org",
903
+ required: true
904
+ },
905
+ {
906
+ name: "accessToken",
907
+ label: "Access Token (syt_...)",
908
+ hint: "Preferred; userId auto-fetched via /whoami",
909
+ required: false
910
+ },
911
+ {
912
+ name: "userId",
913
+ label: "Matrix User ID",
914
+ hint: "@bot:example.org — required only for password login",
915
+ required: false
916
+ },
917
+ {
918
+ name: "password",
919
+ label: "Password (alternative to token)",
920
+ hint: "Token cached to credentials file on first login",
921
+ required: false
922
+ },
923
+ {
924
+ name: "deviceName",
925
+ label: "Device display name",
926
+ hint: "Shown in Matrix clients",
927
+ required: false
928
+ },
929
+ {
930
+ name: "encryption",
931
+ label: "Enable E2EE",
932
+ hint: "true | false — requires crypto native module",
933
+ required: false
934
+ },
935
+ {
936
+ name: "threadReplies",
937
+ label: "Thread replies",
938
+ hint: "off | inbound (default) | always",
939
+ required: false
940
+ },
941
+ {
942
+ name: "textChunkLimit",
943
+ label: "Text chunk limit (chars)",
944
+ hint: "Default: 16000",
945
+ required: false
946
+ },
947
+ {
948
+ name: "mediaMaxMb",
949
+ label: "Media size limit (MB)",
950
+ hint: "Default: 10",
951
+ required: false
952
+ },
953
+ {
954
+ name: "autoJoin",
955
+ label: "Auto-join invites",
956
+ hint: "always (default) | allowlist | off",
957
+ required: false
958
+ }
959
+ ],
511
960
  setupSteps: [
512
- "1. Create a bot account on matrix.org or another homeserver.",
513
- "2. Access token: Element/SchildiChat Settings Help & About → Access Token.",
514
- "3. Or via API: POST /_matrix/client/r0/login with type=m.login.password.",
515
- "4. Homeserver URL: https://matrix.org or your server URL.",
961
+ "1. Create a Matrix bot account on any homeserver (matrix.org has free accounts).",
962
+ "2. Get an access token via the login API:",
963
+ " curl -X POST https://<homeserver>/_matrix/client/v3/login \\",
964
+ " -H \"Content-Type: application/json\" \\",
965
+ " -d '{\"type\":\"m.login.password\",\"identifier\":{\"type\":\"m.id.user\",\"user\":\"<username>\"},\"password\":\"<password>\"}'",
966
+ " Or set userId + password — HyperClaw will call the login API and cache the token.",
967
+ "3. Invite the bot account to a room or DM it from any Matrix client (Element, Beeper, etc.).",
968
+ "4. For Beeper, enable E2EE: set encryption: true and verify the device in Element.",
516
969
  "",
517
- " 🔗 matrix.org — element.io"
970
+ " 🔗 matrix.org/ecosystem/hosting/ — element.io"
518
971
  ],
519
972
  status: "available",
973
+ notes: "Supports DMs, rooms, threads, media, reactions, polls, location, E2EE, multi-account.",
520
974
  npmPackage: "matrix-js-sdk"
521
975
  },
522
976
  {
@@ -530,25 +984,81 @@ const CHANNELS = [
530
984
  extraFields: [
531
985
  {
532
986
  name: "server",
533
- label: "Server",
987
+ label: "Server (IRC_HOST)",
534
988
  hint: "e.g. irc.libera.chat",
535
989
  required: true
536
990
  },
991
+ {
992
+ name: "port",
993
+ label: "Port (IRC_PORT)",
994
+ hint: "Default: 6697 (TLS) or 6667",
995
+ required: false
996
+ },
997
+ {
998
+ name: "tls",
999
+ label: "TLS (IRC_TLS)",
1000
+ hint: "true / false — default: false",
1001
+ required: false
1002
+ },
537
1003
  {
538
1004
  name: "nick",
539
- label: "Nickname",
1005
+ label: "Nickname (IRC_NICK)",
540
1006
  required: true
541
1007
  },
1008
+ {
1009
+ name: "username",
1010
+ label: "Username / ident (IRC_USERNAME)",
1011
+ required: false
1012
+ },
1013
+ {
1014
+ name: "realname",
1015
+ label: "Real name (IRC_REALNAME)",
1016
+ required: false
1017
+ },
1018
+ {
1019
+ name: "password",
1020
+ label: "Server password (IRC_PASSWORD)",
1021
+ hint: "Not NickServ — leave blank if none",
1022
+ required: false
1023
+ },
542
1024
  {
543
1025
  name: "channels",
544
- label: "Default channels (#room)",
1026
+ label: "Channels to join (IRC_CHANNELS)",
1027
+ hint: "#room1,#room2",
1028
+ required: false
1029
+ },
1030
+ {
1031
+ name: "nickservPassword",
1032
+ label: "NickServ password (IRC_NICKSERV_PASSWORD)",
1033
+ hint: "Identify after connect",
1034
+ required: false
1035
+ },
1036
+ {
1037
+ name: "groupPolicy",
1038
+ label: "Group policy",
1039
+ hint: "\"allowlist\" (default) or \"open\"",
1040
+ required: false
1041
+ },
1042
+ {
1043
+ name: "dmPolicy",
1044
+ label: "DM policy",
1045
+ hint: "\"pairing\" (default) | \"allowlist\" | \"open\"",
545
1046
  required: false
546
1047
  }
547
1048
  ],
548
1049
  setupSteps: [
549
1050
  "1. Choose an IRC server (e.g. irc.libera.chat, irc.oftc.net).",
550
- "2. You need a nickname for the bot and optionally a channel to join.",
551
- "3. Some servers require authentication before /join.",
1051
+ "2. Set a nickname for the bot and the channels it should join.",
1052
+ "3. Enable TLS (recommended): set port 6697 and tls: true.",
1053
+ "4. If your nick is registered, set nickservPassword to auto-identify.",
1054
+ "5. Access control defaults: groupPolicy=allowlist (bot only replies in",
1055
+ " configured groups), dmPolicy=pairing (new DMs need pairing approval).",
1056
+ "6. To allow everyone in a channel without mention, set per-channel:",
1057
+ " groups[\"#mychan\"].requireMention = false, allowFrom = [\"*\"].",
1058
+ "",
1059
+ " Env vars: IRC_HOST IRC_PORT IRC_TLS IRC_NICK IRC_USERNAME",
1060
+ " IRC_REALNAME IRC_PASSWORD IRC_CHANNELS",
1061
+ " IRC_NICKSERV_PASSWORD IRC_NICKSERV_REGISTER_EMAIL",
552
1062
  "",
553
1063
  " 🔗 libera.chat — oftc.net"
554
1064
  ],
@@ -562,31 +1072,71 @@ const CHANNELS = [
562
1072
  requiresGateway: true,
563
1073
  supportsDM: true,
564
1074
  platforms: ["all"],
565
- tokenLabel: "Personal Access Token (or Bot token)",
566
- tokenHint: "Account Settings > Security > Personal Access Tokens",
1075
+ tokenLabel: "Bot Token (MATTERMOST_BOT_TOKEN)",
1076
+ tokenHint: "System Console Integrations Bot Accounts → Add Bot Account",
567
1077
  setupSteps: [
568
- "1. In Mattermost: ProfileAccount Settings SecurityPersonal Access Tokens.",
569
- "2. Create token copy it (not shown again).",
570
- "3. Integrations Outgoing Webhooks Add outgoing webhook.",
571
- "4. Note the webhook URL and Trigger Word. The webhook token is needed for verification.",
572
- "5. Webhook URL for gateway: https://<gateway>/webhook/mattermost",
1078
+ "1. Create a Bot Account: System Console IntegrationsBot Accounts Add Bot Account.",
1079
+ "2. Copy the bot token shown after creation (not shown again).",
1080
+ "3. Note your Mattermost base URL (e.g. https://chat.example.com).",
1081
+ "4. The connector uses WebSocket events no outgoing webhook needed.",
1082
+ "5. For slash commands: set commands.native=true and expose callbackUrl.",
1083
+ "6. For buttons: add capabilities: [\"inlineButtons\"] and set interactions.callbackBaseUrl.",
1084
+ "",
1085
+ " Env vars: MATTERMOST_BOT_TOKEN MATTERMOST_URL",
573
1086
  "",
574
- " 🔗 docs.mattermost.com"
1087
+ " Chat modes:",
1088
+ " oncall (default) — reply only when @mentioned",
1089
+ " onmessage — reply to every channel message",
1090
+ " onchar — reply when message starts with a prefix (e.g. \">\", \"!\")",
1091
+ "",
1092
+ " Access control:",
1093
+ " dmPolicy=pairing (default) | allowlist | open | none",
1094
+ " groupPolicy=allowlist (default) | open",
1095
+ " groupAllowFrom=[userId1, ...] — sender gate for channels",
1096
+ "",
1097
+ " 🔗 docs.mattermost.com — mattermost.com"
1098
+ ],
1099
+ extraFields: [
1100
+ {
1101
+ name: "baseUrl",
1102
+ label: "Base URL (MATTERMOST_URL)",
1103
+ hint: "https://chat.example.com",
1104
+ required: true
1105
+ },
1106
+ {
1107
+ name: "chatmode",
1108
+ label: "Chat mode",
1109
+ hint: "\"oncall\" (default) | \"onmessage\" | \"onchar\"",
1110
+ required: false
1111
+ },
1112
+ {
1113
+ name: "oncharPrefixes",
1114
+ label: "onchar prefixes",
1115
+ hint: ">, ! (comma-separated, for chatmode=onchar)",
1116
+ required: false
1117
+ },
1118
+ {
1119
+ name: "dmPolicy",
1120
+ label: "DM policy",
1121
+ hint: "\"pairing\" (default) | \"open\" | \"allowlist\" | \"none\"",
1122
+ required: false
1123
+ },
1124
+ {
1125
+ name: "groupPolicy",
1126
+ label: "Group policy",
1127
+ hint: "\"allowlist\" (default) | \"open\"",
1128
+ required: false
1129
+ },
1130
+ {
1131
+ name: "capabilities",
1132
+ label: "Capabilities",
1133
+ hint: "\"inlineButtons\" — comma-separated",
1134
+ required: false
1135
+ }
575
1136
  ],
576
- extraFields: [{
577
- name: "serverUrl",
578
- label: "Server URL",
579
- hint: "https://mattermost.example.com",
580
- required: true
581
- }, {
582
- name: "webhookToken",
583
- label: "Outgoing Webhook Token",
584
- hint: "From Integrations > Outgoing Webhook",
585
- required: true
586
- }],
587
1137
  status: "available",
588
- notes: "Requires Outgoing Webhook + PAT. Webhook URL: /webhook/mattermost",
589
- npmPackage: "@mattermost/client"
1138
+ notes: "WebSocket + REST. Supports DMs, channels, buttons, reactions, slash commands, multi-account.",
1139
+ npmPackage: "ws"
590
1140
  },
591
1141
  {
592
1142
  id: "googlechat",
@@ -609,18 +1159,27 @@ const CHANNELS = [
609
1159
  id: "msteams",
610
1160
  name: "Microsoft Teams",
611
1161
  emoji: "🟣",
612
- requiresGateway: false,
613
- supportsDM: false,
1162
+ requiresGateway: true,
1163
+ supportsDM: true,
614
1164
  platforms: ["all"],
615
- tokenLabel: "Teams incoming webhook URL",
1165
+ tokenLabel: "App ID (Azure Bot)",
1166
+ extraFields: [{
1167
+ name: "appPassword",
1168
+ label: "App Password (client secret)",
1169
+ hint: "From Azure Bot → Manage → Certificates & secrets",
1170
+ required: true
1171
+ }],
616
1172
  setupSteps: [
617
- "1. In Teams: ChannelConnectors Incoming Webhook Configure.",
618
- "2. Give it a name and copy the Webhook URL.",
619
- "3. Paste the URL below.",
1173
+ "1. Create an Azure Bot: portal.azure.comCreate a resourceAzure Bot",
1174
+ "2. Type of App: Single Tenant. Create new Microsoft App ID.",
1175
+ "3. Configuration copy App ID. Manage → Certificates & secrets → New client secret → copy Value (appPassword).",
1176
+ "4. Channels → Microsoft Teams → Configure. Set Messaging endpoint: https://<your-host>/webhook/msteams",
1177
+ "5. Build a Teams app manifest with botId = App ID. Upload to Teams.",
620
1178
  "",
621
- " 🔗 docs.microsoft.com/microsoftteams/platform/webhooks-and-connectors"
1179
+ " 🔗 docs/msteams.md — full setup guide"
622
1180
  ],
623
- status: "available"
1181
+ status: "available",
1182
+ notes: "Bot Framework. Text + DM. Channel/group files require Graph + SharePoint."
624
1183
  },
625
1184
  {
626
1185
  id: "nostr",
@@ -650,24 +1209,61 @@ const CHANNELS = [
650
1209
  id: "line",
651
1210
  name: "LINE",
652
1211
  emoji: "🟩",
653
- requiresGateway: false,
1212
+ requiresGateway: true,
654
1213
  supportsDM: true,
655
1214
  platforms: ["all"],
656
1215
  tokenLabel: "LINE Channel access token",
657
- extraFields: [{
658
- name: "secret",
659
- label: "Channel Secret",
660
- required: true
661
- }],
1216
+ extraFields: [
1217
+ {
1218
+ name: "channelSecret",
1219
+ label: "Channel Secret",
1220
+ hint: "From LINE Developers Console → Basic settings",
1221
+ required: true
1222
+ },
1223
+ {
1224
+ name: "tokenFile",
1225
+ label: "Token file path (optional)",
1226
+ hint: "Alternative to pasting token directly",
1227
+ required: false
1228
+ },
1229
+ {
1230
+ name: "secretFile",
1231
+ label: "Secret file path (optional)",
1232
+ hint: "Alternative to pasting secret directly",
1233
+ required: false
1234
+ },
1235
+ {
1236
+ name: "webhookPath",
1237
+ label: "Webhook path",
1238
+ hint: "Default: /line/webhook",
1239
+ required: false
1240
+ },
1241
+ {
1242
+ name: "mediaMaxMb",
1243
+ label: "Media download limit (MB)",
1244
+ hint: "Default: 10",
1245
+ required: false
1246
+ },
1247
+ {
1248
+ name: "groupPolicy",
1249
+ label: "Group policy",
1250
+ hint: "open | allowlist | disabled (default: allowlist)",
1251
+ required: false
1252
+ }
1253
+ ],
662
1254
  setupSteps: [
663
- "1. Go to developers.line.biz → Console → Create provider & channel.",
664
- "2. Messaging API channel Configure Basic settings.",
665
- "3. Channel access token: Issue or Regenerate — copy it.",
666
- "4. Channel secret: from Basic settings copy it.",
1255
+ "1. Go to developers.line.biz → Console → Create provider & Messaging API channel.",
1256
+ "2. Channel access token: Issue or Regenerate — copy it.",
1257
+ "3. Channel secret: from Basic settings — copy it.",
1258
+ "4. Enable \"Use webhook\" in Messaging API settings.",
1259
+ "5. Set webhook URL (HTTPS required):",
1260
+ " https://<gateway-host>/line/webhook",
1261
+ "6. Paste token + secret below.",
667
1262
  "",
668
1263
  " 🔗 developers.line.biz"
669
1264
  ],
670
1265
  status: "available",
1266
+ notes: "Webhook receiver. Supports DMs, groups, media, Flex messages, quick replies, locations.",
671
1267
  npmPackage: "@line/bot-sdk"
672
1268
  },
673
1269
  {
@@ -695,27 +1291,68 @@ const CHANNELS = [
695
1291
  {
696
1292
  id: "synology-chat",
697
1293
  name: "Synology Chat",
698
- emoji: "💬",
1294
+ emoji: "💬",
699
1295
  requiresGateway: true,
700
1296
  supportsDM: true,
701
1297
  platforms: ["all"],
702
- tokenLabel: "Incoming Webhook URL",
703
- tokenHint: "Synology Chat incoming webhook URL",
704
- extraFields: [{
705
- name: "outgoingToken",
706
- label: "Outgoing Webhook Token",
707
- hint: "Optional verification token",
708
- required: false
709
- }],
1298
+ tokenLabel: "Outgoing Webhook Token (SYNOLOGY_CHAT_TOKEN)",
1299
+ tokenHint: "From Synology Chat Integrations Outgoing Webhook",
1300
+ extraFields: [
1301
+ {
1302
+ name: "incomingUrl",
1303
+ label: "Incoming Webhook URL (SYNOLOGY_CHAT_INCOMING_URL)",
1304
+ hint: "From Synology Chat Integrations Incoming Webhook",
1305
+ required: true
1306
+ },
1307
+ {
1308
+ name: "webhookPath",
1309
+ label: "Gateway inbound path",
1310
+ hint: "/webhook/synology (default)",
1311
+ required: false
1312
+ },
1313
+ {
1314
+ name: "dmPolicy",
1315
+ label: "DM policy",
1316
+ hint: "\"allowlist\" (default) | \"open\" | \"pairing\" | \"disabled\"",
1317
+ required: false
1318
+ },
1319
+ {
1320
+ name: "allowedUserIds",
1321
+ label: "Allowed user IDs (SYNOLOGY_ALLOWED_USER_IDS)",
1322
+ hint: "Numeric Synology Chat user IDs, comma-separated",
1323
+ required: false
1324
+ },
1325
+ {
1326
+ name: "rateLimitPerMinute",
1327
+ label: "Rate limit per sender/min (SYNOLOGY_RATE_LIMIT)",
1328
+ hint: "30 (default)",
1329
+ required: false
1330
+ },
1331
+ {
1332
+ name: "allowInsecureSsl",
1333
+ label: "Allow insecure SSL",
1334
+ hint: "false (default) — true only for self-signed NAS certs",
1335
+ required: false
1336
+ }
1337
+ ],
710
1338
  setupSteps: [
711
- "1. Synology Chat Integration → Incoming Webhook: create a webhook and copy the URL.",
712
- "2. If you want inbound bot events, create an Outgoing Webhook pointing to /webhook/synology-chat.",
713
- "3. Paste the incoming webhook URL below.",
1339
+ "1. Synology Chat Integrations Incoming Webhook Create copy URL.",
1340
+ "2. Synology Chat Integrations Outgoing Webhook Create:",
1341
+ " Outgoing URL: https://<gateway>/webhook/synology",
1342
+ " Copy the generated token.",
1343
+ "3. Set token + incomingUrl in config (or env vars).",
1344
+ "4. dmPolicy=allowlist requires at least one allowedUserId.",
1345
+ "",
1346
+ " Env vars: SYNOLOGY_CHAT_TOKEN SYNOLOGY_CHAT_INCOMING_URL",
1347
+ " SYNOLOGY_ALLOWED_USER_IDS SYNOLOGY_RATE_LIMIT OPENCLAW_BOT_NAME",
1348
+ "",
1349
+ " Multi-account: channels.synology-chat.accounts.{ default, alerts }",
1350
+ " Targets: <numericId>, synology-chat:<id>, user:<id>",
714
1351
  "",
715
1352
  " 🔗 kb.synology.com"
716
1353
  ],
717
1354
  status: "available",
718
- notes: "Webhook bridge for Synology Chat rooms / bot automation."
1355
+ notes: "Gateway webhooks. Token verify, rate limiting, multi-account, allowInsecureSsl."
719
1356
  },
720
1357
  {
721
1358
  id: "twitch",
@@ -726,61 +1363,84 @@ const CHANNELS = [
726
1363
  platforms: ["all"],
727
1364
  tokenLabel: "OAuth Token (oauth:...)",
728
1365
  tokenHint: "Generate a Twitch chat token for your bot account",
729
- extraFields: [{
730
- name: "username",
731
- label: "Bot Username",
732
- required: true
733
- }, {
734
- name: "channels",
735
- label: "Channels (#name or comma-separated)",
736
- required: true
737
- }],
1366
+ extraFields: [
1367
+ {
1368
+ name: "username",
1369
+ label: "Bot username",
1370
+ required: true
1371
+ },
1372
+ {
1373
+ name: "channels",
1374
+ label: "Channels (comma-separated)",
1375
+ hint: "vevisk,secondchannel",
1376
+ required: true
1377
+ },
1378
+ {
1379
+ name: "commandPrefix",
1380
+ label: "Command prefix",
1381
+ hint: "! (default)",
1382
+ required: false
1383
+ }
1384
+ ],
738
1385
  setupSteps: [
739
- "1. Create or use a Twitch bot account.",
740
- "2. Generate an IRC OAuth token for Twitch chat (format: oauth:...).",
741
- "3. Add one or more channels the bot should join.",
1386
+ "1. Create Twitch bot account. Generate OAuth: twitchapps.com/tmi",
1387
+ "2. Config: username, oauthToken, channels (required).",
1388
+ "3. Add allowFrom (recommended) to restrict who can trigger.",
1389
+ "4. Public chat: prefix required (default !). Whispers: no prefix.",
742
1390
  "",
743
- " 🔗 dev.twitch.tv"
1391
+ " 🔗 docs/twitch.md — dev.twitch.tv"
744
1392
  ],
745
1393
  status: "available",
746
- notes: "Twitch IRC chat integration for streams / channel chat."
1394
+ notes: "IRC over WebSocket. Channel chat + whispers. Pairing, allowlist, modsBypass."
747
1395
  },
748
1396
  {
749
1397
  id: "tlon",
750
- name: "Tlon",
751
- emoji: "🔵",
1398
+ name: "Tlon (Urbit Groups)",
1399
+ emoji: "🌊",
752
1400
  requiresGateway: true,
753
1401
  supportsDM: true,
754
1402
  platforms: ["all"],
755
- tokenLabel: "API Key",
756
- tokenHint: "Token for your Tlon API / bot gateway",
1403
+ tokenLabel: "Login Code",
1404
+ tokenHint: "Ship login code from Landscape Settings → Access key (e.g. lidlut-tabwed-pillex-ridrup)",
757
1405
  extraFields: [
758
1406
  {
759
- name: "apiBaseUrl",
760
- label: "API Base URL",
761
- hint: "e.g. https://tlon.example.com",
1407
+ name: "ship",
1408
+ label: "Ship Name",
1409
+ hint: "e.g. ~sampel-palnet",
762
1410
  required: true
763
1411
  },
764
1412
  {
765
- name: "channelId",
766
- label: "Default Channel ID",
1413
+ name: "url",
1414
+ label: "Ship URL",
1415
+ hint: "e.g. https://sampel-palnet.tlon.network or http://localhost:8080",
1416
+ required: true
1417
+ },
1418
+ {
1419
+ name: "ownerShip",
1420
+ label: "Owner Ship",
1421
+ hint: "Your personal ship — always authorized, receives approval notifications",
767
1422
  required: false
768
1423
  },
769
1424
  {
770
- name: "webhookSecret",
771
- label: "Webhook Secret",
1425
+ name: "allowPrivateNetwork",
1426
+ label: "Allow Private Network",
1427
+ hint: "Enable for localhost/LAN ships (SSRF opt-in)",
772
1428
  required: false
773
1429
  }
774
1430
  ],
775
1431
  setupSteps: [
776
- "1. Point Tlon events to /webhook/tlon on your HyperClaw gateway.",
777
- "2. Use the API base URL and API key for outbound sends.",
778
- "3. Optionally set a default channel ID and webhook secret.",
1432
+ "1. Install plugin: hyperclaw plugins install @hyperclaw/extension-tlon",
1433
+ "2. Get your ship login code from Landscape (Settings System → Access key).",
1434
+ "3. Configure channels.tlon with ship name, URL, and login code.",
1435
+ "4. Optionally set ownerShip to receive approval notifications.",
1436
+ "5. Restart gateway: hyperclaw gateway restart",
1437
+ "6. DM the bot ship in Tlon or mention it in a group channel.",
779
1438
  "",
780
- " 🔗 Tlon bot gateway / integration endpoint"
1439
+ " Plugin required connects via Urbit Eyre HTTP API + SSE stream.",
1440
+ " See: docs/tlon.md"
781
1441
  ],
782
1442
  status: "available",
783
- notes: "Generic Tlon bridge via webhook ingress + outbound API send."
1443
+ notes: "Urbit Eyre HTTP API + SSE. DMs, groups, reactions, owner approval, auto-discovery. Plugin: @hyperclaw/extension-tlon"
784
1444
  },
785
1445
  {
786
1446
  id: "instagram",
@@ -914,46 +1574,136 @@ const CHANNELS = [
914
1574
  status: "available"
915
1575
  },
916
1576
  {
917
- id: "nextcloud",
1577
+ id: "nextcloud-talk",
918
1578
  name: "Nextcloud Talk",
919
1579
  emoji: "☁️",
920
1580
  requiresGateway: true,
921
1581
  supportsDM: true,
922
1582
  platforms: ["all"],
923
- tokenLabel: "Nextcloud URL",
924
- extraFields: [{
925
- name: "username",
926
- label: "Username",
927
- required: true
928
- }, {
929
- name: "password",
930
- label: "App Password",
931
- required: true
932
- }],
1583
+ tokenLabel: "Nextcloud instance URL",
1584
+ tokenHint: "e.g. https://cloud.example.com",
1585
+ extraFields: [
1586
+ {
1587
+ name: "botSecret",
1588
+ label: "Bot shared secret",
1589
+ hint: "From: occ talk:bot:install",
1590
+ required: true
1591
+ },
1592
+ {
1593
+ name: "botSecretFile",
1594
+ label: "Bot secret file path (optional)",
1595
+ hint: "Alternative to inline secret",
1596
+ required: false
1597
+ },
1598
+ {
1599
+ name: "apiUser",
1600
+ label: "API username (optional)",
1601
+ hint: "For DM detection + fallback send via OCS API",
1602
+ required: false
1603
+ },
1604
+ {
1605
+ name: "apiPassword",
1606
+ label: "API/app password (optional)",
1607
+ hint: "Nextcloud app password for apiUser",
1608
+ required: false
1609
+ },
1610
+ {
1611
+ name: "webhookPort",
1612
+ label: "Webhook listener port",
1613
+ hint: "Default: 8788",
1614
+ required: false
1615
+ },
1616
+ {
1617
+ name: "webhookPath",
1618
+ label: "Webhook path",
1619
+ hint: "Default: /nextcloud-talk-webhook",
1620
+ required: false
1621
+ },
1622
+ {
1623
+ name: "webhookPublicUrl",
1624
+ label: "Webhook public URL",
1625
+ hint: "If behind a proxy — set this in occ talk:bot:install",
1626
+ required: false
1627
+ },
1628
+ {
1629
+ name: "groupPolicy",
1630
+ label: "Room policy",
1631
+ hint: "allowlist (default) | open | disabled",
1632
+ required: false
1633
+ },
1634
+ {
1635
+ name: "textChunkLimit",
1636
+ label: "Text chunk limit (chars)",
1637
+ hint: "Default: 32000",
1638
+ required: false
1639
+ }
1640
+ ],
933
1641
  setupSteps: [
934
- "1. Create App Password: Nextcloud Profile Security → App passwords.",
935
- "2. You need the Nextcloud URL, username and App Password.",
1642
+ "1. On your Nextcloud server, create the bot:",
1643
+ " ./occ talk:bot:install \"HyperClaw\" \"<shared-secret>\" \"<webhook-url>\" --feature reaction",
1644
+ " Replace <webhook-url> with your gateway URL, e.g. https://yourhost/nextcloud-talk-webhook",
1645
+ "2. Enable the bot in the target room settings (room → ⋯ → Bots).",
1646
+ "3. Enter the Nextcloud URL (token field) and the shared secret below.",
1647
+ "4. (Optional) Add apiUser + app password to enable DM detection via OCS API.",
936
1648
  "",
937
- " 🔗 nextcloud.com"
1649
+ " 🔗 nextcloud.com — docs.nextcloud.com/server/latest/admin_manual/talk_bots.html"
938
1650
  ],
939
- status: "available"
1651
+ status: "available",
1652
+ notes: "Webhook bot. Supports DMs (with apiUser), rooms, reactions. No media uploads."
940
1653
  },
941
1654
  {
942
1655
  id: "zalo",
943
1656
  name: "Zalo",
944
1657
  emoji: "🔵",
945
- requiresGateway: false,
1658
+ requiresGateway: true,
946
1659
  supportsDM: true,
947
1660
  platforms: ["all"],
948
- tokenLabel: "Zalo OA Access Token",
1661
+ tokenLabel: "Zalo Bot Token (12345689:abc-xyz)",
1662
+ tokenHint: "From bot.zaloplatforms.com",
1663
+ extraFields: [
1664
+ {
1665
+ name: "tokenFile",
1666
+ label: "Token file path (optional)",
1667
+ hint: "Alternative to inline token",
1668
+ required: false
1669
+ },
1670
+ {
1671
+ name: "groupPolicy",
1672
+ label: "Group policy",
1673
+ hint: "allowlist (default) | open | disabled",
1674
+ required: false
1675
+ },
1676
+ {
1677
+ name: "webhookUrl",
1678
+ label: "Webhook URL (optional)",
1679
+ hint: "HTTPS required — leave blank for long-polling",
1680
+ required: false
1681
+ },
1682
+ {
1683
+ name: "webhookSecret",
1684
+ label: "Webhook secret (optional)",
1685
+ hint: "8-256 chars — required if webhookUrl is set",
1686
+ required: false
1687
+ },
1688
+ {
1689
+ name: "mediaMaxMb",
1690
+ label: "Media size limit (MB)",
1691
+ hint: "Default: 5",
1692
+ required: false
1693
+ }
1694
+ ],
949
1695
  setupSteps: [
950
- "1. Zalo Official Account (OA): developers.zalo.me My Apps.",
951
- "2. Create an app and connect it to your OA.",
952
- "3. Access Token from Zalo API (OAuth flow or test token for development).",
1696
+ "1. Go to https://bot.zaloplatforms.com and sign in.",
1697
+ "2. Create a new bot and configure its settings.",
1698
+ "3. Copy the bot token (format: 12345689:abc-xyz).",
1699
+ "4. Paste the token below.",
1700
+ "5. (Optional) Set webhookUrl for webhook mode (HTTPS required).",
1701
+ " Leave blank to use long-polling (no public URL needed).",
953
1702
  "",
954
- " 🔗 developers.zalo.me"
1703
+ " 🔗 bot.zaloplatforms.com"
955
1704
  ],
956
- status: "available"
1705
+ status: "available",
1706
+ notes: "Experimental. DMs supported; groups with allowlist policy. Long-polling by default."
957
1707
  },
958
1708
  {
959
1709
  id: "web",
@@ -1042,18 +1792,24 @@ const ZALO_PERSONAL = {
1042
1792
  requiresGateway: true,
1043
1793
  supportsDM: true,
1044
1794
  platforms: ["all"],
1045
- tokenLabel: "Zalo Personal cookie token",
1046
- tokenHint: "Extract from browser",
1795
+ tokenLabel: "Cookie (from chat.zalo.me)",
1796
+ tokenHint: "DevTools Application → Cookies",
1797
+ extraFields: [{
1798
+ name: "dmPolicy",
1799
+ label: "DM policy",
1800
+ hint: "pairing (default) | allowlist | open | disabled",
1801
+ required: false
1802
+ }],
1047
1803
  setupSteps: [
1048
- "1. Unofficial APIuses browser cookies. May break with Zalo updates.",
1049
- "2. Open Zalo Web in browser, Developer ToolsApplicationCookies.",
1050
- "3. Look for the token/cookie Zalo uses for auth.",
1051
- "4. See docs/channels/zalo-personal.md for details.",
1804
+ "1. ⚠️ Experimentalunofficial. Account ban risk. Use at your own risk.",
1805
+ "2. Open chat.zalo.me, log in. DevTools ApplicationCookiescopy.",
1806
+ "3. Add channel, set cookie (or ZALO_PERSONAL_COOKIE env).",
1807
+ "4. hyperclaw gateway → hyperclaw pairing approve zalo-personal <CODE>",
1052
1808
  "",
1053
- " ⚠️ Unofficialuse at your own risk"
1809
+ " 🔗 docs/zalo-personal.md full setup"
1054
1810
  ],
1055
1811
  status: "available",
1056
- notes: "Uses unofficial Zalo personal API may break on Zalo app updates"
1812
+ notes: "Zalo Web cookie auth. DMs only. No groups. Text chunked ~2000 chars."
1057
1813
  };
1058
1814
  CHANNELS.push(ZALO_PERSONAL);
1059
1815
 
@@ -1185,6 +1941,180 @@ async function getConfiguredChannels() {
1185
1941
  return [];
1186
1942
  }
1187
1943
  }
1944
+ /**
1945
+ * hyperclaw channels login [channel]
1946
+ * Shortcut for first-time login flows (QR pairing, bot tokens, OAuth).
1947
+ * Delegates to channelsAdd with a login-oriented preamble.
1948
+ */
1949
+ async function channelsLogin(channelId) {
1950
+ console.log(chalk.default.bold.cyan("\n 🔑 Channel Login\n"));
1951
+ console.log(chalk.default.gray(" This wizard will guide you through first-time login for a channel.\n"));
1952
+ console.log(chalk.default.gray(" WhatsApp → QR code scan"));
1953
+ console.log(chalk.default.gray(" Telegram → bot token (from @BotFather)"));
1954
+ console.log(chalk.default.gray(" Discord → bot token (from Discord Developer Portal)"));
1955
+ console.log(chalk.default.gray(" Slack → bot + app tokens"));
1956
+ console.log(chalk.default.gray(" Signal → phone number + signal-cli daemon\n"));
1957
+ await channelsAdd(channelId);
1958
+ }
1959
+ function httpProbe(url, ms = 1200) {
1960
+ return new Promise((resolve) => {
1961
+ const req = http.default.get(url, { timeout: ms }, (res) => {
1962
+ res.resume();
1963
+ resolve((res.statusCode ?? 0) < 500);
1964
+ });
1965
+ req.on("error", () => resolve(false));
1966
+ req.on("timeout", () => {
1967
+ req.destroy();
1968
+ resolve(false);
1969
+ });
1970
+ });
1971
+ }
1972
+ /** Attempt a lightweight connectivity probe for a channel. */
1973
+ async function probeChannel(channelId, channelCfg) {
1974
+ switch (channelId) {
1975
+ case "telegram": {
1976
+ const token = channelCfg?.token || process.env.TELEGRAM_BOT_TOKEN;
1977
+ if (!token) return {
1978
+ status: "skipped",
1979
+ detail: "no token"
1980
+ };
1981
+ const ok = await httpProbe(`https://api.telegram.org/bot${token}/getMe`);
1982
+ return ok ? {
1983
+ status: "connected",
1984
+ detail: "api.telegram.org ok"
1985
+ } : {
1986
+ status: "unreachable",
1987
+ detail: "api.telegram.org unreachable"
1988
+ };
1989
+ }
1990
+ case "discord": {
1991
+ const ok = await httpProbe("https://discord.com/api/v10/gateway");
1992
+ return ok ? {
1993
+ status: "connected",
1994
+ detail: "discord.com/api ok"
1995
+ } : {
1996
+ status: "unreachable",
1997
+ detail: "discord.com unreachable"
1998
+ };
1999
+ }
2000
+ case "signal": {
2001
+ const daemonUrl = channelCfg?.daemonUrl || "http://127.0.0.1:8080";
2002
+ const ok = await httpProbe(`${daemonUrl}/v1/about`);
2003
+ return ok ? {
2004
+ status: "connected",
2005
+ detail: `signal-cli at ${daemonUrl}`
2006
+ } : {
2007
+ status: "unreachable",
2008
+ detail: `signal-cli not reachable at ${daemonUrl}`
2009
+ };
2010
+ }
2011
+ case "mattermost": {
2012
+ const baseUrl = channelCfg?.baseUrl;
2013
+ if (!baseUrl) return {
2014
+ status: "skipped",
2015
+ detail: "no baseUrl"
2016
+ };
2017
+ const ok = await httpProbe(`${baseUrl}/api/v4/system/ping`);
2018
+ return ok ? {
2019
+ status: "connected",
2020
+ detail: "system/ping ok"
2021
+ } : {
2022
+ status: "unreachable",
2023
+ detail: "server unreachable"
2024
+ };
2025
+ }
2026
+ case "matrix": {
2027
+ const homeserver = channelCfg?.homeserver;
2028
+ if (!homeserver) return {
2029
+ status: "skipped",
2030
+ detail: "no homeserver"
2031
+ };
2032
+ const ok = await httpProbe(`${homeserver}/_matrix/client/versions`);
2033
+ return ok ? {
2034
+ status: "connected",
2035
+ detail: "matrix client ok"
2036
+ } : {
2037
+ status: "unreachable",
2038
+ detail: "homeserver unreachable"
2039
+ };
2040
+ }
2041
+ case "slack": {
2042
+ const ok = await httpProbe("https://slack.com/api/api.test");
2043
+ return ok ? {
2044
+ status: "connected",
2045
+ detail: "slack.com/api ok"
2046
+ } : {
2047
+ status: "unreachable",
2048
+ detail: "slack.com unreachable"
2049
+ };
2050
+ }
2051
+ default: return {
2052
+ status: "skipped",
2053
+ detail: "probe not implemented"
2054
+ };
2055
+ }
2056
+ }
2057
+ /**
2058
+ * hyperclaw channels status [--probe]
2059
+ * Show all configured channels with their status.
2060
+ * --probe attempts a real connectivity check for each.
2061
+ */
2062
+ async function channelsStatus(opts = {}) {
2063
+ const configFile = path.default.join(os.default.homedir(), ".hyperclaw", "config.json");
2064
+ let cfg = {};
2065
+ try {
2066
+ cfg = fs_extra.default.readJsonSync(configFile);
2067
+ } catch {}
2068
+ const configured = cfg.channels || [];
2069
+ console.log(chalk.default.bold.cyan("\n 📡 CHANNEL STATUS\n"));
2070
+ if (configured.length === 0) {
2071
+ console.log(chalk.default.gray(" No channels configured.\n"));
2072
+ console.log(chalk.default.gray(" Add a channel: hyperclaw channels add\n"));
2073
+ return;
2074
+ }
2075
+ const spinner = opts.probe ? (0, ora.default)("Probing channels...").start() : null;
2076
+ const results = [];
2077
+ for (const id of configured) {
2078
+ const ch = getChannel(id);
2079
+ if (!ch) continue;
2080
+ const channelCfg = cfg.channelConfigs?.[id];
2081
+ const dmPolicy = channelCfg?.dmPolicy?.policy ?? channelCfg?.dmPolicy;
2082
+ const r = {
2083
+ id,
2084
+ name: ch.name,
2085
+ emoji: ch.emoji,
2086
+ configured: true,
2087
+ dmPolicy
2088
+ };
2089
+ if (opts.probe) {
2090
+ const { status, detail } = await probeChannel(id, channelCfg);
2091
+ r.probe = status;
2092
+ r.probeDetail = detail;
2093
+ }
2094
+ results.push(r);
2095
+ }
2096
+ if (spinner) spinner.stop();
2097
+ const allIds = CHANNELS.map((c) => c.id);
2098
+ const unconfigured = allIds.filter((id) => !configured.includes(id));
2099
+ for (const r of results) {
2100
+ const probeStr = opts.probe ? r.probe === "connected" ? chalk.default.green(" ✔ connected") : r.probe === "unreachable" ? chalk.default.red(" ✖ unreachable") : chalk.default.gray(" — skipped") : "";
2101
+ const dm = r.dmPolicy ? chalk.default.gray(` dm:${r.dmPolicy}`) : "";
2102
+ console.log(` ${chalk.default.green("●")} ${r.emoji} ${r.name.padEnd(18)}${dm}${probeStr}`);
2103
+ if (opts.probe && r.probeDetail) console.log(` ${chalk.default.gray(r.probeDetail)}`);
2104
+ }
2105
+ if (unconfigured.length > 0) console.log(chalk.default.gray(`\n ○ ${unconfigured.length} unconfigured channel(s) — add with: hyperclaw channels add`));
2106
+ console.log();
2107
+ if (opts.probe) {
2108
+ const unreachable = results.filter((r) => r.probe === "unreachable");
2109
+ if (unreachable.length > 0) {
2110
+ console.log(chalk.default.yellow(` ⚠ ${unreachable.length} channel(s) unreachable:`));
2111
+ for (const r of unreachable) console.log(chalk.default.gray(` ${r.id}: ${r.probeDetail}`));
2112
+ console.log();
2113
+ console.log(chalk.default.gray(" Troubleshoot: hyperclaw doctor"));
2114
+ console.log(chalk.default.gray(" Logs: hyperclaw logs --follow\n"));
2115
+ } else console.log(chalk.default.green(" ✔ All probed channels reachable\n"));
2116
+ }
2117
+ }
1188
2118
 
1189
2119
  //#endregion
1190
2120
  //#region src/infra/update-channels.ts
@@ -1496,22 +2426,68 @@ var init_queue = require_chunk.__esm({ "src/delivery/queue.ts"() {
1496
2426
  //#endregion
1497
2427
  //#region src/cli/run-main.ts
1498
2428
  const program = new commander.Command();
1499
- program.name("hyperclaw").description("⚡ HyperClaw — AI Gateway Platform. The Lobster Evolution 🦅").version("4.0.2");
2429
+ program.name("hyperclaw").description("⚡ HyperClaw — AI Gateway Platform. The Lobster Evolution 🦅").version("5.0.0").option("--profile <name>", "Use an isolated gateway profile. Auto-scopes HYPERCLAW_STATE_DIR and HYPERCLAW_CONFIG_PATH. Required for multi-gateway setups (rescue bot, staging, etc.). Example: hyperclaw --profile rescue gateway --port 19001").hook("preAction", (thisCommand) => {
2430
+ const profile = thisCommand.opts().profile;
2431
+ if (profile) {
2432
+ const os$8 = require("os");
2433
+ const path$7 = require("path");
2434
+ const home = os$8.homedir();
2435
+ if (!process.env.HYPERCLAW_STATE_DIR) process.env.HYPERCLAW_STATE_DIR = path$7.join(home, `.hyperclaw-${profile}`);
2436
+ if (!process.env.HYPERCLAW_CONFIG_PATH) process.env.HYPERCLAW_CONFIG_PATH = path$7.join(process.env.HYPERCLAW_STATE_DIR, "hyperclaw.json");
2437
+ }
2438
+ });
1500
2439
  program.command("init").description("Initialize HyperClaw with interactive wizard").option("-a, --auto-config", "Auto-configure with defaults").option("-d, --daemon", "Install as system daemon").option("-s, --start-now", "Start gateway after setup").action(async (opts) => {
1501
2440
  await new require_onboard.Banner().showNeonBanner(false);
1502
2441
  await new require_onboard.HyperClawWizard().run(opts);
1503
2442
  process.exit(0);
1504
2443
  });
1505
- program.command("onboard").description("Full onboarding wizard — preferred setup path").option("--install-daemon", "Auto-install system daemon (starts on boot, grants full PC access)").option("--quick", "Use QuickStart mode (skip advanced options)").action(async (opts) => {
2444
+ program.command("onboard").description("Full onboarding wizard — preferred setup path").option("--install-daemon", "Auto-install system daemon (starts on boot, grants full PC access)").option("--quick", "Use QuickStart mode (skip advanced options)").option("--reset", "Reset config before running wizard (sends to trash, not deleted)").option("--reset-scope <scope>", "What to reset: config | config+creds | full", "config").option("--non-interactive", "Run in non-interactive mode (use flags for all options)").option("--json", "Output result as JSON (use with --non-interactive)").option("--anthropic-api-key <key>", "Anthropic API key (non-interactive)").option("--openai-api-key <key>", "OpenAI API key (non-interactive)").option("--gateway-port <port>", "Gateway port (non-interactive)", "18789").option("--gateway-bind <bind>", "Gateway bind: loopback | all (non-interactive)", "loopback").option("--daemon-runtime <runtime>", "Daemon runtime: node | bun (non-interactive)", "node").option("--skip-skills", "Skip skills setup (non-interactive)").option("--skip-search", "Skip web search setup (non-interactive)").action(async (opts) => {
1506
2445
  await new require_onboard.Banner().showNeonBanner(false);
2446
+ if (opts.reset) {
2447
+ const fs$7 = require("fs-extra");
2448
+ const path$7 = require("path");
2449
+ const os$8 = require("os");
2450
+ const hcDir = path$7.join(os$8.homedir(), ".hyperclaw");
2451
+ const scope = opts.resetScope ?? "config";
2452
+ const filesToRemove = [path$7.join(hcDir, "hyperclaw.json")];
2453
+ if (scope === "config+creds" || scope === "full") {
2454
+ filesToRemove.push(path$7.join(hcDir, "credentials"));
2455
+ filesToRemove.push(path$7.join(hcDir, "sessions"));
2456
+ }
2457
+ if (scope === "full") filesToRemove.push(path$7.join(hcDir, "workspace"));
2458
+ const chalk$11 = require("chalk");
2459
+ console.log(chalk$11.yellow(`\n ⚠ Reset scope: ${chalk$11.bold(scope)}\n`));
2460
+ console.log(chalk$11.gray(" Files to remove:"));
2461
+ filesToRemove.forEach((f) => console.log(chalk$11.gray(` • ${f}`)));
2462
+ const inquirer$2 = require("inquirer");
2463
+ const { confirmReset } = await inquirer$2.prompt([{
2464
+ type: "confirm",
2465
+ name: "confirmReset",
2466
+ message: "Confirm reset? (files will be moved to trash/backup, not permanently deleted)",
2467
+ default: false
2468
+ }]);
2469
+ if (confirmReset) {
2470
+ const backupDir = path$7.join(hcDir, `backup-${Date.now()}`);
2471
+ await fs$7.ensureDir(backupDir);
2472
+ for (const f of filesToRemove) if (await fs$7.pathExists(f)) {
2473
+ const dest = path$7.join(backupDir, path$7.basename(f));
2474
+ await fs$7.move(f, dest);
2475
+ console.log(chalk$11.gray(` ✓ Moved ${path$7.basename(f)} → backup/`));
2476
+ }
2477
+ console.log(chalk$11.green("\n ✔ Reset complete. Starting fresh...\n"));
2478
+ } else {
2479
+ console.log(chalk$11.gray("\n Reset cancelled.\n"));
2480
+ process.exit(0);
2481
+ }
2482
+ }
1507
2483
  if (opts.installDaemon) {
1508
- const chalk$10 = require("chalk");
1509
- console.log(chalk$10.yellow("\n ⚠ --install-daemon mode\n"));
1510
- console.log(chalk$10.gray(" The daemon will run as a background system service and will have:\n"));
1511
- console.log(chalk$10.white(" • Full shell / command execution on this machine"));
1512
- console.log(chalk$10.white(" • File system read & write access"));
1513
- console.log(chalk$10.white(" • Network access (gateway WebSocket)"));
1514
- console.log(chalk$10.white(" • Auto-start on every system boot\n"));
2484
+ const chalk$11 = require("chalk");
2485
+ console.log(chalk$11.yellow("\n ⚠ --install-daemon mode\n"));
2486
+ console.log(chalk$11.gray(" The daemon will run as a background system service and will have:\n"));
2487
+ console.log(chalk$11.white(" • Full shell / command execution on this machine"));
2488
+ console.log(chalk$11.white(" • File system read & write access"));
2489
+ console.log(chalk$11.white(" • Network access (gateway WebSocket)"));
2490
+ console.log(chalk$11.white(" • Auto-start on every system boot\n"));
1515
2491
  const inquirer$2 = require("inquirer");
1516
2492
  const { confirmed } = await inquirer$2.prompt([{
1517
2493
  type: "confirm",
@@ -1520,14 +2496,23 @@ program.command("onboard").description("Full onboarding wizard — preferred set
1520
2496
  default: false
1521
2497
  }]);
1522
2498
  if (!confirmed) {
1523
- console.log(chalk$10.gray("\n Cancelled. Run without --install-daemon to choose during setup.\n"));
2499
+ console.log(chalk$11.gray("\n Cancelled. Run without --install-daemon to choose during setup.\n"));
1524
2500
  process.exit(0);
1525
2501
  }
1526
2502
  }
1527
2503
  await new require_onboard.HyperClawWizard().run({
1528
2504
  ...opts,
1529
2505
  wizard: true,
1530
- installDaemon: opts.installDaemon ?? false
2506
+ installDaemon: opts.installDaemon ?? false,
2507
+ nonInteractive: opts.nonInteractive ?? false,
2508
+ jsonOutput: opts.json ?? false,
2509
+ skipSkills: opts.skipSkills ?? false,
2510
+ skipSearch: opts.skipSearch ?? false,
2511
+ daemonRuntime: opts.daemonRuntime ?? "node",
2512
+ gatewayPort: opts.gatewayPort ? parseInt(opts.gatewayPort) : void 0,
2513
+ gatewayBind: opts.gatewayBind ?? "loopback",
2514
+ anthropicApiKey: opts.anthropicApiKey,
2515
+ openaiApiKey: opts.openaiApiKey
1531
2516
  });
1532
2517
  process.exit(0);
1533
2518
  });
@@ -1552,18 +2537,18 @@ gatewayCmd.command("status").description("Show gateway status").action(async ()
1552
2537
  process.exit(0);
1553
2538
  });
1554
2539
  gatewayCmd.command("start").description("Start the gateway service").option("-p, --port <port>", "Override port").action(async (opts) => {
1555
- await new require_onboard.DaemonManager().start();
2540
+ await new require_daemon.DaemonManager().start();
1556
2541
  });
1557
2542
  gatewayCmd.command("stop").description("Stop the gateway service").action(async () => {
1558
- await new require_onboard.DaemonManager().stop();
2543
+ await new require_daemon.DaemonManager().stop();
1559
2544
  process.exit(0);
1560
2545
  });
1561
2546
  gatewayCmd.command("restart").description("Restart the gateway service").action(async () => {
1562
- const dm = new require_onboard.DaemonManager();
2547
+ const dm = new require_daemon.DaemonManager();
1563
2548
  await dm.restart();
1564
2549
  });
1565
2550
  program.command("daemon").description("Manage HyperClaw system service (alias: gateway)").argument("<action>", "start|stop|restart|status|logs|install|uninstall").action(async (action) => {
1566
- const dm = new require_onboard.DaemonManager();
2551
+ const dm = new require_daemon.DaemonManager();
1567
2552
  if (action === "start") await new require_onboard.Banner().showNeonBanner(true);
1568
2553
  await dm.handle(action);
1569
2554
  if (action === "start" || action === "restart") return;
@@ -1571,20 +2556,20 @@ program.command("daemon").description("Manage HyperClaw system service (alias: g
1571
2556
  });
1572
2557
  const sandboxCmd = program.command("sandbox").description("Debug sandbox and tool policy");
1573
2558
  sandboxCmd.command("explain").description("Show effective sandbox mode, tool policy, and allowed tools").option("--json", "Output as JSON").action(async (opts) => {
1574
- const chalk$10 = require("chalk");
1575
- const fs$6 = require("fs-extra");
1576
- const path$6 = require("path");
1577
- const os$7 = require("os");
2559
+ const chalk$11 = require("chalk");
2560
+ const fs$7 = require("fs-extra");
2561
+ const path$7 = require("path");
2562
+ const os$8 = require("os");
1578
2563
  const { getConfigPath } = await Promise.resolve().then(() => require("./paths-D-QecARF.js"));
1579
2564
  const cfgPath = getConfigPath();
1580
2565
  let cfg = {};
1581
2566
  try {
1582
- cfg = await fs$6.readJson(cfgPath);
2567
+ cfg = await fs$7.readJson(cfgPath);
1583
2568
  } catch {}
1584
2569
  const sandboxMode = cfg?.agents?.defaults?.sandbox?.mode ?? "non-main";
1585
2570
  const toolsCfg = cfg?.tools ?? {};
1586
- const { describeToolPolicy, applyToolPolicy } = await Promise.resolve().then(() => require("./tool-policy-DNvNRnve.js"));
1587
- const { getBuiltinTools, getSessionsTools, getPCAccessTools, getBrowserTools, getExtractionTools, getWebsiteWatchTools, getVisionTools } = await Promise.resolve().then(() => require("./src-BEVLgaF1.js"));
2571
+ const { describeToolPolicy, applyToolPolicy } = await Promise.resolve().then(() => require("./tool-policy-CNT-mF2Z.js"));
2572
+ const { getBuiltinTools, getSessionsTools, getPCAccessTools, getBrowserTools, getExtractionTools, getWebsiteWatchTools, getVisionTools } = await Promise.resolve().then(() => require("./src-BxPHKO5x.js"));
1588
2573
  const allTools = [
1589
2574
  ...getBuiltinTools(),
1590
2575
  ...getSessionsTools(() => null),
@@ -1612,15 +2597,15 @@ sandboxCmd.command("explain").description("Show effective sandbox mode, tool pol
1612
2597
  }, null, 2));
1613
2598
  process.exit(0);
1614
2599
  }
1615
- console.log(chalk$10.bold.hex("#06b6d4")("\n 🔒 SANDBOX EXPLAIN\n"));
2600
+ console.log(chalk$11.bold.hex("#06b6d4")("\n 🔒 SANDBOX EXPLAIN\n"));
1616
2601
  console.log(` Sandbox mode: ${sandboxMode} (non-main sessions get restricted pcTools)`);
1617
2602
  console.log(` Tool profile: ${policy.profile}`);
1618
2603
  console.log(` Policy source: ${policy.source}`);
1619
2604
  if (policy.allow?.length) console.log(` Allow: ${policy.allow.join(", ")}`);
1620
2605
  if (policy.deny?.length) console.log(` Deny: ${policy.deny.join(", ")}`);
1621
2606
  console.log(` Tools: ${filtered.length} / ${allTools.length} allowed`);
1622
- console.log(chalk$10.gray("\n Allowed: " + filtered.map((t) => t.name).join(", ")));
1623
- console.log(chalk$10.gray("\n Elevated: " + ((cfg?.tools?.elevated)?.enabled ? "enabled" : "disabled")));
2607
+ console.log(chalk$11.gray("\n Allowed: " + filtered.map((t) => t.name).join(", ")));
2608
+ console.log(chalk$11.gray("\n Elevated: " + ((cfg?.tools?.elevated)?.enabled ? "enabled" : "disabled")));
1624
2609
  console.log();
1625
2610
  process.exit(0);
1626
2611
  });
@@ -1637,6 +2622,14 @@ channelsCmd.command("remove <channel>").description("Remove a channel").action(a
1637
2622
  await channelsRemove(channel);
1638
2623
  process.exit(0);
1639
2624
  });
2625
+ channelsCmd.command("login [channel]").description("First-time login / QR pairing for a channel").action(async (channel) => {
2626
+ await channelsLogin(channel);
2627
+ process.exit(0);
2628
+ });
2629
+ channelsCmd.command("status").description("Show channel status (use --probe to test connectivity)").option("--probe", "Probe each channel for real connectivity").action(async (opts) => {
2630
+ await channelsStatus({ probe: !!opts.probe });
2631
+ process.exit(0);
2632
+ });
1640
2633
  const hooksCmd = program.command("hooks").description("Hook management");
1641
2634
  hooksCmd.command("list").description("List all hooks").option("--eligible", "Show only eligible hooks").action((opts) => {
1642
2635
  new require_loader.HookLoader().list(opts.eligible);
@@ -1672,12 +2665,67 @@ agentsCmd.command("unbind").description("Remove channel ↔ agent bindings").act
1672
2665
  process.exit(0);
1673
2666
  });
1674
2667
  const pairingCmd = program.command("pairing").description("DM pairing codes");
1675
- pairingCmd.command("list").description("List pending and approved pairing codes").action(() => {
1676
- new require_pairing.PairingStore().showList();
2668
+ pairingCmd.command("list [channel]").description("List pending DM pairing requests (optionally filter by channel)").action(async (channel) => {
2669
+ await new require_pairing.GlobalPairingManager().showList(channel);
1677
2670
  process.exit(0);
1678
2671
  });
1679
- pairingCmd.command("approve <channel> <code>").description("Approve a pairing code and add user to allowlist").action((channel, code) => {
1680
- new require_pairing.PairingStore().cliApprove(channel, code);
2672
+ pairingCmd.command("approve <channel> <code>").description("Approve a pairing code and add sender to channel allowlist").option("--account <id>", "Account ID for multi-account channels", "default").action(async (channel, code, opts) => {
2673
+ await new require_pairing.GlobalPairingManager().cliApprove(channel, code, opts.account);
2674
+ process.exit(0);
2675
+ });
2676
+ const devicesCmd = program.command("devices").description("Node/device pairing (iOS, Android, macOS, headless)");
2677
+ devicesCmd.command("list").description("List pending and paired devices").action(async () => {
2678
+ await new DevicePairingStore().showCLI();
2679
+ process.exit(0);
2680
+ });
2681
+ devicesCmd.command("pair").description("Create a new device pairing request and print setup code").option("-u, --gateway-url <url>", "Gateway WebSocket URL", "ws://localhost:18789").option("-n, --name <name>", "Device name (optional)").option("-p, --platform <platform>", "Platform hint: ios|android|macos|headless (optional)").action(async (opts) => {
2682
+ const store = new DevicePairingStore();
2683
+ const result = await store.createRequest(opts.gatewayUrl, {
2684
+ deviceName: opts.name,
2685
+ platform: opts.platform
2686
+ });
2687
+ console.log(chalk.default.bold.cyan("\n 📱 DEVICE PAIR REQUEST\n"));
2688
+ console.log(` Request ID: ${chalk.default.bold(result.requestId)}`);
2689
+ console.log(` Expires: ${chalk.default.gray(new Date(result.expiresAt).toLocaleTimeString())}`);
2690
+ console.log();
2691
+ console.log(chalk.default.yellow(" Setup code (send to device or paste in app):"));
2692
+ console.log(chalk.default.bold(`\n ${result.setupCode}\n`));
2693
+ console.log(chalk.default.gray(" Approve: hyperclaw devices approve " + result.requestId));
2694
+ console.log(chalk.default.gray(" Reject: hyperclaw devices reject " + result.requestId));
2695
+ console.log(chalk.default.gray("\n Telegram: message your bot with /pair for guided flow.\n"));
2696
+ process.exit(0);
2697
+ });
2698
+ devicesCmd.command("approve <requestId>").description("Approve a pending device pairing request").action(async (requestId) => {
2699
+ const store = new DevicePairingStore();
2700
+ const device = await store.approve(requestId);
2701
+ if (device) {
2702
+ console.log(chalk.default.green(`\n ✔ Device approved: ${chalk.default.bold(device.deviceId)}`));
2703
+ if (device.deviceName) console.log(chalk.default.gray(` Name: ${device.deviceName}`));
2704
+ console.log(chalk.default.gray(` Paired at: ${device.pairedAt}\n`));
2705
+ } else {
2706
+ console.log(chalk.default.red(`\n ✖ Request not found or expired: ${requestId}\n`));
2707
+ process.exit(1);
2708
+ }
2709
+ process.exit(0);
2710
+ });
2711
+ devicesCmd.command("reject <requestId>").description("Reject a pending device pairing request").action(async (requestId) => {
2712
+ const store = new DevicePairingStore();
2713
+ const ok = await store.reject(requestId);
2714
+ if (ok) console.log(chalk.default.green(`\n ✔ Request rejected: ${requestId}\n`));
2715
+ else {
2716
+ console.log(chalk.default.red(`\n ✖ Request not found: ${requestId}\n`));
2717
+ process.exit(1);
2718
+ }
2719
+ process.exit(0);
2720
+ });
2721
+ devicesCmd.command("unpair <deviceId>").description("Remove a paired device").action(async (deviceId) => {
2722
+ const store = new DevicePairingStore();
2723
+ const ok = await store.unpair(deviceId);
2724
+ if (ok) console.log(chalk.default.green(`\n ✔ Device unpaired: ${deviceId}\n`));
2725
+ else {
2726
+ console.log(chalk.default.red(`\n ✖ Device not found: ${deviceId}\n`));
2727
+ process.exit(1);
2728
+ }
1681
2729
  process.exit(0);
1682
2730
  });
1683
2731
  const msgCmd = program.command("message").description("Send messages");
@@ -1741,13 +2789,13 @@ skillCmd.command("install <id>").description("Install skill from ClawHub (or bun
1741
2789
  process.exit(0);
1742
2790
  });
1743
2791
  program.command("menu-bar").description("Launch macOS menu bar companion (Electron tray app)").action(async () => {
1744
- const path$6 = await import("path");
2792
+ const path$7 = await import("path");
1745
2793
  const { spawn } = await import("child_process");
1746
- const fs$6 = await import("fs-extra");
1747
- const root = path$6.join(process.cwd(), "apps", "macos");
1748
- const altRoot = path$6.join(__dirname, "..", "..", "apps", "macos");
1749
- const macosDir = await fs$6.pathExists(root) ? root : await fs$6.pathExists(altRoot) ? altRoot : null;
1750
- if (!macosDir || !await fs$6.pathExists(path$6.join(macosDir, "package.json"))) {
2794
+ const fs$7 = await import("fs-extra");
2795
+ const root = path$7.join(process.cwd(), "apps", "macos");
2796
+ const altRoot = path$7.join(__dirname, "..", "..", "apps", "macos");
2797
+ const macosDir = await fs$7.pathExists(root) ? root : await fs$7.pathExists(altRoot) ? altRoot : null;
2798
+ if (!macosDir || !await fs$7.pathExists(path$7.join(macosDir, "package.json"))) {
1751
2799
  console.log(chalk.default.gray("\n macOS menu bar app not found."));
1752
2800
  console.log(chalk.default.gray(" Run from HyperClaw repo root, or: cd apps/macos && npm start\n"));
1753
2801
  process.exit(1);
@@ -1774,8 +2822,15 @@ program.command("update").description("Update HyperClaw").option("-c, --channel
1774
2822
  await performUpdate(effective.channel, installKind);
1775
2823
  process.exit(0);
1776
2824
  });
1777
- program.command("doctor").description("Health check — surfaces misconfigs and risky DM policies").option("--fix", "Auto-repair fixable issues").action(async (opts) => {
1778
- await require_doctor.runDoctor(opts.fix);
2825
+ program.command("doctor").description("Health check — surfaces misconfigs, risky DM policies, and repairs").option("--fix", "Auto-repair fixable issues").option("--repair", "Apply recommended repairs (same as --fix)").option("--force", "Apply aggressive repairs (use with --repair)").option("-y, --yes", "Accept defaults without prompting").option("--non-interactive", "Skip prompts; only run safe migrations").option("--deep", "Scan system services for extra gateway installs").action(async (opts) => {
2826
+ await require_doctor.runDoctor(opts.fix || opts.repair, {
2827
+ fix: opts.fix,
2828
+ repair: opts.repair,
2829
+ force: opts.force,
2830
+ yes: opts.yes,
2831
+ nonInteractive: opts.nonInteractive,
2832
+ deep: opts.deep
2833
+ });
1779
2834
  process.exit(0);
1780
2835
  });
1781
2836
  const memCmd = program.command("memory").description("Agent memory management");
@@ -1857,7 +2912,7 @@ cfgCmd.command("set-service-key <serviceId> [apiKey]").description("Set API key
1857
2912
  cfgCmd.command("schema").description("Show configuration schema").action(() => {
1858
2913
  console.log(chalk.default.bold.hex("#06b6d4")("\n Config schema: ~/.hyperclaw/config.json\n"));
1859
2914
  const schema = {
1860
- version: "string (e.g. \"4.0.2\")",
2915
+ version: "string (e.g. \"5.0.0\")",
1861
2916
  workspaceName: "string",
1862
2917
  provider: {
1863
2918
  providerId: "string",
@@ -1950,17 +3005,17 @@ program.command("deploy").description("Deploy gateway to cloud (Fly.io or Render
1950
3005
  program.command("voice-call").description("Start voice call session — terminal mode, talks to gateway").option("-u, --gateway-url <url>", "Gateway URL", "http://localhost:18789").action(async (opts) => {
1951
3006
  const axios = (await import("axios")).default;
1952
3007
  const readline$2 = await import("readline");
1953
- const chalk$10 = require("chalk");
3008
+ const chalk$11 = require("chalk");
1954
3009
  const url = opts.gatewayUrl || "http://localhost:18789";
1955
- console.log(chalk$10.bold.cyan("\n 🎙️ HYPERCLAW VOICE CALL\n"));
1956
- console.log(chalk$10.gray(` Gateway: ${url}`));
1957
- console.log(chalk$10.gray(" Type a message and press Enter. Ctrl+C to exit.\n"));
3010
+ console.log(chalk$11.bold.cyan("\n 🎙️ HYPERCLAW VOICE CALL\n"));
3011
+ console.log(chalk$11.gray(` Gateway: ${url}`));
3012
+ console.log(chalk$11.gray(" Type a message and press Enter. Ctrl+C to exit.\n"));
1958
3013
  const rl = readline$2.createInterface({
1959
3014
  input: process.stdin,
1960
3015
  output: process.stdout
1961
3016
  });
1962
3017
  const ask = () => {
1963
- rl.question(chalk$10.cyan(" You: "), async (input) => {
3018
+ rl.question(chalk$11.cyan(" You: "), async (input) => {
1964
3019
  if (!input?.trim()) {
1965
3020
  ask();
1966
3021
  return;
@@ -1970,9 +3025,9 @@ program.command("voice-call").description("Start voice call session — terminal
1970
3025
  message: input.trim(),
1971
3026
  thinking: "none"
1972
3027
  }, { timeout: 6e4 });
1973
- console.log(chalk$10.green(` 🦅 Agent: ${(res.data?.response || "").slice(0, 500)}\n`));
3028
+ console.log(chalk$11.green(` 🦅 Agent: ${(res.data?.response || "").slice(0, 500)}\n`));
1974
3029
  } catch (e) {
1975
- console.log(chalk$10.red(` Error: ${e.response?.data?.error || e.message}\n`));
3030
+ console.log(chalk$11.red(` Error: ${e.response?.data?.error || e.message}\n`));
1976
3031
  }
1977
3032
  ask();
1978
3033
  });
@@ -1992,13 +3047,102 @@ program.command("dashboard").description("Launch live terminal dashboard").optio
1992
3047
  await new Dashboard().launch(opts.live);
1993
3048
  if (!opts.live) process.exit(0);
1994
3049
  });
1995
- program.command("status").description("System overview").action(async () => {
3050
+ program.command("status").description("System overview").option("--all", "Full local diagnosis (read-only)").option("--deep", "Also probe the running gateway").action(async (opts) => {
1996
3051
  await new require_onboard.Banner().showStatus();
3052
+ if (opts.all || opts.deep) {
3053
+ const fs$7 = await import("fs-extra");
3054
+ const { getConfigPath } = await Promise.resolve().then(() => require("./paths-D-QecARF.js"));
3055
+ const t = (await Promise.resolve().then(() => require("./theme-cx0fkgWC.js"))).getTheme(false);
3056
+ const configPath = getConfigPath();
3057
+ console.log(t.bold("\n ─── Deep status ───\n"));
3058
+ try {
3059
+ const cfg = await fs$7.readJson(configPath);
3060
+ console.log(t.muted(" Config: ") + (cfg ? t.success("loaded") : t.error("missing")));
3061
+ console.log(t.muted(" Channels: ") + JSON.stringify(cfg?.gateway?.enabledChannels || cfg?.channels || []));
3062
+ } catch {
3063
+ console.log(t.muted(" Config: ") + t.error("unreadable"));
3064
+ }
3065
+ if (opts.deep) {
3066
+ const http$2 = await import("http");
3067
+ const { resolveGatewayUrl } = await Promise.resolve().then(() => require("./health-B-asI__D.js"));
3068
+ const cfg = await new require_manager.ConfigManager().load();
3069
+ const { gatewayUrl } = resolveGatewayUrl(cfg);
3070
+ const u = new URL(gatewayUrl);
3071
+ const optsReq = {
3072
+ hostname: u.hostname,
3073
+ port: u.port || (u.protocol === "https:" ? 443 : 80),
3074
+ path: "/api/status",
3075
+ method: "GET",
3076
+ timeout: 3e3
3077
+ };
3078
+ if (u.protocol === "https:") {
3079
+ const https = await import("https");
3080
+ await new Promise((resolve) => {
3081
+ const req = https.request(`${gatewayUrl}/api/status`, { timeout: 3e3 }, (res) => {
3082
+ let d = "";
3083
+ res.on("data", (c) => d += c);
3084
+ res.on("end", () => {
3085
+ try {
3086
+ const j = JSON.parse(d);
3087
+ console.log(t.muted(" Gateway: ") + t.success("reachable") + ` (sessions: ${j.sessions ?? "-"}, uptime: ${j.uptime ?? "-"})`);
3088
+ } catch {
3089
+ console.log(t.muted(" Gateway: ") + t.error("unreachable or invalid response"));
3090
+ }
3091
+ resolve();
3092
+ });
3093
+ });
3094
+ req.on("error", () => {
3095
+ console.log(t.muted(" Gateway: ") + t.error("unreachable"));
3096
+ resolve();
3097
+ });
3098
+ req.on("timeout", () => {
3099
+ req.destroy();
3100
+ console.log(t.muted(" Gateway: ") + t.error("timeout"));
3101
+ resolve();
3102
+ });
3103
+ req.end();
3104
+ });
3105
+ } else await new Promise((resolve) => {
3106
+ const req = http$2.request(optsReq, (res) => {
3107
+ let d = "";
3108
+ res.on("data", (c) => d += c);
3109
+ res.on("end", () => {
3110
+ try {
3111
+ const j = JSON.parse(d);
3112
+ console.log(t.muted(" Gateway: ") + t.success("reachable") + ` (sessions: ${j.sessions ?? "-"}, uptime: ${j.uptime ?? "-"})`);
3113
+ } catch {
3114
+ console.log(t.muted(" Gateway: ") + t.error("unreachable or invalid response"));
3115
+ }
3116
+ resolve();
3117
+ });
3118
+ });
3119
+ req.on("error", () => {
3120
+ console.log(t.muted(" Gateway: ") + t.error("unreachable"));
3121
+ resolve();
3122
+ });
3123
+ req.on("timeout", () => {
3124
+ req.destroy();
3125
+ console.log(t.muted(" Gateway: ") + t.error("timeout"));
3126
+ resolve();
3127
+ });
3128
+ req.end();
3129
+ });
3130
+ }
3131
+ console.log();
3132
+ }
1997
3133
  process.exit(0);
1998
3134
  });
3135
+ program.command("health").description("Quick gateway health probe (Runtime, RPC probe, channel count)").option("--json", "Output raw JSON").option("-v, --verbose", "Show state dir, config path, and env overrides").action(async (opts) => {
3136
+ const result = await require_health.runHealth({
3137
+ json: opts.json,
3138
+ verbose: opts.verbose
3139
+ });
3140
+ if (!result.allOk) process.exitCode = 1;
3141
+ process.exit(process.exitCode ?? 0);
3142
+ });
1999
3143
  const themeCmd = program.command("theme").description("Switch CLI color theme");
2000
3144
  themeCmd.command("list").description("List available themes").action(async () => {
2001
- const { allThemes, getThemeName } = await Promise.resolve().then(() => require("./theme-Iefa3L63.js"));
3145
+ const { allThemes, getThemeName } = await Promise.resolve().then(() => require("./theme-cx0fkgWC.js"));
2002
3146
  const current = getThemeName();
2003
3147
  console.log(chalk.default.bold.hex("#06b6d4")("\n 🎨 AVAILABLE THEMES\n"));
2004
3148
  for (const { name, label } of allThemes()) {
@@ -2010,7 +3154,7 @@ themeCmd.command("list").description("List available themes").action(async () =>
2010
3154
  process.exit(0);
2011
3155
  });
2012
3156
  themeCmd.command("set <theme>").description("Set theme: dark | grey | white").action(async (name) => {
2013
- const { setThemeName, allThemes } = await Promise.resolve().then(() => require("./theme-Iefa3L63.js"));
3157
+ const { setThemeName, allThemes } = await Promise.resolve().then(() => require("./theme-cx0fkgWC.js"));
2014
3158
  const valid = allThemes().map((t) => t.name);
2015
3159
  if (!valid.includes(name)) {
2016
3160
  console.log(chalk.default.red(`\n ✖ Unknown theme: "${name}". Use: ${valid.join(" | ")}\n`));
@@ -2022,7 +3166,7 @@ themeCmd.command("set <theme>").description("Set theme: dark | grey | white").ac
2022
3166
  process.exit(0);
2023
3167
  });
2024
3168
  themeCmd.command("preview").description("Preview all themes side-by-side").action(async () => {
2025
- const { allThemes, getTheme, setThemeName, getThemeName } = await Promise.resolve().then(() => require("./theme-Iefa3L63.js"));
3169
+ const { allThemes, getTheme, setThemeName, getThemeName } = await Promise.resolve().then(() => require("./theme-cx0fkgWC.js"));
2026
3170
  const current = getThemeName();
2027
3171
  console.log(chalk.default.bold("\n 🎨 THEME PREVIEW\n"));
2028
3172
  for (const { name, label } of allThemes()) {
@@ -2039,45 +3183,49 @@ themeCmd.command("preview").description("Preview all themes side-by-side").actio
2039
3183
  });
2040
3184
  const secretsCmd = program.command("secrets").description("External secrets management");
2041
3185
  secretsCmd.command("audit").description("Audit all required secrets").option("--required-by <ids>", "Filter by skill/provider IDs (comma-separated)").action(async (opts) => {
2042
- const { SecretsManager } = await Promise.resolve().then(() => require("./manager-DgyF52mg.js"));
3186
+ const { SecretsManager } = await Promise.resolve().then(() => require("./manager-FCgF1plu.js"));
2043
3187
  const filter = opts.requiredBy?.split(",");
2044
3188
  await new SecretsManager().audit(filter);
2045
3189
  process.exit(0);
2046
3190
  });
2047
3191
  secretsCmd.command("set <KEY=value>").description("Set a secret in .env file").action(async (kv) => {
2048
- const { SecretsManager } = await Promise.resolve().then(() => require("./manager-DgyF52mg.js"));
3192
+ const { SecretsManager } = await Promise.resolve().then(() => require("./manager-FCgF1plu.js"));
2049
3193
  await new SecretsManager().set(kv);
2050
3194
  process.exit(0);
2051
3195
  });
2052
3196
  secretsCmd.command("apply").description("Write secrets from .env to shell config (~/.bashrc, ~/.zshrc)").action(async () => {
2053
- const { SecretsManager } = await Promise.resolve().then(() => require("./manager-DgyF52mg.js"));
3197
+ const { SecretsManager } = await Promise.resolve().then(() => require("./manager-FCgF1plu.js"));
2054
3198
  await new SecretsManager().apply();
2055
3199
  process.exit(0);
2056
3200
  });
2057
3201
  secretsCmd.command("reload").description("Reload secrets into running gateway").action(async () => {
2058
- const { SecretsManager } = await Promise.resolve().then(() => require("./manager-DgyF52mg.js"));
3202
+ const { SecretsManager } = await Promise.resolve().then(() => require("./manager-FCgF1plu.js"));
2059
3203
  await new SecretsManager().reload();
2060
3204
  process.exit(0);
2061
3205
  });
2062
3206
  secretsCmd.command("remove <key>").description("Remove a secret from .env").action(async (key) => {
2063
- const { SecretsManager } = await Promise.resolve().then(() => require("./manager-DgyF52mg.js"));
3207
+ const { SecretsManager } = await Promise.resolve().then(() => require("./manager-FCgF1plu.js"));
2064
3208
  await new SecretsManager().remove(key);
2065
3209
  process.exit(0);
2066
3210
  });
2067
3211
  secretsCmd.command("credentials").description("List provider credential files (credentials/*.json)").action(async () => {
2068
- const { CredentialsStore } = await Promise.resolve().then(() => require("./credentials-store-BvnMPJwi.js"));
3212
+ const { CredentialsStore } = await Promise.resolve().then(() => require("./credentials-store-C6ir0Dae.js"));
2069
3213
  await new CredentialsStore().showList();
2070
3214
  process.exit(0);
2071
3215
  });
2072
3216
  const securityCmd = program.command("security").description("Security tools");
2073
- securityCmd.command("audit").description("Security audit — file permissions, DM policies, embedded secrets").option("--deep", "Full deep scan including token entropy and installed skill risks").action(async (opts) => {
2074
- const { runSecurityAudit } = await Promise.resolve().then(() => require("./audit-BYxPlnTQ.js"));
2075
- await runSecurityAudit(opts.deep);
3217
+ securityCmd.command("audit").description("Security audit — file permissions, DM policies, embedded secrets").option("--deep", "Full deep scan including token entropy and installed skill risks").option("--fix", "Auto-fix safe findings (file permissions etc.)").option("--json", "Machine-readable JSON output").action(async (opts) => {
3218
+ const { runSecurityAudit } = await Promise.resolve().then(() => require("./audit-BaIiyWFu.js"));
3219
+ await runSecurityAudit({
3220
+ deep: opts.deep,
3221
+ fix: opts.fix,
3222
+ json: opts.json
3223
+ });
2076
3224
  process.exit(0);
2077
3225
  });
2078
3226
  const agentRunCmd = program.command("agent").description("Run agent with thinking control");
2079
3227
  agentRunCmd.requiredOption("-m, --message <text>", "Message to send to the agent").option("--thinking <level>", "Thinking level: high|medium|low|none", "none").option("--model <model>", "Override model").option("--session <id>", "Session/thread ID").option("--multi-step", "Decompose into steps and run each (sequential)").option("--parallel", "Run sub-agents in parallel for independent subtasks").option("--verbose", "Show thinking blocks and request details").option("--workspace <dir>", "Override workspace directory").action(async (opts) => {
2080
- const { runAgent } = await Promise.resolve().then(() => require("./src-BEVLgaF1.js"));
3228
+ const { runAgent } = await Promise.resolve().then(() => require("./src-BxPHKO5x.js"));
2081
3229
  await runAgent({
2082
3230
  message: opts.message,
2083
3231
  thinking: opts.thinking,
@@ -2093,7 +3241,7 @@ agentRunCmd.requiredOption("-m, --message <text>", "Message to send to the agent
2093
3241
  });
2094
3242
  const threadsCmd = program.command("threads").description("ACP thread-bound agent sessions");
2095
3243
  threadsCmd.command("list").description("List agent threads").option("--channel <id>", "Filter by channel").option("--active", "Show only active threads").action(async (opts) => {
2096
- const { ACPThreadManager } = await Promise.resolve().then(() => require("./src-BEVLgaF1.js"));
3244
+ const { ACPThreadManager } = await Promise.resolve().then(() => require("./src-BxPHKO5x.js"));
2097
3245
  const mgr = new ACPThreadManager();
2098
3246
  const threads = await mgr.list({
2099
3247
  channelId: opts.channel,
@@ -2103,33 +3251,33 @@ threadsCmd.command("list").description("List agent threads").option("--channel <
2103
3251
  process.exit(0);
2104
3252
  });
2105
3253
  threadsCmd.command("terminate <id>").description("Terminate a thread").action(async (id) => {
2106
- const { ACPThreadManager } = await Promise.resolve().then(() => require("./src-BEVLgaF1.js"));
3254
+ const { ACPThreadManager } = await Promise.resolve().then(() => require("./src-BxPHKO5x.js"));
2107
3255
  await new ACPThreadManager().terminate(id);
2108
3256
  console.log(require("chalk").green(`\n ✔ Thread terminated: ${id}\n`));
2109
3257
  process.exit(0);
2110
3258
  });
2111
3259
  const canvasCmd = program.command("canvas").description("Live AI-driven UI canvas");
2112
3260
  canvasCmd.command("show").description("Show current canvas components").action(async () => {
2113
- const { CanvasRenderer } = await Promise.resolve().then(() => require("./renderer-pqlDRKbH.js"));
3261
+ const { CanvasRenderer } = await Promise.resolve().then(() => require("./renderer-BVQrd0_g.js"));
2114
3262
  await new CanvasRenderer().show();
2115
3263
  process.exit(0);
2116
3264
  });
2117
3265
  canvasCmd.command("add <type> <title>").description("Add a canvas component (type: chart|table|form|markdown|image|custom)").action(async (type, title) => {
2118
- const { CanvasRenderer } = await Promise.resolve().then(() => require("./renderer-pqlDRKbH.js"));
3266
+ const { CanvasRenderer } = await Promise.resolve().then(() => require("./renderer-BVQrd0_g.js"));
2119
3267
  await new CanvasRenderer().addComponent(type, title);
2120
3268
  process.exit(0);
2121
3269
  });
2122
3270
  canvasCmd.command("clear").description("Clear all canvas components").action(async () => {
2123
- const { CanvasRenderer } = await Promise.resolve().then(() => require("./renderer-pqlDRKbH.js"));
3271
+ const { CanvasRenderer } = await Promise.resolve().then(() => require("./renderer-BVQrd0_g.js"));
2124
3272
  await new CanvasRenderer().clear();
2125
3273
  process.exit(0);
2126
3274
  });
2127
3275
  canvasCmd.command("export").description("Export canvas as HTML file").action(async () => {
2128
- const { CanvasRenderer } = await Promise.resolve().then(() => require("./renderer-pqlDRKbH.js"));
2129
- const fs$6 = require("fs-extra");
3276
+ const { CanvasRenderer } = await Promise.resolve().then(() => require("./renderer-BVQrd0_g.js"));
3277
+ const fs$7 = require("fs-extra");
2130
3278
  const html = await new CanvasRenderer().exportHtml();
2131
3279
  const outFile = require("path").join(require("os").homedir(), ".hyperclaw", "canvas", "export.html");
2132
- await fs$6.writeFile(outFile, html);
3280
+ await fs$7.writeFile(outFile, html);
2133
3281
  console.log(require("chalk").green(`\n ✔ Canvas exported to ${outFile}\n`));
2134
3282
  process.exit(0);
2135
3283
  });
@@ -2146,167 +3294,167 @@ deliveryCmd.command("retry <id>").description("Retry a dead-lettered delivery it
2146
3294
  });
2147
3295
  const mcpCmd = program.command("mcp").description("MCP (Model Context Protocol) server management");
2148
3296
  mcpCmd.command("list").action(async () => {
2149
- const { mcpList } = await Promise.resolve().then(() => require("./mcp-B_9Ber63.js"));
3297
+ const { mcpList } = await Promise.resolve().then(() => require("./mcp-CfoSU4Uz.js"));
2150
3298
  await mcpList();
2151
3299
  process.exit(0);
2152
3300
  });
2153
3301
  mcpCmd.command("add").action(async () => {
2154
- const { mcpAdd } = await Promise.resolve().then(() => require("./mcp-B_9Ber63.js"));
3302
+ const { mcpAdd } = await Promise.resolve().then(() => require("./mcp-CfoSU4Uz.js"));
2155
3303
  await mcpAdd();
2156
3304
  process.exit(0);
2157
3305
  });
2158
3306
  mcpCmd.command("remove <id>").action(async (id) => {
2159
- const { mcpRemove } = await Promise.resolve().then(() => require("./mcp-B_9Ber63.js"));
3307
+ const { mcpRemove } = await Promise.resolve().then(() => require("./mcp-CfoSU4Uz.js"));
2160
3308
  await mcpRemove(id);
2161
3309
  process.exit(0);
2162
3310
  });
2163
3311
  mcpCmd.command("probe [id]").action(async (id) => {
2164
- const { mcpProbe } = await Promise.resolve().then(() => require("./mcp-B_9Ber63.js"));
3312
+ const { mcpProbe } = await Promise.resolve().then(() => require("./mcp-CfoSU4Uz.js"));
2165
3313
  await mcpProbe(id);
2166
3314
  process.exit(0);
2167
3315
  });
2168
3316
  const nodeCmd = program.command("node").description("HyperClaw node management (local, remote, android)");
2169
3317
  nodeCmd.command("list").action(async () => {
2170
- const { nodeList } = await Promise.resolve().then(() => require("./node-pwL6O_KX.js"));
3318
+ const { nodeList } = await Promise.resolve().then(() => require("./node-Dw2Gi-cP.js"));
2171
3319
  await nodeList();
2172
3320
  process.exit(0);
2173
3321
  });
2174
3322
  nodeCmd.command("add").action(async () => {
2175
- const { nodeAdd } = await Promise.resolve().then(() => require("./node-pwL6O_KX.js"));
3323
+ const { nodeAdd } = await Promise.resolve().then(() => require("./node-Dw2Gi-cP.js"));
2176
3324
  await nodeAdd();
2177
3325
  process.exit(0);
2178
3326
  });
2179
3327
  nodeCmd.command("probe [id]").action(async (id) => {
2180
- const { nodeProbe } = await Promise.resolve().then(() => require("./node-pwL6O_KX.js"));
3328
+ const { nodeProbe } = await Promise.resolve().then(() => require("./node-Dw2Gi-cP.js"));
2181
3329
  await nodeProbe(id);
2182
3330
  process.exit(0);
2183
3331
  });
2184
3332
  nodeCmd.command("remove <id>").action(async (id) => {
2185
- const { nodeRemove } = await Promise.resolve().then(() => require("./node-pwL6O_KX.js"));
3333
+ const { nodeRemove } = await Promise.resolve().then(() => require("./node-Dw2Gi-cP.js"));
2186
3334
  await nodeRemove(id);
2187
3335
  process.exit(0);
2188
3336
  });
2189
3337
  const arCmd = program.command("auto-reply").description("Auto-reply rule engine");
2190
3338
  arCmd.command("list").action(async () => {
2191
- const { AutoReplyEngine } = await Promise.resolve().then(() => require("./rules-BooT_qFP.js"));
3339
+ const { AutoReplyEngine } = await Promise.resolve().then(() => require("./rules-BE4GV6cV.js"));
2192
3340
  const e = new AutoReplyEngine();
2193
3341
  await e.load();
2194
3342
  e.showList();
2195
3343
  process.exit(0);
2196
3344
  });
2197
3345
  arCmd.command("toggle <id>").action(async (id) => {
2198
- const { AutoReplyEngine } = await Promise.resolve().then(() => require("./rules-BooT_qFP.js"));
3346
+ const { AutoReplyEngine } = await Promise.resolve().then(() => require("./rules-BE4GV6cV.js"));
2199
3347
  const e = new AutoReplyEngine();
2200
3348
  await e.toggle(id);
2201
3349
  process.exit(0);
2202
3350
  });
2203
3351
  arCmd.command("remove <id>").action(async (id) => {
2204
- const { AutoReplyEngine } = await Promise.resolve().then(() => require("./rules-BooT_qFP.js"));
3352
+ const { AutoReplyEngine } = await Promise.resolve().then(() => require("./rules-BE4GV6cV.js"));
2205
3353
  const e = new AutoReplyEngine();
2206
3354
  await e.remove(id);
2207
3355
  process.exit(0);
2208
3356
  });
2209
3357
  const gmailCmd = program.command("gmail").description("Gmail Pub/Sub real-time notifications");
2210
3358
  gmailCmd.command("watch-setup").description("Register Gmail watch for push notifications. Requires: hyperclaw auth oauth google-gmail").requiredOption("-t, --topic <name>", "Pub/Sub topic (e.g. projects/myproject/topics/gmail-push)").option("-l, --labels <ids>", "Label IDs to watch (comma-separated)", "INBOX").action(async (opts) => {
2211
- const chalk$10 = require("chalk");
3359
+ const chalk$11 = require("chalk");
2212
3360
  try {
2213
- const { setupGmailWatch } = await Promise.resolve().then(() => require("./gmail-watch-setup-Czt8rXaX.js"));
3361
+ const { setupGmailWatch } = await Promise.resolve().then(() => require("./gmail-watch-setup-Du7DVV7S.js"));
2214
3362
  const labelIds = opts.labels.split(",").map((s) => s.trim()).filter(Boolean);
2215
3363
  const result = await setupGmailWatch({
2216
3364
  topicName: opts.topic,
2217
3365
  labelIds
2218
3366
  });
2219
- console.log(chalk$10.hex("#06b6d4")("\n ✔ Gmail watch registered"));
2220
- console.log(chalk$10.gray(` historyId: ${result.historyId}`));
2221
- console.log(chalk$10.gray(` expiration: ${new Date(parseInt(result.expiration, 10)).toISOString()}`));
2222
- console.log(chalk$10.gray("\n Push endpoint: https://<your-server>/webhook/gmail-pubsub"));
2223
- console.log(chalk$10.gray(" Ensure email channel is enabled and gateway is publicly accessible.\n"));
3367
+ console.log(chalk$11.hex("#06b6d4")("\n ✔ Gmail watch registered"));
3368
+ console.log(chalk$11.gray(` historyId: ${result.historyId}`));
3369
+ console.log(chalk$11.gray(` expiration: ${new Date(parseInt(result.expiration, 10)).toISOString()}`));
3370
+ console.log(chalk$11.gray("\n Push endpoint: https://<your-server>/webhook/gmail-pubsub"));
3371
+ console.log(chalk$11.gray(" Ensure email channel is enabled and gateway is publicly accessible.\n"));
2224
3372
  } catch (e) {
2225
- console.error(chalk$10.red("\n ✖ " + e.message + "\n"));
3373
+ console.error(chalk$11.red("\n ✖ " + e.message + "\n"));
2226
3374
  process.exit(1);
2227
3375
  }
2228
3376
  process.exit(0);
2229
3377
  });
2230
3378
  const cronCmd = program.command("cron").description("Scheduled tasks (cron → agent prompt)");
2231
3379
  cronCmd.command("list").action(async () => {
2232
- const chalk$10 = require("chalk");
2233
- const { loadCronTasks } = await Promise.resolve().then(() => require("./cron-tasks-BvDFNyiE.js"));
3380
+ const chalk$11 = require("chalk");
3381
+ const { loadCronTasks } = await Promise.resolve().then(() => require("./cron-tasks-Bli7Kzd2.js"));
2234
3382
  const tasks = await loadCronTasks();
2235
- console.log(chalk$10.bold.cyan("\n ⏰ CRON TASKS\n"));
3383
+ console.log(chalk$11.bold.cyan("\n ⏰ CRON TASKS\n"));
2236
3384
  if (tasks.length === 0) {
2237
- console.log(chalk$10.gray(" No tasks. Add: hyperclaw cron add \"0 9 * * 1-5\" \"Check calendar\"\n"));
3385
+ console.log(chalk$11.gray(" No tasks. Add: hyperclaw cron add \"0 9 * * 1-5\" \"Check calendar\"\n"));
2238
3386
  process.exit(0);
2239
3387
  return;
2240
3388
  }
2241
3389
  for (const t of tasks) {
2242
- const dot = t.enabled ? chalk$10.green("●") : chalk$10.gray("○");
2243
- console.log(` ${dot} ${chalk$10.white(t.name || t.id)}`);
2244
- console.log(` ${chalk$10.gray("Schedule:")} ${t.schedule}`);
2245
- console.log(` ${chalk$10.gray("Prompt:")} ${t.prompt.slice(0, 60)}${t.prompt.length > 60 ? "..." : ""}`);
2246
- if (t.lastRunAt) console.log(` ${chalk$10.gray("Last run:")} ${t.lastRunAt}`);
3390
+ const dot = t.enabled ? chalk$11.green("●") : chalk$11.gray("○");
3391
+ console.log(` ${dot} ${chalk$11.white(t.name || t.id)}`);
3392
+ console.log(` ${chalk$11.gray("Schedule:")} ${t.schedule}`);
3393
+ console.log(` ${chalk$11.gray("Prompt:")} ${t.prompt.slice(0, 60)}${t.prompt.length > 60 ? "..." : ""}`);
3394
+ if (t.lastRunAt) console.log(` ${chalk$11.gray("Last run:")} ${t.lastRunAt}`);
2247
3395
  console.log();
2248
3396
  }
2249
3397
  process.exit(0);
2250
3398
  });
2251
3399
  cronCmd.command("add").arguments("<schedule> <prompt>").option("-n, --name <name>", "Task name").action(async (schedule, prompt, opts) => {
2252
- const chalk$10 = require("chalk");
2253
- const { loadCronTasks, addCronTask, saveCronTasks } = await Promise.resolve().then(() => require("./cron-tasks-BvDFNyiE.js"));
3400
+ const chalk$11 = require("chalk");
3401
+ const { loadCronTasks, addCronTask, saveCronTasks } = await Promise.resolve().then(() => require("./cron-tasks-Bli7Kzd2.js"));
2254
3402
  await loadCronTasks();
2255
3403
  addCronTask(schedule, prompt, opts.name);
2256
3404
  await saveCronTasks();
2257
- console.log(chalk$10.green(`\n ✔ Cron task added: ${schedule} → "${prompt.slice(0, 40)}..."\n`));
2258
- console.log(chalk$10.gray(" Restart gateway to apply.\n"));
3405
+ console.log(chalk$11.green(`\n ✔ Cron task added: ${schedule} → "${prompt.slice(0, 40)}..."\n`));
3406
+ console.log(chalk$11.gray(" Restart gateway to apply.\n"));
2259
3407
  process.exit(0);
2260
3408
  });
2261
3409
  cronCmd.command("remove <id>").action(async (id) => {
2262
- const chalk$10 = require("chalk");
2263
- const { loadCronTasks, removeCronTask, saveCronTasks } = await Promise.resolve().then(() => require("./cron-tasks-BvDFNyiE.js"));
3410
+ const chalk$11 = require("chalk");
3411
+ const { loadCronTasks, removeCronTask, saveCronTasks } = await Promise.resolve().then(() => require("./cron-tasks-Bli7Kzd2.js"));
2264
3412
  await loadCronTasks();
2265
3413
  if (removeCronTask(id)) {
2266
3414
  await saveCronTasks();
2267
- console.log(chalk$10.green(`\n ✔ Task removed\n`));
2268
- } else console.log(chalk$10.red(`\n ✖ Task not found: ${id}\n`));
3415
+ console.log(chalk$11.green(`\n ✔ Task removed\n`));
3416
+ } else console.log(chalk$11.red(`\n ✖ Task not found: ${id}\n`));
2269
3417
  process.exit(0);
2270
3418
  });
2271
3419
  program.command("nodes").description("List connected mobile nodes (iOS/Android Connect tab)").action(async () => {
2272
- const chalk$10 = require("chalk");
2273
- const http = await import("http");
2274
- const fs$6 = await import("fs-extra");
2275
- const path$6 = await import("path");
2276
- const os$7 = await import("os");
3420
+ const chalk$11 = require("chalk");
3421
+ const http$2 = await import("http");
3422
+ const fs$7 = await import("fs-extra");
3423
+ const path$7 = await import("path");
3424
+ const os$8 = await import("os");
2277
3425
  let port = 18789;
2278
3426
  try {
2279
- const cfg = await fs$6.readJson(path$6.join(os$7.homedir(), ".hyperclaw", "hyperclaw.json"));
3427
+ const cfg = await fs$7.readJson(path$7.join(os$8.homedir(), ".hyperclaw", "hyperclaw.json"));
2280
3428
  port = cfg?.gateway?.port ?? 18789;
2281
3429
  } catch {}
2282
3430
  return new Promise((resolve, reject) => {
2283
- const req = http.get(`http://127.0.0.1:${port}/api/nodes`, (res) => {
3431
+ const req = http$2.get(`http://127.0.0.1:${port}/api/nodes`, (res) => {
2284
3432
  let data = "";
2285
3433
  res.on("data", (c) => data += c);
2286
3434
  res.on("end", () => {
2287
3435
  try {
2288
3436
  const j = JSON.parse(data);
2289
3437
  const nodes = j.nodes || [];
2290
- console.log(chalk$10.bold.cyan("\n 📱 CONNECTED NODES\n"));
3438
+ console.log(chalk$11.bold.cyan("\n 📱 CONNECTED NODES\n"));
2291
3439
  if (nodes.length === 0) {
2292
- console.log(chalk$10.gray(" No mobile nodes. Open iOS/Android app → Connect tab → pair with gateway."));
2293
- console.log(chalk$10.gray(` Gateway: ws://localhost:${port}\n`));
3440
+ console.log(chalk$11.gray(" No mobile nodes. Open iOS/Android app → Connect tab → pair with gateway."));
3441
+ console.log(chalk$11.gray(` Gateway: ws://localhost:${port}\n`));
2294
3442
  } else {
2295
3443
  for (const n of nodes) {
2296
- console.log(` ${chalk$10.green("●")} ${n.nodeId} ${chalk$10.gray(`(${n.platform || "?"})`)}`);
2297
- console.log(` ${chalk$10.gray("Device:")} ${n.deviceName || "—"}`);
2298
- console.log(` ${chalk$10.gray("Capabilities:")} ${Object.entries(n.capabilities || {}).filter(([, v]) => v).map(([k]) => k).join(", ") || "—"}`);
3444
+ console.log(` ${chalk$11.green("●")} ${n.nodeId} ${chalk$11.gray(`(${n.platform || "?"})`)}`);
3445
+ console.log(` ${chalk$11.gray("Device:")} ${n.deviceName || "—"}`);
3446
+ console.log(` ${chalk$11.gray("Capabilities:")} ${Object.entries(n.capabilities || {}).filter(([, v]) => v).map(([k]) => k).join(", ") || "—"}`);
2299
3447
  }
2300
3448
  console.log();
2301
3449
  }
2302
3450
  } catch {
2303
- console.log(chalk$10.red(" Could not reach gateway. Start with: hyperclaw daemon start\n"));
3451
+ console.log(chalk$11.red(" Could not reach gateway. Start with: hyperclaw daemon start\n"));
2304
3452
  }
2305
3453
  resolve();
2306
3454
  });
2307
3455
  });
2308
3456
  req.on("error", () => {
2309
- console.log(chalk$10.red(" Gateway offline. Start with: hyperclaw daemon start\n"));
3457
+ console.log(chalk$11.red(" Gateway offline. Start with: hyperclaw daemon start\n"));
2310
3458
  resolve();
2311
3459
  });
2312
3460
  req.setTimeout(3e3, () => {
@@ -2317,20 +3465,20 @@ program.command("nodes").description("List connected mobile nodes (iOS/Android C
2317
3465
  });
2318
3466
  const whCmd = program.command("webhooks").description("Webhook endpoint management");
2319
3467
  whCmd.command("list").action(async () => {
2320
- const { WebhookManager } = await Promise.resolve().then(() => require("./manager-DSGhn5i3.js"));
3468
+ const { WebhookManager } = await Promise.resolve().then(() => require("./manager-BpDfbDjg.js"));
2321
3469
  const m = new WebhookManager();
2322
3470
  await m.load();
2323
3471
  m.showList();
2324
3472
  process.exit(0);
2325
3473
  });
2326
3474
  whCmd.command("remove <id>").action(async (id) => {
2327
- const { WebhookManager } = await Promise.resolve().then(() => require("./manager-DSGhn5i3.js"));
3475
+ const { WebhookManager } = await Promise.resolve().then(() => require("./manager-BpDfbDjg.js"));
2328
3476
  const m = new WebhookManager();
2329
3477
  await m.remove(id);
2330
3478
  process.exit(0);
2331
3479
  });
2332
3480
  whCmd.command("toggle <id>").action(async (id) => {
2333
- const { WebhookManager } = await Promise.resolve().then(() => require("./manager-DSGhn5i3.js"));
3481
+ const { WebhookManager } = await Promise.resolve().then(() => require("./manager-BpDfbDjg.js"));
2334
3482
  const m = new WebhookManager();
2335
3483
  await m.toggle(id);
2336
3484
  process.exit(0);
@@ -2339,7 +3487,7 @@ const logsCmd = program.command("logs").description("View gateway logs");
2339
3487
  logsCmd.option("-n, --lines <n>", "Number of lines to show", "50");
2340
3488
  logsCmd.option("-f, --follow", "Stream logs in real time");
2341
3489
  logsCmd.action(async (opts) => {
2342
- const { tailLog, streamLog } = await Promise.resolve().then(() => require("./logger-8tEtAd3y.js"));
3490
+ const { tailLog, streamLog } = await Promise.resolve().then(() => require("./logger-ybOp7VOC.js"));
2343
3491
  if (opts.follow) await streamLog();
2344
3492
  else {
2345
3493
  await tailLog(parseInt(opts.lines));
@@ -2347,7 +3495,7 @@ logsCmd.action(async (opts) => {
2347
3495
  }
2348
3496
  });
2349
3497
  program.command("gateway:serve").description("Start the gateway server in the foreground (used by daemon)").action(async () => {
2350
- const { startGateway } = await Promise.resolve().then(() => require("./server-DU9POoWc.js"));
3498
+ const { startGateway } = await Promise.resolve().then(() => require("./server-D4wVHiX9.js"));
2351
3499
  await startGateway();
2352
3500
  process.on("SIGINT", () => process.exit(0));
2353
3501
  process.on("SIGTERM", () => process.exit(0));
@@ -2355,15 +3503,15 @@ program.command("gateway:serve").description("Start the gateway server in the fo
2355
3503
  });
2356
3504
  const gatewayCfgCmd = gatewayCmd.command("config").description("Configure gateway settings");
2357
3505
  gatewayCfgCmd.option("--set-token <token>", "Set gateway auth token").option("--regenerate-token", "Generate a new random token").option("--set-port <port>", "Set gateway port").option("--set-bind <addr>", "Set gateway bind address").action(async (opts) => {
2358
- const chalk$10 = require("chalk");
2359
- const fs$6 = require("fs-extra");
2360
- const path$6 = require("path");
2361
- const os$7 = require("os");
2362
- const crypto = require("crypto");
2363
- const cfgFile = path$6.join(os$7.homedir(), ".hyperclaw", "hyperclaw.json");
3506
+ const chalk$11 = require("chalk");
3507
+ const fs$7 = require("fs-extra");
3508
+ const path$7 = require("path");
3509
+ const os$8 = require("os");
3510
+ const crypto$2 = require("crypto");
3511
+ const cfgFile = path$7.join(os$8.homedir(), ".hyperclaw", "hyperclaw.json");
2364
3512
  let cfg = {};
2365
3513
  try {
2366
- cfg = await fs$6.readJson(cfgFile);
3514
+ cfg = await fs$7.readJson(cfgFile);
2367
3515
  } catch {}
2368
3516
  if (!cfg.gateway) cfg.gateway = {
2369
3517
  port: 18789,
@@ -2374,48 +3522,48 @@ gatewayCfgCmd.option("--set-token <token>", "Set gateway auth token").option("--
2374
3522
  hooks: true
2375
3523
  };
2376
3524
  if (opts.regenerateToken) {
2377
- cfg.gateway.authToken = crypto.randomBytes(32).toString("hex");
2378
- console.log(chalk$10.hex("#06b6d4")("\n ✔ New gateway token generated"));
2379
- console.log(chalk$10.gray(` Token: ${cfg.gateway.authToken}`));
3525
+ cfg.gateway.authToken = crypto$2.randomBytes(32).toString("hex");
3526
+ console.log(chalk$11.hex("#06b6d4")("\n ✔ New gateway token generated"));
3527
+ console.log(chalk$11.gray(` Token: ${cfg.gateway.authToken}`));
2380
3528
  }
2381
3529
  if (opts.setToken) {
2382
3530
  cfg.gateway.authToken = opts.setToken;
2383
- console.log(chalk$10.hex("#06b6d4")(`\n ✔ Gateway token set`));
3531
+ console.log(chalk$11.hex("#06b6d4")(`\n ✔ Gateway token set`));
2384
3532
  }
2385
3533
  if (opts.setPort) {
2386
3534
  cfg.gateway.port = parseInt(opts.setPort);
2387
- console.log(chalk$10.hex("#06b6d4")(`\n ✔ Port set to ${cfg.gateway.port}`));
3535
+ console.log(chalk$11.hex("#06b6d4")(`\n ✔ Port set to ${cfg.gateway.port}`));
2388
3536
  }
2389
3537
  if (opts.setBind) {
2390
3538
  cfg.gateway.bind = opts.setBind;
2391
- console.log(chalk$10.hex("#06b6d4")(`\n ✔ Bind set to ${cfg.gateway.bind}`));
3539
+ console.log(chalk$11.hex("#06b6d4")(`\n ✔ Bind set to ${cfg.gateway.bind}`));
2392
3540
  }
2393
- await fs$6.ensureDir(path$6.dirname(cfgFile));
2394
- await fs$6.writeJson(cfgFile, cfg, { spaces: 2 });
2395
- await fs$6.chmod(cfgFile, 384);
2396
- console.log(chalk$10.gray(` Saved to ${cfgFile}\n`));
3541
+ await fs$7.ensureDir(path$7.dirname(cfgFile));
3542
+ await fs$7.writeJson(cfgFile, cfg, { spaces: 2 });
3543
+ await fs$7.chmod(cfgFile, 384);
3544
+ console.log(chalk$11.gray(` Saved to ${cfgFile}\n`));
2397
3545
  process.exit(0);
2398
3546
  });
2399
3547
  const authCmd = program.command("auth").description("OAuth and provider credentials");
2400
3548
  authCmd.command("add <service_id>").description("Add API key for a service (any provider we do not ship). Stored in credentials/ and .env.").option("--key <api_key>", "API key (prompts if omitted)").option("--base-url <url>", "Base URL (optional, e.g. https://api.example.com)").option("--env-var <name>", "Env var name (default: <SERVICE_ID>_API_KEY)").action(async (serviceId, opts) => {
2401
- const chalk$10 = require("chalk");
3549
+ const chalk$11 = require("chalk");
2402
3550
  const inquirer$2 = require("inquirer");
2403
- const { CredentialsStore } = await Promise.resolve().then(() => require("./credentials-store-BvnMPJwi.js"));
3551
+ const { CredentialsStore } = await Promise.resolve().then(() => require("./credentials-store-C6ir0Dae.js"));
2404
3552
  const { getHyperClawDir, getEnvFilePath } = await Promise.resolve().then(() => require("./paths-D-QecARF.js"));
2405
- const { getApiKeyGuide, GENERIC_API_KEY_STEPS } = await Promise.resolve().then(() => require("./api-keys-guide-Dq5Obbp4.js"));
2406
- const fs$6 = await import("fs-extra");
2407
- const path$6 = await import("path");
3553
+ const { getApiKeyGuide, GENERIC_API_KEY_STEPS } = await Promise.resolve().then(() => require("./api-keys-guide-BCcOl0Q7.js"));
3554
+ const fs$7 = await import("fs-extra");
3555
+ const path$7 = await import("path");
2408
3556
  const guide = getApiKeyGuide(serviceId);
2409
3557
  const steps = guide?.setupSteps ?? GENERIC_API_KEY_STEPS;
2410
- console.log(chalk$10.bold.hex("#06b6d4")(`\n 🔑 Add API key: ${guide?.name ?? serviceId}\n`));
2411
- console.log(chalk$10.bold(" Steps:\n"));
2412
- for (const step of steps) if (step.startsWith(" 🔗")) console.log(chalk$10.hex("#06b6d4")(step));
2413
- else if (step.startsWith(" 💡")) console.log(chalk$10.gray(step));
2414
- else console.log(chalk$10.gray(` ${step}`));
3558
+ console.log(chalk$11.bold.hex("#06b6d4")(`\n 🔑 Add API key: ${guide?.name ?? serviceId}\n`));
3559
+ console.log(chalk$11.bold(" Steps:\n"));
3560
+ for (const step of steps) if (step.startsWith(" 🔗")) console.log(chalk$11.hex("#06b6d4")(step));
3561
+ else if (step.startsWith(" 💡")) console.log(chalk$11.gray(step));
3562
+ else console.log(chalk$11.gray(` ${step}`));
2415
3563
  console.log();
2416
3564
  const safeId = serviceId.replace(/[^a-zA-Z0-9_-]/g, "_").toLowerCase();
2417
3565
  if (!safeId) {
2418
- console.log(chalk$10.red("\n ✖ Invalid service ID\n"));
3566
+ console.log(chalk$11.red("\n ✖ Invalid service ID\n"));
2419
3567
  process.exit(1);
2420
3568
  }
2421
3569
  let apiKey = opts.key || process.env[`${safeId.toUpperCase().replace(/-/g, "_")}_API_KEY`];
@@ -2437,44 +3585,44 @@ authCmd.command("add <service_id>").description("Add API key for a service (any
2437
3585
  });
2438
3586
  const envVar = opts.envVar || `${safeId.toUpperCase().replace(/-/g, "_")}_API_KEY`;
2439
3587
  const envPath = getEnvFilePath();
2440
- await fs$6.ensureDir(path$6.dirname(envPath));
3588
+ await fs$7.ensureDir(path$7.dirname(envPath));
2441
3589
  let envContent = "";
2442
- if (await fs$6.pathExists(envPath)) envContent = await fs$6.readFile(envPath, "utf8");
3590
+ if (await fs$7.pathExists(envPath)) envContent = await fs$7.readFile(envPath, "utf8");
2443
3591
  const envLine = `${envVar}=${apiKey}`;
2444
3592
  const re = new RegExp(`^${envVar}=.*$`, "m");
2445
3593
  if (re.test(envContent)) envContent = envContent.replace(re, envLine);
2446
3594
  else envContent = envContent.trimEnd() + (envContent ? "\n" : "") + envLine + "\n";
2447
- await fs$6.writeFile(envPath, envContent, { mode: 384 });
2448
- console.log(chalk$10.hex("#06b6d4")(`\n ✔ Added: ${safeId}`));
2449
- console.log(chalk$10.gray(` Credentials: ~/.hyperclaw/credentials/${safeId}.json`));
2450
- console.log(chalk$10.gray(` Env: ${envVar} (in .env — use in skills via process.env.${envVar.replace(/-/g, "_")})`));
2451
- console.log(chalk$10.gray("\n Run: hyperclaw secrets apply to add to shell"));
2452
- console.log(chalk$10.gray(" Run: hyperclaw secrets reload to inject into running gateway\n"));
3595
+ await fs$7.writeFile(envPath, envContent, { mode: 384 });
3596
+ console.log(chalk$11.hex("#06b6d4")(`\n ✔ Added: ${safeId}`));
3597
+ console.log(chalk$11.gray(` Credentials: ~/.hyperclaw/credentials/${safeId}.json`));
3598
+ console.log(chalk$11.gray(` Env: ${envVar} (in .env — use in skills via process.env.${envVar.replace(/-/g, "_")})`));
3599
+ console.log(chalk$11.gray("\n Run: hyperclaw secrets apply to add to shell"));
3600
+ console.log(chalk$11.gray(" Run: hyperclaw secrets reload to inject into running gateway\n"));
2453
3601
  process.exit(0);
2454
3602
  });
2455
3603
  authCmd.command("remove <service_id>").description("Remove API key for a service from credentials and .env").action(async (serviceId) => {
2456
- const chalk$10 = require("chalk");
2457
- const { CredentialsStore } = await Promise.resolve().then(() => require("./credentials-store-BvnMPJwi.js"));
3604
+ const chalk$11 = require("chalk");
3605
+ const { CredentialsStore } = await Promise.resolve().then(() => require("./credentials-store-C6ir0Dae.js"));
2458
3606
  const { getHyperClawDir, getEnvFilePath } = await Promise.resolve().then(() => require("./paths-D-QecARF.js"));
2459
- const fs$6 = await import("fs-extra");
3607
+ const fs$7 = await import("fs-extra");
2460
3608
  const safeId = serviceId.replace(/[^a-zA-Z0-9_-]/g, "_").toLowerCase();
2461
3609
  const creds = new CredentialsStore(getHyperClawDir());
2462
3610
  await creds.remove(safeId);
2463
3611
  const envVar = `${safeId.toUpperCase().replace(/-/g, "_")}_API_KEY`;
2464
3612
  const envPath = getEnvFilePath();
2465
- if (await fs$6.pathExists(envPath)) {
2466
- let c = await fs$6.readFile(envPath, "utf8");
3613
+ if (await fs$7.pathExists(envPath)) {
3614
+ let c = await fs$7.readFile(envPath, "utf8");
2467
3615
  c = c.replace(new RegExp(`^${envVar}=.*\n?`, "gm"), "");
2468
- await fs$6.writeFile(envPath, c);
3616
+ await fs$7.writeFile(envPath, c);
2469
3617
  }
2470
- console.log(chalk$10.hex("#06b6d4")(`\n ✔ Removed: ${safeId}\n`));
3618
+ console.log(chalk$11.hex("#06b6d4")(`\n ✔ Removed: ${safeId}\n`));
2471
3619
  process.exit(0);
2472
3620
  });
2473
3621
  authCmd.command("oauth <provider>").description("Run full OAuth flow. Providers: google, google-gmail (Gmail Pub/Sub), microsoft").option("--client-id <id>", "OAuth client ID (or GOOGLE_OAUTH_CLIENT_ID)").option("--client-secret <secret>", "OAuth client secret (optional for PKCE)").action(async (provider, opts) => {
2474
- const chalk$10 = require("chalk");
3622
+ const chalk$11 = require("chalk");
2475
3623
  const ora$4 = (await import("ora")).default;
2476
3624
  try {
2477
- const { runOAuthFlow } = await Promise.resolve().then(() => require("./oauth-flow-CpWlgvNB.js"));
3625
+ const { runOAuthFlow } = await Promise.resolve().then(() => require("./oauth-flow-DQPvMHRH.js"));
2478
3626
  const spinner = ora$4("Starting OAuth flow...").start();
2479
3627
  spinner.text = "Opening browser — complete the consent and return here.";
2480
3628
  const tokens = await runOAuthFlow(provider, {
@@ -2482,7 +3630,7 @@ authCmd.command("oauth <provider>").description("Run full OAuth flow. Providers:
2482
3630
  clientSecret: opts.clientSecret
2483
3631
  });
2484
3632
  spinner.stop();
2485
- const { writeOAuthToken } = await Promise.resolve().then(() => require("./oauth-provider-BZb6qOw5.js"));
3633
+ const { writeOAuthToken } = await Promise.resolve().then(() => require("./oauth-provider-Uo4Nib_c.js"));
2486
3634
  const now = Math.floor(Date.now() / 1e3);
2487
3635
  const expires_at = tokens.expires_in ? now + tokens.expires_in : void 0;
2488
3636
  const tokenUrl = provider === "google" || provider === "google-gmail" ? "https://oauth2.googleapis.com/token" : provider === "microsoft" ? "https://login.microsoftonline.com/common/oauth2/v2.0/token" : void 0;
@@ -2492,19 +3640,19 @@ authCmd.command("oauth <provider>").description("Run full OAuth flow. Providers:
2492
3640
  expires_at,
2493
3641
  token_url: tokenUrl
2494
3642
  });
2495
- console.log(chalk$10.hex("#06b6d4")(`\n ✔ OAuth tokens saved for: ${provider}`));
2496
- console.log(chalk$10.gray(" Set in hyperclaw.json: \"provider\": { \"authType\": \"oauth\", \"providerId\": \"" + provider + "\" }\n"));
3643
+ console.log(chalk$11.hex("#06b6d4")(`\n ✔ OAuth tokens saved for: ${provider}`));
3644
+ console.log(chalk$11.gray(" Set in hyperclaw.json: \"provider\": { \"authType\": \"oauth\", \"providerId\": \"" + provider + "\" }\n"));
2497
3645
  } catch (e) {
2498
- console.error(chalk$10.red("\n ✖ OAuth failed: " + e.message + "\n"));
3646
+ console.error(chalk$11.red("\n ✖ OAuth failed: " + e.message + "\n"));
2499
3647
  process.exit(1);
2500
3648
  }
2501
3649
  process.exit(0);
2502
3650
  });
2503
3651
  authCmd.command("setup-token <provider>").description("Save setup token (Anthropic Claude Pro/Max). Run: claude setup-token, paste result here.").action(async (provider) => {
2504
- const chalk$10 = require("chalk");
3652
+ const chalk$11 = require("chalk");
2505
3653
  const inquirer$2 = await import("inquirer");
2506
3654
  if (provider !== "anthropic") {
2507
- console.log(chalk$10.yellow(`\n Provider "${provider}" may not support setup-token. Use "hyperclaw auth add" for API keys.\n`));
3655
+ console.log(chalk$11.yellow(`\n Provider "${provider}" may not support setup-token. Use "hyperclaw auth add" for API keys.\n`));
2508
3656
  process.exit(1);
2509
3657
  }
2510
3658
  const { token } = await inquirer$2.default.prompt([{
@@ -2514,24 +3662,24 @@ authCmd.command("setup-token <provider>").description("Save setup token (Anthrop
2514
3662
  mask: "●"
2515
3663
  }]);
2516
3664
  if (!token?.trim()) {
2517
- console.log(chalk$10.red("\n ✖ No token provided.\n"));
3665
+ console.log(chalk$11.red("\n ✖ No token provided.\n"));
2518
3666
  process.exit(1);
2519
3667
  }
2520
- const { writeOAuthToken } = await Promise.resolve().then(() => require("./oauth-provider-BZb6qOw5.js"));
3668
+ const { writeOAuthToken } = await Promise.resolve().then(() => require("./oauth-provider-Uo4Nib_c.js"));
2521
3669
  await writeOAuthToken("anthropic-setup", {
2522
3670
  access_token: token.trim(),
2523
3671
  token_url: "https://api.anthropic.com"
2524
3672
  });
2525
- console.log(chalk$10.hex("#06b6d4")("\n ✔ Anthropic setup token saved to ~/.hyperclaw/oauth-anthropic-setup.json"));
2526
- console.log(chalk$10.gray(" Use providerId: anthropic with authType: oauth and oauthTokenPath for Claude Pro/Max.\n"));
3673
+ console.log(chalk$11.hex("#06b6d4")("\n ✔ Anthropic setup token saved to ~/.hyperclaw/oauth-anthropic-setup.json"));
3674
+ console.log(chalk$11.gray(" Use providerId: anthropic with authType: oauth and oauthTokenPath for Claude Pro/Max.\n"));
2527
3675
  process.exit(0);
2528
3676
  });
2529
3677
  authCmd.command("oauth-set <provider>").description("Save OAuth tokens manually (access_token, refresh_token, etc.) to ~/.hyperclaw/oauth-<provider>.json").option("--token <access_token>", "Access token").option("--refresh <refresh_token>", "Refresh token (optional)").option("--expires-in <seconds>", "Token lifetime in seconds (optional)").option("--token-url <url>", "Refresh endpoint URL (optional)").action(async (provider, opts) => {
2530
- const chalk$10 = require("chalk");
2531
- const { writeOAuthToken } = await Promise.resolve().then(() => require("./oauth-provider-BZb6qOw5.js"));
3678
+ const chalk$11 = require("chalk");
3679
+ const { writeOAuthToken } = await Promise.resolve().then(() => require("./oauth-provider-Uo4Nib_c.js"));
2532
3680
  const access_token = opts.token || process.env.OAUTH_ACCESS_TOKEN;
2533
3681
  if (!access_token) {
2534
- console.log(chalk$10.red("\n ✖ Provide --token <access_token> or set OAUTH_ACCESS_TOKEN\n"));
3682
+ console.log(chalk$11.red("\n ✖ Provide --token <access_token> or set OAUTH_ACCESS_TOKEN\n"));
2535
3683
  process.exit(1);
2536
3684
  }
2537
3685
  const expires_at = opts.expiresIn ? Math.floor(Date.now() / 1e3) + parseInt(opts.expiresIn, 10) : void 0;
@@ -2541,22 +3689,22 @@ authCmd.command("oauth-set <provider>").description("Save OAuth tokens manually
2541
3689
  expires_at,
2542
3690
  token_url: opts.tokenUrl || void 0
2543
3691
  });
2544
- console.log(chalk$10.hex("#06b6d4")(`\n ✔ OAuth tokens saved for provider: ${provider}`));
2545
- console.log(chalk$10.gray(" Set in hyperclaw.json: \"provider\": { \"authType\": \"oauth\", \"providerId\": \"" + provider + "\" }\n"));
3692
+ console.log(chalk$11.hex("#06b6d4")(`\n ✔ OAuth tokens saved for provider: ${provider}`));
3693
+ console.log(chalk$11.gray(" Set in hyperclaw.json: \"provider\": { \"authType\": \"oauth\", \"providerId\": \"" + provider + "\" }\n"));
2546
3694
  process.exit(0);
2547
3695
  });
2548
3696
  const workspaceCmd = program.command("workspace").description("Manage agent workspace files");
2549
3697
  workspaceCmd.command("init [dir]").description("Initialize workspace files (SOUL.md, USER.md, TOOLS.md, HEARTBEAT.md, BOOTSTRAP.md) in a directory").action(async (dir) => {
2550
- const chalk$10 = require("chalk");
2551
- const fs$6 = require("fs-extra");
2552
- const path$6 = require("path");
2553
- const os$7 = require("os");
2554
- const targetDir = dir || path$6.join(os$7.homedir(), ".hyperclaw");
3698
+ const chalk$11 = require("chalk");
3699
+ const fs$7 = require("fs-extra");
3700
+ const path$7 = require("path");
3701
+ const os$8 = require("os");
3702
+ const targetDir = dir || path$7.join(os$8.homedir(), ".hyperclaw");
2555
3703
  let cfg = {};
2556
3704
  try {
2557
- cfg = await fs$6.readJson(path$6.join(os$7.homedir(), ".hyperclaw", "hyperclaw.json"));
3705
+ cfg = await fs$7.readJson(path$7.join(os$8.homedir(), ".hyperclaw", "hyperclaw.json"));
2558
3706
  } catch {}
2559
- const { initWorkspaceFiles } = await Promise.resolve().then(() => require("./memory-BI1kPkAN.js"));
3707
+ const { initWorkspaceFiles } = await Promise.resolve().then(() => require("./memory-BlHL7JCO.js"));
2560
3708
  await initWorkspaceFiles({
2561
3709
  agentName: cfg.identity?.agentName || "Hyper",
2562
3710
  personality: cfg.identity?.personality || "helpful and concise",
@@ -2564,17 +3712,17 @@ workspaceCmd.command("init [dir]").description("Initialize workspace files (SOUL
2564
3712
  userName: cfg.identity?.userName || "User",
2565
3713
  rules: cfg.identity?.rules ?? []
2566
3714
  }, targetDir);
2567
- console.log(chalk$10.hex("#06b6d4")(`\n ✔ Workspace files initialized in ${targetDir}`));
2568
- console.log(chalk$10.gray(" Files: SOUL.md USER.md TOOLS.md HEARTBEAT.md BOOTSTRAP.md\n"));
3715
+ console.log(chalk$11.hex("#06b6d4")(`\n ✔ Workspace files initialized in ${targetDir}`));
3716
+ console.log(chalk$11.gray(" Files: SOUL.md USER.md TOOLS.md HEARTBEAT.md BOOTSTRAP.md\n"));
2569
3717
  process.exit(0);
2570
3718
  });
2571
3719
  workspaceCmd.command("show [dir]").description("Show workspace files summary").action(async (dir) => {
2572
- const chalk$10 = require("chalk");
2573
- const fs$6 = require("fs-extra");
2574
- const path$6 = require("path");
2575
- const os$7 = require("os");
2576
- const targetDir = dir || path$6.join(os$7.homedir(), ".hyperclaw");
2577
- console.log(chalk$10.bold.hex("#06b6d4")("\n 📁 WORKSPACE\n"));
3720
+ const chalk$11 = require("chalk");
3721
+ const fs$7 = require("fs-extra");
3722
+ const path$7 = require("path");
3723
+ const os$8 = require("os");
3724
+ const targetDir = dir || path$7.join(os$8.homedir(), ".hyperclaw");
3725
+ console.log(chalk$11.bold.hex("#06b6d4")("\n 📁 WORKSPACE\n"));
2578
3726
  for (const fname of [
2579
3727
  "SOUL.md",
2580
3728
  "USER.md",
@@ -2584,27 +3732,27 @@ workspaceCmd.command("show [dir]").description("Show workspace files summary").a
2584
3732
  "AGENTS.md",
2585
3733
  "MEMORY.md"
2586
3734
  ]) {
2587
- const fpath = path$6.join(targetDir, fname);
2588
- const exists = await fs$6.pathExists(fpath);
2589
- const size = exists ? (await fs$6.stat(fpath)).size : 0;
2590
- const dot = exists ? chalk$10.hex("#06b6d4")("✔") : chalk$10.gray("○");
2591
- console.log(` ${dot} ${fname.padEnd(14)} ${exists ? chalk$10.gray(`${size} bytes`) : chalk$10.gray("(missing)")}`);
3735
+ const fpath = path$7.join(targetDir, fname);
3736
+ const exists = await fs$7.pathExists(fpath);
3737
+ const size = exists ? (await fs$7.stat(fpath)).size : 0;
3738
+ const dot = exists ? chalk$11.hex("#06b6d4")("✔") : chalk$11.gray("○");
3739
+ console.log(` ${dot} ${fname.padEnd(14)} ${exists ? chalk$11.gray(`${size} bytes`) : chalk$11.gray("(missing)")}`);
2592
3740
  }
2593
3741
  console.log();
2594
3742
  process.exit(0);
2595
3743
  });
2596
3744
  const botCmd = program.command("bot").description("HyperClaw Bot — companion bot for remote gateway control");
2597
3745
  botCmd.command("status").action(async () => {
2598
- const { showBotStatus } = await Promise.resolve().then(() => require("./hyperclawbot-D9KCtc4P.js"));
3746
+ const { showBotStatus } = await Promise.resolve().then(() => require("./hyperclawbot-zvczQgKx.js"));
2599
3747
  await showBotStatus();
2600
3748
  process.exit(0);
2601
3749
  });
2602
3750
  botCmd.command("setup").description("Configure HyperClaw Bot (Telegram token, allowed users)").action(async () => {
2603
3751
  const inquirer$2 = require("inquirer");
2604
- const { saveBotConfig } = await Promise.resolve().then(() => require("./hyperclawbot-D9KCtc4P.js"));
2605
- const chalk$10 = require("chalk");
2606
- console.log(chalk$10.bold.hex("#06b6d4")("\n 🦅 HYPERCLAW BOT SETUP\n"));
2607
- console.log(chalk$10.gray(" Create a bot at t.me/BotFather, then paste the token below.\n"));
3752
+ const { saveBotConfig } = await Promise.resolve().then(() => require("./hyperclawbot-zvczQgKx.js"));
3753
+ const chalk$11 = require("chalk");
3754
+ console.log(chalk$11.bold.hex("#06b6d4")("\n 🦅 HYPERCLAW BOT SETUP\n"));
3755
+ console.log(chalk$11.gray(" Create a bot at t.me/BotFather, then paste the token below.\n"));
2608
3756
  const { platform } = await inquirer$2.prompt([{
2609
3757
  type: "list",
2610
3758
  name: "platform",
@@ -2638,20 +3786,20 @@ botCmd.command("setup").description("Configure HyperClaw Bot (Telegram token, al
2638
3786
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
2639
3787
  };
2640
3788
  await saveBotConfig(cfg);
2641
- console.log(chalk$10.hex("#06b6d4")("\n ✔ HyperClaw Bot configured"));
3789
+ console.log(chalk$11.hex("#06b6d4")("\n ✔ HyperClaw Bot configured"));
2642
3790
  if (platform === "discord") try {
2643
3791
  require.resolve("discord.js");
2644
3792
  } catch {
2645
- console.log(chalk$10.yellow(" ⚠ For Discord: run: npm install discord.js"));
3793
+ console.log(chalk$11.yellow(" ⚠ For Discord: run: npm install discord.js"));
2646
3794
  }
2647
- console.log(chalk$10.gray(" Start with: hyperclaw bot start\n"));
3795
+ console.log(chalk$11.gray(" Start with: hyperclaw bot start\n"));
2648
3796
  process.exit(0);
2649
3797
  });
2650
3798
  botCmd.command("start").description("Start HyperClaw Bot (foreground or background)").option("--background", "Run bot in background (use hyperclaw bot stop to stop)").action(async (opts) => {
2651
3799
  const { spawn } = await import("child_process");
2652
- const path$6 = await import("path");
3800
+ const path$7 = await import("path");
2653
3801
  if (opts?.background) {
2654
- const entry = process.argv[1] || path$6.join(__dirname, "run-main.js");
3802
+ const entry = process.argv[1] || path$7.join(__dirname, "run-main.js");
2655
3803
  const child = spawn(process.execPath, [
2656
3804
  entry,
2657
3805
  "bot",
@@ -2663,14 +3811,14 @@ botCmd.command("start").description("Start HyperClaw Bot (foreground or backgrou
2663
3811
  cwd: process.cwd()
2664
3812
  });
2665
3813
  child.unref();
2666
- const { writeBotPid } = await Promise.resolve().then(() => require("./hyperclawbot-D9KCtc4P.js"));
3814
+ const { writeBotPid } = await Promise.resolve().then(() => require("./hyperclawbot-zvczQgKx.js"));
2667
3815
  await writeBotPid(child.pid);
2668
3816
  console.log(require("chalk").green(`\n ✔ HyperClaw Bot started in background (PID ${child.pid})`));
2669
3817
  console.log(require("chalk").gray(" Stop with: hyperclaw bot stop\n"));
2670
3818
  process.exit(0);
2671
3819
  return;
2672
3820
  }
2673
- const { loadBotConfig, TelegramHyperClawBot, DiscordHyperClawBot } = await Promise.resolve().then(() => require("./hyperclawbot-D9KCtc4P.js"));
3821
+ const { loadBotConfig, TelegramHyperClawBot, DiscordHyperClawBot } = await Promise.resolve().then(() => require("./hyperclawbot-zvczQgKx.js"));
2674
3822
  const cfg = await loadBotConfig();
2675
3823
  if (!cfg) {
2676
3824
  console.log(require("chalk").red("\n ✖ HyperClaw Bot not configured. Run: hyperclaw bot setup\n"));
@@ -2696,42 +3844,42 @@ botCmd.command("start").description("Start HyperClaw Bot (foreground or backgrou
2696
3844
  }
2697
3845
  });
2698
3846
  botCmd.command("stop").description("Stop HyperClaw Bot (when running in background)").action(async () => {
2699
- const chalk$10 = require("chalk");
2700
- const { stopBotProcess } = await Promise.resolve().then(() => require("./hyperclawbot-D9KCtc4P.js"));
3847
+ const chalk$11 = require("chalk");
3848
+ const { stopBotProcess } = await Promise.resolve().then(() => require("./hyperclawbot-zvczQgKx.js"));
2701
3849
  const stopped = await stopBotProcess();
2702
- if (stopped) console.log(chalk$10.green("\n ✔ HyperClaw Bot stopped\n"));
2703
- else console.log(chalk$10.gray("\n Bot not running in background (no PID file). Use Ctrl+C to stop foreground bot.\n"));
3850
+ if (stopped) console.log(chalk$11.green("\n ✔ HyperClaw Bot stopped\n"));
3851
+ else console.log(chalk$11.gray("\n Bot not running in background (no PID file). Use Ctrl+C to stop foreground bot.\n"));
2704
3852
  process.exit(stopped ? 0 : 0);
2705
3853
  });
2706
3854
  memCmd.command("search <query>").description("Search MEMORY.md").action(async (query) => {
2707
- const { searchMemory } = await Promise.resolve().then(() => require("./src-BEVLgaF1.js"));
3855
+ const { searchMemory } = await Promise.resolve().then(() => require("./src-BxPHKO5x.js"));
2708
3856
  await searchMemory(query);
2709
3857
  process.exit(0);
2710
3858
  });
2711
3859
  memCmd.command("auto-show").description("Show auto-extracted memories from MEMORY.md").action(async () => {
2712
- const { showMemory } = await Promise.resolve().then(() => require("./src-BEVLgaF1.js"));
3860
+ const { showMemory } = await Promise.resolve().then(() => require("./src-BxPHKO5x.js"));
2713
3861
  await showMemory();
2714
3862
  process.exit(0);
2715
3863
  });
2716
3864
  memCmd.command("clear").description("Clear all auto-extracted memories").action(async () => {
2717
- const { clearMemory } = await Promise.resolve().then(() => require("./src-BEVLgaF1.js"));
3865
+ const { clearMemory } = await Promise.resolve().then(() => require("./src-BxPHKO5x.js"));
2718
3866
  await clearMemory();
2719
3867
  process.exit(0);
2720
3868
  });
2721
3869
  memCmd.command("save <text>").description("Manually save a fact to MEMORY.md").action(async (text) => {
2722
- const { saveMemoryDirect } = await Promise.resolve().then(() => require("./src-BEVLgaF1.js"));
3870
+ const { saveMemoryDirect } = await Promise.resolve().then(() => require("./src-BxPHKO5x.js"));
2723
3871
  await saveMemoryDirect(text);
2724
3872
  console.log(chalk.default.hex("#06b6d4")(` ✅ Saved: ${text}\n`));
2725
3873
  process.exit(0);
2726
3874
  });
2727
3875
  const pcCmd = program.command("pc").description("PC access — give the AI access to your computer");
2728
3876
  pcCmd.command("status").description("Show PC access status and config").action(async () => {
2729
- const { showPCAccessStatus } = await Promise.resolve().then(() => require("./src-BEVLgaF1.js"));
3877
+ const { showPCAccessStatus } = await Promise.resolve().then(() => require("./src-BxPHKO5x.js"));
2730
3878
  await showPCAccessStatus();
2731
3879
  process.exit(0);
2732
3880
  });
2733
3881
  pcCmd.command("enable").description("Enable PC access for the AI").option("--level <level>", "Access level: read-only | sandboxed | full", "full").option("--paths <paths>", "Comma-separated allowed paths (sandboxed mode)").action(async (opts) => {
2734
- const { savePCAccessConfig } = await Promise.resolve().then(() => require("./src-BEVLgaF1.js"));
3882
+ const { savePCAccessConfig } = await Promise.resolve().then(() => require("./src-BxPHKO5x.js"));
2735
3883
  const level = opts.level;
2736
3884
  const allowed = [
2737
3885
  "read-only",
@@ -2758,7 +3906,7 @@ pcCmd.command("enable").description("Enable PC access for the AI").option("--lev
2758
3906
  process.exit(0);
2759
3907
  });
2760
3908
  pcCmd.command("disable").description("Disable PC access").action(async () => {
2761
- const { savePCAccessConfig } = await Promise.resolve().then(() => require("./src-BEVLgaF1.js"));
3909
+ const { savePCAccessConfig } = await Promise.resolve().then(() => require("./src-BxPHKO5x.js"));
2762
3910
  await savePCAccessConfig({ enabled: false });
2763
3911
  console.log(chalk.default.hex("#06b6d4")("\n ✅ PC access disabled\n"));
2764
3912
  process.exit(0);
@@ -2782,7 +3930,7 @@ pcCmd.command("log").description("Show PC access audit log").option("-n, --lines
2782
3930
  process.exit(0);
2783
3931
  });
2784
3932
  pcCmd.command("run <command>").description("Run a shell command via PC access (must be enabled)").action(async (command) => {
2785
- const { loadPCAccessConfig, getPCAccessTools } = await Promise.resolve().then(() => require("./src-BEVLgaF1.js"));
3933
+ const { loadPCAccessConfig, getPCAccessTools } = await Promise.resolve().then(() => require("./src-BxPHKO5x.js"));
2786
3934
  const cfg = await loadPCAccessConfig();
2787
3935
  if (!cfg.enabled) {
2788
3936
  console.log(chalk.default.red("\n ✖ PC access disabled. Run: hyperclaw pc enable\n"));
@@ -2798,9 +3946,9 @@ pcCmd.command("run <command>").description("Run a shell command via PC access (m
2798
3946
  function checkForUpdate() {
2799
3947
  const { execFile: execFile$1 } = require("child_process");
2800
3948
  const { readFileSync } = require("fs");
2801
- const path$6 = require("path");
3949
+ const path$7 = require("path");
2802
3950
  try {
2803
- const pkgPath = path$6.resolve(__dirname, "../../package.json");
3951
+ const pkgPath = path$7.resolve(__dirname, "../../package.json");
2804
3952
  const current = JSON.parse(readFileSync(pkgPath, "utf8")).version;
2805
3953
  execFile$1("npm", [
2806
3954
  "view",
@@ -2822,15 +3970,31 @@ function checkForUpdate() {
2822
3970
  });
2823
3971
  } catch {}
2824
3972
  }
3973
+ program.command("setup").description("Setup wizard — alias for `hyperclaw onboard`").option("--install-daemon", "Auto-install system daemon").option("--reset", "Reset config before running wizard").option("--non-interactive", "Non-interactive mode").option("--json", "Output result as JSON (use with --non-interactive)").option("--anthropic-api-key <key>", "Anthropic API key (non-interactive)").option("--openai-api-key <key>", "OpenAI API key (non-interactive)").option("--gateway-port <port>", "Gateway port (non-interactive)", "18789").option("--gateway-bind <bind>", "Gateway bind: loopback | all", "loopback").action(async (opts) => {
3974
+ await new require_onboard.Banner().showNeonBanner(false);
3975
+ const wizardOpts = {
3976
+ wizard: true,
3977
+ installDaemon: opts.installDaemon ?? false,
3978
+ reset: opts.reset ?? false,
3979
+ nonInteractive: opts.nonInteractive ?? false,
3980
+ jsonOutput: opts.json ?? false,
3981
+ gatewayPort: opts.gatewayPort ? parseInt(opts.gatewayPort) : void 0,
3982
+ gatewayBind: opts.gatewayBind ?? "loopback",
3983
+ anthropicApiKey: opts.anthropicApiKey,
3984
+ openaiApiKey: opts.openaiApiKey
3985
+ };
3986
+ await new require_onboard.HyperClawWizard().run(wizardOpts);
3987
+ process.exit(0);
3988
+ });
2825
3989
  checkForUpdate();
2826
3990
  if (process.argv.length === 2) (async () => {
2827
- const { ConfigManager: ConfigManager$1 } = await Promise.resolve().then(() => require("./manager-DLmZI-9R.js"));
3991
+ const { ConfigManager: ConfigManager$1 } = await Promise.resolve().then(() => require("./manager-CrVDn6eN.js"));
2828
3992
  const cfg = await new ConfigManager$1().load().catch(() => null);
2829
3993
  if (cfg?.provider?.apiKey || cfg?.provider?.providerId) {
2830
3994
  await new require_onboard.Banner().showNeonBanner(false);
2831
- const { getTheme } = await Promise.resolve().then(() => require("./theme-Iefa3L63.js"));
3995
+ const { getTheme } = await Promise.resolve().then(() => require("./theme-cx0fkgWC.js"));
2832
3996
  const t = getTheme(false);
2833
- const chalk$10 = require("chalk");
3997
+ const chalk$11 = require("chalk");
2834
3998
  console.log(t.bold(" Quick actions:\n"));
2835
3999
  console.log(` ${t.c("hyperclaw onboard")} — re-run setup wizard`);
2836
4000
  console.log(` ${t.c("hyperclaw onboard --install-daemon")} — wizard + daemon (full PC access)`);
@@ -2842,7 +4006,7 @@ if (process.argv.length === 2) (async () => {
2842
4006
  console.log(` ${t.c("hyperclaw --help")} — all commands\n`);
2843
4007
  } else {
2844
4008
  await new require_onboard.Banner().showNeonBanner(false);
2845
- const { HyperClawWizard: HyperClawWizard$1 } = await Promise.resolve().then(() => require("./onboard-aTwlQs-4.js"));
4009
+ const { HyperClawWizard: HyperClawWizard$1 } = await Promise.resolve().then(() => require("./onboard-0WoDxbv_.js"));
2846
4010
  await new HyperClawWizard$1().run({ wizard: true });
2847
4011
  }
2848
4012
  process.exit(0);