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.
- package/README.md +54 -3
- package/dist/a2ui-protocol-CfBI44-Q.js +75 -0
- package/dist/agents-routing-ChHiZp36.js +327 -0
- package/dist/agents-routing-ChqZ6l2S.js +4 -0
- package/dist/api-keys-guide-BCcOl0Q7.js +149 -0
- package/dist/audit-BaIiyWFu.js +441 -0
- package/dist/bounty-tools-DWudyZie.js +211 -0
- package/dist/browser-tools-BsTeGMnX.js +5 -0
- package/dist/browser-tools-D8_rLe2p.js +179 -0
- package/dist/claw-tasks-CgTsiNE8.js +80 -0
- package/dist/connector-5N0-X_xs.js +194 -0
- package/dist/connector-B3v0qcXg.js +425 -0
- package/dist/connector-B8R3iBY1.js +280 -0
- package/dist/connector-BAM-08NN.js +189 -0
- package/dist/connector-BC8FIVu4.js +181 -0
- package/dist/connector-BDmwwaVc.js +213 -0
- package/dist/connector-BGjbBy69.js +225 -0
- package/dist/connector-BO2SRzfG.js +218 -0
- package/dist/connector-BfXky0L3.js +167 -0
- package/dist/connector-BiiSJpx3.js +192 -0
- package/dist/connector-BnDmIhIu.js +85 -0
- package/dist/connector-C1HSoUyk.js +189 -0
- package/dist/connector-CKQHZOXg.js +568 -0
- package/dist/connector-CRl-iidy.js +239 -0
- package/dist/connector-Ci9glMD-.js +340 -0
- package/dist/connector-CjtZIEDj.js +181 -0
- package/dist/connector-Ck6JtOsX.js +531 -0
- package/dist/connector-D8Kelee0.js +286 -0
- package/dist/connector-DAnRJ0oP.js +162 -0
- package/dist/connector-DXTp5PE8.js +508 -0
- package/dist/connector-Dih6dUPP.js +173 -0
- package/dist/connector-DqTH_tPX.js +182 -0
- package/dist/connector-DrnEiiyP.js +419 -0
- package/dist/connector-DtR5GGTX.js +167 -0
- package/dist/connector-Tky_qS_K.js +350 -0
- package/dist/connector-ZSc3oTTy.js +305 -0
- package/dist/connector-sW5yhU1m.js +498 -0
- package/dist/connector-u3ICd3Ic.js +552 -0
- package/dist/cost-tracker-DD9wtWsr.js +103 -0
- package/dist/credentials-store-C6ir0Dae.js +4 -0
- package/dist/credentials-store-H13LqOwJ.js +77 -0
- package/dist/cron-tasks-Bli7Kzd2.js +82 -0
- package/dist/daemon-Bg4GtCmc.js +318 -0
- package/dist/daemon-DhmwY8k4.js +5 -0
- package/dist/delivery-BmIYy9VQ.js +4 -0
- package/dist/delivery-pWUPBp1F.js +95 -0
- package/dist/destructive-gate-D6vWOdEl.js +101 -0
- package/dist/developer-keys-CPWT7Q6S.js +8 -0
- package/dist/developer-keys-DrrcUqFa.js +127 -0
- package/dist/doctor-BvCe8BBk.js +230 -0
- package/dist/doctor-CxyPLYsJ.js +6 -0
- package/dist/engine-CEDSqXfw.js +256 -0
- package/dist/engine-Da4JMNpI.js +7 -0
- package/dist/env-resolve-CiXbWYwe.js +10 -0
- package/dist/env-resolve-CmGWhWXJ.js +115 -0
- package/dist/extraction-tools-HOZstZ0y.js +91 -0
- package/dist/extraction-tools-m4lmAv7l.js +5 -0
- package/dist/form_data-Cz040rio.js +8657 -0
- package/dist/gmail-watch-setup-Du7DVV7S.js +40 -0
- package/dist/health-B-asI__D.js +6 -0
- package/dist/health-Ds2YlpTB.js +152 -0
- package/dist/heartbeat-engine-BYT5ayQH.js +83 -0
- package/dist/hub-D0XwdjM-.js +515 -0
- package/dist/hub-LiD5Iztb.js +6 -0
- package/dist/hyperclawbot-zvczQgKx.js +505 -0
- package/dist/inference-BKVkBREb.js +6 -0
- package/dist/inference-DCXH4Q3x.js +922 -0
- package/dist/knowledge-graph-iBG76fvm.js +131 -0
- package/dist/loader-CC45xGpC.js +4 -0
- package/dist/loader-CnEdOyjT.js +400 -0
- package/dist/logger-ybOp7VOC.js +83 -0
- package/dist/manager-03ipO9R0.js +105 -0
- package/dist/manager-BpDfbDjg.js +117 -0
- package/dist/manager-Bxl0sqlh.js +4 -0
- package/dist/manager-CrVDn6eN.js +6 -0
- package/dist/manager-FCgF1plu.js +218 -0
- package/dist/manager-rgCsaWT1.js +40 -0
- package/dist/mcp-CfoSU4Uz.js +139 -0
- package/dist/mcp-loader-DkRBsLpk.js +94 -0
- package/dist/memory-BlHL7JCO.js +4 -0
- package/dist/memory-DsS_eFvJ.js +270 -0
- package/dist/memory-auto-BkvtSFUw.js +5 -0
- package/dist/memory-auto-Bnz_-1wP.js +306 -0
- package/dist/memory-integration-cSYkZyEo.js +91 -0
- package/dist/moltbook-BtLDZTfM.js +81 -0
- package/dist/node-Dw2Gi-cP.js +222 -0
- package/dist/nodes-registry-B8dmrlLv.js +52 -0
- package/dist/oauth-flow-DQPvMHRH.js +150 -0
- package/dist/oauth-provider-Uo4Nib_c.js +110 -0
- package/dist/observability-BV-Yx0V9.js +89 -0
- package/dist/onboard-0WoDxbv_.js +10 -0
- package/dist/onboard-BXNXCQp4.js +4070 -0
- package/dist/orchestrator-DmnEvMaL.js +189 -0
- package/dist/orchestrator-RI3bpqqc.js +6 -0
- package/dist/pairing-6iM27aD8.js +196 -0
- package/dist/pairing-dGoiGepK.js +4 -0
- package/dist/pc-access-CgCsYrpt.js +8 -0
- package/dist/pc-access-_iH2aorG.js +819 -0
- package/dist/pending-approval-CUXjysAo.js +22 -0
- package/dist/reminders-store-Drjed_-h.js +58 -0
- package/dist/renderer-BVQrd0_g.js +225 -0
- package/dist/rules-BE4GV6cV.js +103 -0
- package/dist/run-main.js +1607 -443
- package/dist/runner-DatMMYYE.js +1271 -0
- package/dist/sdk/index.js +2 -2
- package/dist/sdk/index.mjs +2 -2
- package/dist/security-BqNyT4ID.js +4 -0
- package/dist/security-tpgqPWWH.js +73 -0
- package/dist/server-D4wVHiX9.js +4 -0
- package/dist/server-Dh3JlBFB.js +1255 -0
- package/dist/session-store-BUiPz0Vv.js +5 -0
- package/dist/session-store-is4B6qmD.js +113 -0
- package/dist/sessions-tools-CbUTFe4i.js +5 -0
- package/dist/sessions-tools-CeqD7iil.js +95 -0
- package/dist/skill-loader-BaNLVmJy.js +7 -0
- package/dist/skill-loader-HgpF6Vqs.js +159 -0
- package/dist/skill-runtime-CJN24QPW.js +102 -0
- package/dist/skill-runtime-w1ig_lcw.js +5 -0
- package/dist/src-BxPHKO5x.js +63 -0
- package/dist/src-DIc-L2IG.js +20 -0
- package/dist/src-g_rNx5rh.js +458 -0
- package/dist/sub-agent-tools-CHQoHz9c.js +39 -0
- package/dist/theme-DcxwcUgZ.js +180 -0
- package/dist/theme-cx0fkgWC.js +8 -0
- package/dist/tool-policy-CNT-mF2Z.js +189 -0
- package/dist/tts-elevenlabs-BRosZv-f.js +61 -0
- package/dist/update-check-C2Dz85wJ.js +81 -0
- package/dist/vision-BMmiIKy7.js +121 -0
- package/dist/vision-tools-DVuYc17I.js +51 -0
- package/dist/vision-tools-U3YC4L-g.js +5 -0
- package/dist/voice-transcription-B555DbWR.js +138 -0
- package/dist/website-watch-tools-DFMrJU-R.js +139 -0
- package/dist/website-watch-tools-Du3W5sN7.js +5 -0
- 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
|
+
});
|