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