hyperclaw 4.0.0 → 4.0.2
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 +53 -18
- package/dist/a2ui-protocol-CT_jDEU9.js +75 -0
- package/dist/agents-routing-683Q2JGp.js +129 -0
- package/dist/agents-routing-BpZBswBH.js +4 -0
- package/dist/api-keys-guide-Bzig1R5W.js +149 -0
- package/dist/api-keys-guide-Dq5Obbp4.js +149 -0
- package/dist/audit-BYxPlnTQ.js +248 -0
- package/dist/bounty-tools-C6LyzxM-.js +211 -0
- package/dist/browser-tools-CQBSbIuO.js +5 -0
- package/dist/browser-tools-YQmwRLLM.js +179 -0
- package/dist/claw-tasks-BRLUvFRD.js +80 -0
- package/dist/connector-3HnyH8fn.js +167 -0
- package/dist/connector-6PMZo5Ky.js +189 -0
- package/dist/connector-B6eoF3DD.js +181 -0
- package/dist/connector-B9tLG8UZ.js +196 -0
- package/dist/connector-BOlqjXWP.js +182 -0
- package/dist/connector-BP8zsbP8.js +189 -0
- package/dist/connector-BPoSevxp.js +286 -0
- package/dist/connector-BRHj773i.js +163 -0
- package/dist/connector-BToxU-jV.js +267 -0
- package/dist/connector-BliDVsJQ.js +239 -0
- package/dist/connector-Bv6s9oP7.js +88 -0
- package/dist/connector-By5wWGTR.js +343 -0
- package/dist/connector-C1BaFFgN.js +213 -0
- package/dist/connector-CRRWY5Wv.js +167 -0
- package/dist/connector-CXPQVGyI.js +85 -0
- package/dist/connector-Cdk1CXKi.js +194 -0
- package/dist/connector-CwlgFgjx.js +181 -0
- package/dist/connector-DFchk6l7.js +178 -0
- package/dist/connector-DKw7tRAy.js +192 -0
- package/dist/connector-DRv1ahC_.js +343 -0
- package/dist/connector-DU63KW94.js +165 -0
- package/dist/connector-Dbvb1Cj9.js +280 -0
- package/dist/connector-DcZdQcgR.js +173 -0
- package/dist/connector-DxKL8VvZ.js +182 -0
- package/dist/connector-T_YdZtzv.js +162 -0
- package/dist/connector-i4gOS9xL.js +154 -0
- package/dist/connector-rHXE1ZD2.js +167 -0
- package/dist/connector-wdUXChwa.js +172 -0
- package/dist/cost-tracker-pVE15Yq4.js +103 -0
- package/dist/credentials-store-BvnMPJwi.js +4 -0
- package/dist/credentials-store-sb-TRLwR.js +77 -0
- package/dist/cron-tasks-BvDFNyiE.js +82 -0
- package/dist/delivery-B-SJqXLn.js +95 -0
- package/dist/delivery-D5Z98EVq.js +95 -0
- package/dist/delivery-DCOXhXEO.js +5 -0
- package/dist/delivery-VgFeuu2J.js +5 -0
- package/dist/destructive-gate-m-dWqUFg.js +101 -0
- package/dist/developer-keys-JaJK3T27.js +127 -0
- package/dist/developer-keys-kyqmtWK3.js +8 -0
- package/dist/doctor-3oi89QIc.js +175 -0
- package/dist/doctor-Cf1XSfp9.js +4 -0
- package/dist/engine-B4eMiTgl.js +7 -0
- package/dist/engine-B8M7dYul.js +7 -0
- package/dist/engine-BhT-1M9W.js +256 -0
- package/dist/engine-D49jnSd_.js +256 -0
- package/dist/env-resolve-DWOQ45jG.js +9 -0
- package/dist/env-resolve-szSWl0UF.js +94 -0
- package/dist/extraction-tools-D3qDFBJ1.js +91 -0
- package/dist/extraction-tools-DLr_AEwq.js +5 -0
- package/dist/form_data-B_hIUrxU.js +8657 -0
- package/dist/gmail-watch-setup-Czt8rXaX.js +40 -0
- package/dist/heartbeat-engine-CRqfPcFM.js +83 -0
- package/dist/hub-DTsqe5Bt.js +6 -0
- package/dist/hub-FrPTA33j.js +515 -0
- package/dist/hyperclawbot-D9KCtc4P.js +480 -0
- package/dist/hyperclawbot-DfMGowZC.js +480 -0
- package/dist/hyperclawbot-Dw27pJo4.js +480 -0
- package/dist/inference-CTWJeX9Q.js +922 -0
- package/dist/inference-ix607p7k.js +6 -0
- package/dist/knowledge-graph-DqA-Fztl.js +131 -0
- package/dist/loader-CISCqBto.js +400 -0
- package/dist/loader-CYMQ8VOS.js +4 -0
- package/dist/logger-8tEtAd3y.js +83 -0
- package/dist/manager-CPjeRe-6.js +4 -0
- package/dist/manager-Cwzj7w5R.js +105 -0
- package/dist/manager-DLmZI-9R.js +6 -0
- package/dist/manager-DSGhn5i3.js +117 -0
- package/dist/manager-DgyF52mg.js +218 -0
- package/dist/manager-Dm8nrMFx.js +40 -0
- package/dist/mcp-B_9Ber63.js +139 -0
- package/dist/mcp-loader-DSM5UiFG.js +94 -0
- package/dist/mcp-loader-j5ZLLw5O.js +94 -0
- package/dist/memory-BI1kPkAN.js +4 -0
- package/dist/memory-BVFGkxxK.js +270 -0
- package/dist/memory-auto-Bc7euou4.js +306 -0
- package/dist/memory-auto-DPfbkMVt.js +5 -0
- package/dist/memory-integration-DZExqWr4.js +91 -0
- package/dist/moltbook-B6ZeGN5_.js +81 -0
- package/dist/node-pwL6O_KX.js +222 -0
- package/dist/nodes-registry-CsPm_-CJ.js +52 -0
- package/dist/oauth-flow-CpWlgvNB.js +150 -0
- package/dist/oauth-provider-BZb6qOw5.js +110 -0
- package/dist/observability-B43YvNQV.js +89 -0
- package/dist/onboard-3q20ZyHj.js +9 -0
- package/dist/onboard-Bd_wsYdi.js +4086 -0
- package/dist/onboard-CAN7x3me.js +3026 -0
- package/dist/onboard-DnegOHMh.js +3026 -0
- package/dist/onboard-RYtDlYBw.js +9 -0
- package/dist/onboard-aTwlQs-4.js +9 -0
- package/dist/orchestrator-BSp2M5EU.js +189 -0
- package/dist/orchestrator-C7ko5tWa.js +6 -0
- package/dist/orchestrator-DfPkIx2Z.js +6 -0
- package/dist/orchestrator-NJQsmiBE.js +189 -0
- package/dist/pairing-DU0_J28n.js +87 -0
- package/dist/pairing-DWllbSbO.js +4 -0
- package/dist/pc-access-Ly-uA8mn.js +8 -0
- package/dist/pc-access-NxBvTrRj.js +819 -0
- package/dist/pending-approval-DIHvwwWS.js +22 -0
- package/dist/puppeteer-2o3QOwAy.js +2 -2
- package/dist/puppeteer-BYTMp3BI.js +2 -2
- package/dist/puppeteer-DQ45qwWk.js +2 -2
- package/dist/reminders-store-D79qdfN0.js +58 -0
- package/dist/renderer-pqlDRKbH.js +225 -0
- package/dist/rules-BooT_qFP.js +103 -0
- package/dist/run-main.js +366 -1109
- package/dist/runner-Bu--_RXw.js +810 -0
- package/dist/runner-D1rjuMTJ.js +810 -0
- package/dist/sdk/index.js +2 -2
- package/dist/sdk/index.mjs +2 -2
- package/dist/security-C-5URby1.js +73 -0
- package/dist/security-_xve79aq.js +4 -0
- package/dist/server-0kgyELx4.js +1047 -0
- package/dist/server-BIuTobTC.js +4 -0
- package/dist/server-BRlCEjyT.js +1047 -0
- package/dist/server-CCI1hv45.js +1047 -0
- package/dist/server-DU9POoWc.js +4 -0
- package/dist/server-RBqwE_GN.js +4 -0
- package/dist/session-store-CujxByI6.js +113 -0
- package/dist/session-store-qpJUg2M1.js +5 -0
- package/dist/sessions-tools-CB2qbwIk.js +5 -0
- package/dist/sessions-tools-DHMaTZIs.js +95 -0
- package/dist/skill-loader-BkceKkIg.js +7 -0
- package/dist/skill-loader-DhgIwK4J.js +159 -0
- package/dist/skill-runtime--LqxWrp5.js +102 -0
- package/dist/skill-runtime-C5l0Tgt-.js +5 -0
- package/dist/skill-runtime-DsXK_HYG.js +102 -0
- package/dist/skill-runtime-IVTiqrMR.js +5 -0
- package/dist/src-BEVLgaF1.js +63 -0
- package/dist/src-Bgu_OxTQ.js +458 -0
- package/dist/src-Bq-oKt7Z.js +458 -0
- package/dist/src-DWCUhnD4.js +20 -0
- package/dist/src-cfRTjFef.js +63 -0
- package/dist/sub-agent-tools-BD9DF8_g.js +39 -0
- package/dist/sub-agent-tools-V7b3T9_s.js +39 -0
- package/dist/tool-policy-DNvNRnve.js +189 -0
- package/dist/tts-elevenlabs-BUOGKL-k.js +61 -0
- package/dist/update-check-BD4qH7Am.js +81 -0
- package/dist/vision-DRq-f-Dj.js +121 -0
- package/dist/vision-tools-CFZEpQKm.js +5 -0
- package/dist/vision-tools-CQnBI9aa.js +51 -0
- package/dist/voice-transcription-CbQBToY0.js +138 -0
- package/dist/voice-transcription-CgWq54hn.js +138 -0
- package/dist/website-watch-tools-Bk_TnwuE.js +5 -0
- package/dist/website-watch-tools-DraMPxdl.js +139 -0
- package/package.json +1 -1
|
@@ -0,0 +1,3026 @@
|
|
|
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-BzDlV2CS.js');
|
|
5
|
+
const require_server = require('./server-CCI1hv45.js');
|
|
6
|
+
const require_theme = require('./theme-LUTKWUWd.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/infra/daemon.ts
|
|
195
|
+
const execAsync$1 = (0, util.promisify)(child_process.exec);
|
|
196
|
+
const HC_DIR$2 = path.default.join(os.default.homedir(), ".hyperclaw");
|
|
197
|
+
const PID_FILE = path.default.join(HC_DIR$2, "gateway.pid");
|
|
198
|
+
var DaemonManager = class {
|
|
199
|
+
async install() {
|
|
200
|
+
const platform = os.default.platform();
|
|
201
|
+
if (platform === "darwin") await this.installMacOS();
|
|
202
|
+
else if (platform === "linux") await this.installLinux();
|
|
203
|
+
else if (platform === "win32") await this.installWindows();
|
|
204
|
+
}
|
|
205
|
+
async start() {
|
|
206
|
+
const s = (0, ora.default)("🩸 Starting HyperClaw daemon...").start();
|
|
207
|
+
try {
|
|
208
|
+
const server = await require_server.startGateway({ daemonMode: true });
|
|
209
|
+
await fs_extra.default.ensureDir(HC_DIR$2);
|
|
210
|
+
await fs_extra.default.writeFile(PID_FILE, String(process.pid), "utf8");
|
|
211
|
+
s.succeed(`🩸 Daemon started — ws://127.0.0.1:${server.getStatus().port}`);
|
|
212
|
+
const shutdown = async () => {
|
|
213
|
+
const active = require_server.getActiveServer();
|
|
214
|
+
if (active) await active.stop();
|
|
215
|
+
try {
|
|
216
|
+
await fs_extra.default.remove(PID_FILE);
|
|
217
|
+
} catch {}
|
|
218
|
+
process.exit(0);
|
|
219
|
+
};
|
|
220
|
+
process.on("SIGINT", shutdown);
|
|
221
|
+
process.on("SIGTERM", shutdown);
|
|
222
|
+
} catch (e) {
|
|
223
|
+
s.fail(`Failed to start: ${e.message}`);
|
|
224
|
+
throw e;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
async stop() {
|
|
228
|
+
const pidPath = PID_FILE;
|
|
229
|
+
if (!await fs_extra.default.pathExists(pidPath)) {
|
|
230
|
+
console.log(chalk.default.gray(" Gateway not running (no PID file)"));
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
const s = (0, ora.default)("🩸 Stopping daemon...").start();
|
|
234
|
+
try {
|
|
235
|
+
const pid = parseInt(await fs_extra.default.readFile(pidPath, "utf8"), 10);
|
|
236
|
+
process.kill(pid, "SIGTERM");
|
|
237
|
+
await fs_extra.default.remove(pidPath);
|
|
238
|
+
s.succeed("🩸 Daemon stopped");
|
|
239
|
+
} catch (e) {
|
|
240
|
+
await fs_extra.default.remove(pidPath).catch(() => {});
|
|
241
|
+
if (e.code === "ESRCH") s.succeed("Gateway was not running");
|
|
242
|
+
else s.fail(`Stop failed: ${e.message}`);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
async restart() {
|
|
246
|
+
await this.stop();
|
|
247
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
248
|
+
await this.start();
|
|
249
|
+
}
|
|
250
|
+
async status() {
|
|
251
|
+
const pidPath = PID_FILE;
|
|
252
|
+
let running = false;
|
|
253
|
+
let pid = null;
|
|
254
|
+
let port = 18789;
|
|
255
|
+
if (await fs_extra.default.pathExists(pidPath)) try {
|
|
256
|
+
pid = parseInt(await fs_extra.default.readFile(pidPath, "utf8"), 10);
|
|
257
|
+
process.kill(pid, 0);
|
|
258
|
+
running = true;
|
|
259
|
+
} catch {
|
|
260
|
+
await fs_extra.default.remove(pidPath).catch(() => {});
|
|
261
|
+
}
|
|
262
|
+
try {
|
|
263
|
+
const cfg = await fs_extra.default.readJson(path.default.join(HC_DIR$2, "hyperclaw.json"));
|
|
264
|
+
if (cfg?.gateway?.port) port = cfg.gateway.port;
|
|
265
|
+
} catch {}
|
|
266
|
+
console.log(chalk.default.red("\n 🩸 HyperClaw Daemon Status"));
|
|
267
|
+
console.log(running ? chalk.default.red(" 🩸 ● Running") : chalk.default.red(" ○ Stopped"));
|
|
268
|
+
if (pid) console.log(chalk.default.gray(` PID: ${pid}`));
|
|
269
|
+
console.log(chalk.default.gray(` Port: ${port}`));
|
|
270
|
+
console.log(chalk.default.gray(" Runtime: node"));
|
|
271
|
+
console.log();
|
|
272
|
+
}
|
|
273
|
+
async logs() {
|
|
274
|
+
const t = () => chalk.default.gray(`[${(/* @__PURE__ */ new Date()).toLocaleTimeString()}]`);
|
|
275
|
+
console.log(`\n${t()} ${chalk.default.red("🩸 HyperClaw daemon started on port 18789")}`);
|
|
276
|
+
console.log(`${t()} ${chalk.default.red("🩸 PC access: full (daemon mode)")}`);
|
|
277
|
+
console.log(`${t()} ${chalk.default.red("Provider: openrouter/auto")}`);
|
|
278
|
+
console.log(`${t()} ${chalk.default.red("Channels loaded: telegram, discord, web, cli")}`);
|
|
279
|
+
console.log(`${t()} ${chalk.default.red("Skills loaded: translator, reminders")}`);
|
|
280
|
+
console.log(`${t()} ${chalk.default.red("AGENTS.md loaded — 5 global rules active")}`);
|
|
281
|
+
console.log(`${t()} ${chalk.default.red("Voice engine: standby")}`);
|
|
282
|
+
console.log(`${t()} ${chalk.default.red("🩸 Daemon ready — ws://127.0.0.1:1515")}`);
|
|
283
|
+
console.log();
|
|
284
|
+
}
|
|
285
|
+
async handle(action) {
|
|
286
|
+
const actions = {
|
|
287
|
+
start: () => this.start(),
|
|
288
|
+
stop: () => this.stop(),
|
|
289
|
+
restart: () => this.restart(),
|
|
290
|
+
status: () => this.status(),
|
|
291
|
+
logs: () => this.logs()
|
|
292
|
+
};
|
|
293
|
+
const fn = actions[action];
|
|
294
|
+
if (fn) await fn();
|
|
295
|
+
else console.log(chalk.default.red(`Unknown action: ${action}`) + chalk.default.gray("\n 🩸 Use: start, stop, restart, status, logs"));
|
|
296
|
+
}
|
|
297
|
+
async installLinux() {
|
|
298
|
+
const home = os.default.homedir();
|
|
299
|
+
const pathEnv = process.env.PATH || "/usr/local/bin:/usr/bin:/bin";
|
|
300
|
+
const hcPath = (await execAsync$1("which hyperclaw").catch(() => ({ stdout: "/usr/local/bin/hyperclaw" }))).stdout.trim();
|
|
301
|
+
const unit = `[Unit]
|
|
302
|
+
Description=HyperClaw AI Gateway
|
|
303
|
+
After=network.target
|
|
304
|
+
# For full desktop access (screenshots, xdg-open): run in graphical session
|
|
305
|
+
# systemctl --user runs in user context with session when logged in
|
|
306
|
+
|
|
307
|
+
[Service]
|
|
308
|
+
Type=simple
|
|
309
|
+
ExecStart=${hcPath} daemon start
|
|
310
|
+
Restart=always
|
|
311
|
+
RestartSec=3
|
|
312
|
+
Environment=NODE_ENV=production
|
|
313
|
+
Environment=HOME=${home}
|
|
314
|
+
Environment=PATH=${pathEnv}
|
|
315
|
+
# Load .env from HyperClaw home if exists
|
|
316
|
+
EnvironmentFile=-${path.default.join(home, ".hyperclaw", ".env")}
|
|
317
|
+
|
|
318
|
+
[Install]
|
|
319
|
+
WantedBy=default.target
|
|
320
|
+
`;
|
|
321
|
+
const userSystemdDir = path.default.join(os.default.homedir(), ".config", "systemd", "user");
|
|
322
|
+
const unitFile = path.default.join(userSystemdDir, "hyperclaw.service");
|
|
323
|
+
try {
|
|
324
|
+
await fs_extra.default.ensureDir(userSystemdDir);
|
|
325
|
+
await fs_extra.default.writeFile(unitFile, unit);
|
|
326
|
+
const username = os.default.userInfo().username;
|
|
327
|
+
try {
|
|
328
|
+
await execAsync$1(`loginctl enable-linger ${username}`);
|
|
329
|
+
} catch {}
|
|
330
|
+
try {
|
|
331
|
+
await execAsync$1("systemctl --user daemon-reload");
|
|
332
|
+
} catch {}
|
|
333
|
+
try {
|
|
334
|
+
await execAsync$1("systemctl --user enable hyperclaw.service");
|
|
335
|
+
} catch {}
|
|
336
|
+
console.log(chalk.default.red(" 🩸 Systemd user service installed"));
|
|
337
|
+
console.log(chalk.default.gray(" ✅ Lingering enabled (service runs without login)"));
|
|
338
|
+
console.log(chalk.default.gray(` Unit: ${unitFile}`));
|
|
339
|
+
} catch (err) {
|
|
340
|
+
console.log(chalk.default.yellow(" ⚠ Could not install systemd service — run manually:"));
|
|
341
|
+
console.log(chalk.default.gray(` sudo cp hyperclaw.service /etc/systemd/system/`));
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
async installMacOS() {
|
|
345
|
+
const home = os.default.homedir();
|
|
346
|
+
const plistPath = path.default.join(home, "Library/LaunchAgents/ai.hyperclaw.gateway.plist");
|
|
347
|
+
const nodePath = (await execAsync$1("which node").catch(() => ({ stdout: "/usr/local/bin/node" }))).stdout.trim();
|
|
348
|
+
const hcPath = (await execAsync$1("which hyperclaw").catch(() => ({ stdout: "/usr/local/bin/hyperclaw" }))).stdout.trim();
|
|
349
|
+
const pathEnv = process.env.PATH || "/usr/local/bin:/usr/bin:/bin:/opt/homebrew/bin";
|
|
350
|
+
const pathEscaped = pathEnv.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
351
|
+
const plist = `<?xml version="1.0" encoding="UTF-8"?>
|
|
352
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
353
|
+
<plist version="1.0">
|
|
354
|
+
<dict>
|
|
355
|
+
<key>Label</key>
|
|
356
|
+
<string>ai.hyperclaw.gateway</string>
|
|
357
|
+
<key>ProgramArguments</key>
|
|
358
|
+
<array>
|
|
359
|
+
<string>${nodePath}</string>
|
|
360
|
+
<string>${hcPath}</string>
|
|
361
|
+
<string>daemon</string>
|
|
362
|
+
<string>start</string>
|
|
363
|
+
</array>
|
|
364
|
+
<key>RunAtLoad</key>
|
|
365
|
+
<true/>
|
|
366
|
+
<key>KeepAlive</key>
|
|
367
|
+
<true/>
|
|
368
|
+
<key>StandardOutPath</key>
|
|
369
|
+
<string>${home}/.hyperclaw/logs/gateway.log</string>
|
|
370
|
+
<key>StandardErrorPath</key>
|
|
371
|
+
<string>${home}/.hyperclaw/logs/gateway.err</string>
|
|
372
|
+
<key>EnvironmentVariables</key>
|
|
373
|
+
<dict>
|
|
374
|
+
<key>PATH</key>
|
|
375
|
+
<string>${pathEscaped}</string>
|
|
376
|
+
<key>HOME</key>
|
|
377
|
+
<string>${home}</string>
|
|
378
|
+
</dict>
|
|
379
|
+
</dict>
|
|
380
|
+
</plist>`;
|
|
381
|
+
await fs_extra.default.ensureDir(path.default.dirname(plistPath));
|
|
382
|
+
await fs_extra.default.writeFile(plistPath, plist);
|
|
383
|
+
try {
|
|
384
|
+
await execAsync$1(`launchctl load ${plistPath}`);
|
|
385
|
+
} catch {}
|
|
386
|
+
console.log(chalk.default.red(" 🩸 LaunchAgent installed"));
|
|
387
|
+
console.log(chalk.default.gray(` Plist: ${plistPath}`));
|
|
388
|
+
}
|
|
389
|
+
async installWindows() {
|
|
390
|
+
console.log(chalk.default.yellow("\n Windows daemon installation\n"));
|
|
391
|
+
console.log(chalk.default.gray(" For full desktop access (screenshots, clipboard, apps):"));
|
|
392
|
+
console.log(chalk.default.gray(" Prefer running in an interactive user session:"));
|
|
393
|
+
console.log(chalk.default.cyan(" hyperclaw daemon start"));
|
|
394
|
+
console.log(chalk.default.gray(" Windows services have limited PATH and desktop access.\n"));
|
|
395
|
+
console.log(chalk.default.gray(" To install as a service (run cmd/PowerShell as Administrator):"));
|
|
396
|
+
console.log(chalk.default.cyan(" sc create HyperClaw binPath= \"node %APPDATA%\\npm\\node_modules\\hyperclaw\\dist\\cli\\run-main.js daemon start\" start= auto"));
|
|
397
|
+
console.log(chalk.default.cyan(" sc start HyperClaw"));
|
|
398
|
+
console.log(chalk.default.gray("\n Ensure node, git, python are in system PATH (not only user PATH)."));
|
|
399
|
+
console.log(chalk.default.gray(" See docs/FULL-ACCESS-CHECKLIST.md for details.\n"));
|
|
400
|
+
}
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
//#endregion
|
|
404
|
+
//#region src/cli/gateway.ts
|
|
405
|
+
const execAsync = (0, util.promisify)(child_process.exec);
|
|
406
|
+
const HC_DIR$1 = path.default.join(os.default.homedir(), ".hyperclaw");
|
|
407
|
+
var GatewayManager = class {
|
|
408
|
+
async isRunning(port = 18789) {
|
|
409
|
+
return this.detect(port);
|
|
410
|
+
}
|
|
411
|
+
async detectRuntime() {
|
|
412
|
+
for (const r of [
|
|
413
|
+
"bun",
|
|
414
|
+
"deno",
|
|
415
|
+
"node"
|
|
416
|
+
]) try {
|
|
417
|
+
await execAsync(`which ${r}`);
|
|
418
|
+
return r;
|
|
419
|
+
} catch {}
|
|
420
|
+
return "node";
|
|
421
|
+
}
|
|
422
|
+
exposureLabel(e) {
|
|
423
|
+
const m = {
|
|
424
|
+
off: "Off",
|
|
425
|
+
serve: "Serve (Tailscale)",
|
|
426
|
+
funnel: "Funnel (public)"
|
|
427
|
+
};
|
|
428
|
+
return m[e] || e;
|
|
429
|
+
}
|
|
430
|
+
async detect(port = 18789) {
|
|
431
|
+
return new Promise((resolve) => {
|
|
432
|
+
try {
|
|
433
|
+
const ws$1 = new ws.WebSocket(`ws://127.0.0.1:${port}`);
|
|
434
|
+
const t = setTimeout(() => {
|
|
435
|
+
ws$1.terminate();
|
|
436
|
+
resolve(false);
|
|
437
|
+
}, 1500);
|
|
438
|
+
ws$1.on("open", () => {
|
|
439
|
+
clearTimeout(t);
|
|
440
|
+
ws$1.close();
|
|
441
|
+
resolve(true);
|
|
442
|
+
});
|
|
443
|
+
ws$1.on("error", () => {
|
|
444
|
+
clearTimeout(t);
|
|
445
|
+
resolve(false);
|
|
446
|
+
});
|
|
447
|
+
} catch {
|
|
448
|
+
resolve(false);
|
|
449
|
+
}
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
async showStatus(cfg) {
|
|
453
|
+
const running = await this.detect(cfg.port);
|
|
454
|
+
const bindLabel = {
|
|
455
|
+
"127.0.0.1": "loopback (localhost only)",
|
|
456
|
+
"0.0.0.0": "all interfaces (LAN)",
|
|
457
|
+
"tailscale": "Tailscale VPN only"
|
|
458
|
+
}[cfg.bind] || cfg.bind;
|
|
459
|
+
console.log(chalk.default.bold.cyan("\n 💻 GATEWAY\n"));
|
|
460
|
+
console.log(` ${running ? chalk.default.green("● Running") : chalk.default.gray("○ Stopped")} ws://127.0.0.1:${cfg.port}`);
|
|
461
|
+
console.log(` Bind: ${bindLabel}`);
|
|
462
|
+
console.log(` Runtime: ${cfg.runtime}${cfg.runtime === "node" ? chalk.default.gray(" (recommended)") : ""}`);
|
|
463
|
+
if (cfg.tailscaleExposure !== "off") console.log(` Tailscale: ${chalk.default.yellow(cfg.tailscaleExposure)}`);
|
|
464
|
+
console.log(` Token: ${cfg.authToken ? chalk.default.green("set") : chalk.default.yellow("none (open)")}`);
|
|
465
|
+
console.log(` Channels: ${cfg.enabledChannels.join(", ") || chalk.default.gray("none")}`);
|
|
466
|
+
console.log();
|
|
467
|
+
}
|
|
468
|
+
async applyTailscaleExposure(mode, port) {
|
|
469
|
+
try {
|
|
470
|
+
if (mode === "serve") await execAsync(`tailscale serve https / http://localhost:${port}`);
|
|
471
|
+
else await execAsync(`tailscale funnel ${port}`);
|
|
472
|
+
console.log(chalk.default.green(` ✅ Tailscale ${mode} enabled`));
|
|
473
|
+
} catch (e) {
|
|
474
|
+
console.log(chalk.default.yellow(` ⚠ Tailscale: ${e.message.slice(0, 60)}`));
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
generateToken() {
|
|
478
|
+
return require("crypto").randomBytes(32).toString("base64url");
|
|
479
|
+
}
|
|
480
|
+
async resolveBindAddress(bind) {
|
|
481
|
+
if (["127.0.0.1", "0.0.0.0"].includes(bind)) return bind;
|
|
482
|
+
if (bind === "tailscale") try {
|
|
483
|
+
const { stdout } = await execAsync("tailscale ip -4 2>/dev/null");
|
|
484
|
+
return stdout.trim();
|
|
485
|
+
} catch {
|
|
486
|
+
return "127.0.0.1";
|
|
487
|
+
}
|
|
488
|
+
return bind;
|
|
489
|
+
}
|
|
490
|
+
async installService(cfg) {
|
|
491
|
+
const platform = os.default.platform();
|
|
492
|
+
if (platform === "linux") await this.installSystemd(cfg);
|
|
493
|
+
else if (platform === "darwin") await this.installLaunchAgent(cfg);
|
|
494
|
+
else console.log(chalk.default.yellow(" Windows: Use NSSM or Task Scheduler"));
|
|
495
|
+
}
|
|
496
|
+
async installSystemd(cfg) {
|
|
497
|
+
const binary = process.execPath;
|
|
498
|
+
const content = `[Unit]
|
|
499
|
+
Description=HyperClaw Gateway
|
|
500
|
+
After=network.target
|
|
501
|
+
|
|
502
|
+
[Service]
|
|
503
|
+
Type=simple
|
|
504
|
+
ExecStart=${binary} ${path.default.join(__dirname, "../../dist/cli/run-main.js")} gateway start
|
|
505
|
+
Restart=on-failure
|
|
506
|
+
RestartSec=5
|
|
507
|
+
Environment=PORT=${cfg.port}
|
|
508
|
+
|
|
509
|
+
[Install]
|
|
510
|
+
WantedBy=default.target
|
|
511
|
+
`;
|
|
512
|
+
const serviceDir = path.default.join(os.default.homedir(), ".config/systemd/user");
|
|
513
|
+
const serviceFile = path.default.join(serviceDir, "hyperclaw.service");
|
|
514
|
+
try {
|
|
515
|
+
await fs_extra.default.ensureDir(serviceDir);
|
|
516
|
+
await fs_extra.default.writeFile(serviceFile, content);
|
|
517
|
+
await execAsync("systemctl --user daemon-reload");
|
|
518
|
+
await execAsync("systemctl --user enable hyperclaw");
|
|
519
|
+
await execAsync(`loginctl enable-linger ${os.default.userInfo().username}`).catch(() => {});
|
|
520
|
+
console.log(chalk.default.green(" ✅ systemd service installed (lingering enabled)"));
|
|
521
|
+
} catch (e) {
|
|
522
|
+
console.log(chalk.default.gray(` Service file: ${serviceFile}`));
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
async installLaunchAgent(cfg) {
|
|
526
|
+
const binary = process.execPath;
|
|
527
|
+
const plistDir = path.default.join(os.default.homedir(), "Library/LaunchAgents");
|
|
528
|
+
const plistPath = path.default.join(plistDir, "ai.hyperclaw.gateway.plist");
|
|
529
|
+
const content = `<?xml version="1.0" encoding="UTF-8"?>
|
|
530
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
531
|
+
<plist version="1.0"><dict>
|
|
532
|
+
<key>Label</key><string>ai.hyperclaw.gateway</string>
|
|
533
|
+
<key>ProgramArguments</key><array>
|
|
534
|
+
<string>${binary}</string>
|
|
535
|
+
<string>${path.default.join(__dirname, "../../dist/cli/run-main.js")}</string>
|
|
536
|
+
<string>gateway</string><string>start</string>
|
|
537
|
+
</array>
|
|
538
|
+
<key>RunAtLoad</key><true/>
|
|
539
|
+
<key>KeepAlive</key><true/>
|
|
540
|
+
<key>StandardOutPath</key><string>${HC_DIR$1}/gateway.log</string>
|
|
541
|
+
<key>StandardErrorPath</key><string>${HC_DIR$1}/gateway.err</string>
|
|
542
|
+
</dict></plist>`;
|
|
543
|
+
await fs_extra.default.ensureDir(plistDir);
|
|
544
|
+
await fs_extra.default.writeFile(plistPath, content);
|
|
545
|
+
try {
|
|
546
|
+
await execAsync(`launchctl load ${plistPath}`);
|
|
547
|
+
console.log(chalk.default.green(" ✅ LaunchAgent installed"));
|
|
548
|
+
} catch {
|
|
549
|
+
console.log(chalk.default.gray(` Written: ${plistPath}`));
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
async reload(port, authToken) {
|
|
553
|
+
try {
|
|
554
|
+
const ws$1 = new ws.WebSocket(`ws://127.0.0.1:${port}`);
|
|
555
|
+
await new Promise((resolve, reject) => {
|
|
556
|
+
ws$1.on("open", () => {
|
|
557
|
+
if (authToken) ws$1.send(JSON.stringify({
|
|
558
|
+
type: "auth",
|
|
559
|
+
token: authToken
|
|
560
|
+
}));
|
|
561
|
+
ws$1.send(JSON.stringify({ type: "config:reload" }));
|
|
562
|
+
setTimeout(() => {
|
|
563
|
+
ws$1.close();
|
|
564
|
+
resolve();
|
|
565
|
+
}, 400);
|
|
566
|
+
});
|
|
567
|
+
ws$1.on("error", reject);
|
|
568
|
+
setTimeout(() => {
|
|
569
|
+
ws$1.terminate();
|
|
570
|
+
resolve();
|
|
571
|
+
}, 2e3);
|
|
572
|
+
});
|
|
573
|
+
console.log(chalk.default.green(" ✅ Gateway reloaded"));
|
|
574
|
+
} catch {
|
|
575
|
+
console.log(chalk.default.yellow(" ⚠ Gateway not running — changes apply on next start"));
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
};
|
|
579
|
+
|
|
580
|
+
//#endregion
|
|
581
|
+
//#region src/cli/providers.ts
|
|
582
|
+
const PROVIDERS = [
|
|
583
|
+
{
|
|
584
|
+
id: "anthropic",
|
|
585
|
+
displayName: "🎭 Anthropic",
|
|
586
|
+
authType: "api_key",
|
|
587
|
+
authLabel: "Anthropic API Key",
|
|
588
|
+
authHint: "console.anthropic.com → API Keys",
|
|
589
|
+
models: [
|
|
590
|
+
{
|
|
591
|
+
id: "claude-opus-4-5",
|
|
592
|
+
name: "Claude Opus 4.5",
|
|
593
|
+
contextK: 200,
|
|
594
|
+
reasoning: true,
|
|
595
|
+
flagship: true
|
|
596
|
+
},
|
|
597
|
+
{
|
|
598
|
+
id: "claude-sonnet-4-5",
|
|
599
|
+
name: "Claude Sonnet 4.5",
|
|
600
|
+
contextK: 200,
|
|
601
|
+
reasoning: true
|
|
602
|
+
},
|
|
603
|
+
{
|
|
604
|
+
id: "claude-haiku-4-5",
|
|
605
|
+
name: "Claude Haiku 4.5",
|
|
606
|
+
contextK: 200,
|
|
607
|
+
fast: true
|
|
608
|
+
}
|
|
609
|
+
]
|
|
610
|
+
},
|
|
611
|
+
{
|
|
612
|
+
id: "openrouter",
|
|
613
|
+
displayName: "🌐 OpenRouter",
|
|
614
|
+
authType: "api_key",
|
|
615
|
+
authLabel: "OpenRouter API Key",
|
|
616
|
+
authHint: "openrouter.ai/keys",
|
|
617
|
+
baseUrl: "https://openrouter.ai/api/v1",
|
|
618
|
+
supportsTranscription: true,
|
|
619
|
+
models: [
|
|
620
|
+
{
|
|
621
|
+
id: "openrouter/auto",
|
|
622
|
+
name: "Auto (best available)",
|
|
623
|
+
contextK: 200,
|
|
624
|
+
flagship: true
|
|
625
|
+
},
|
|
626
|
+
{
|
|
627
|
+
id: "anthropic/claude-opus-4.6",
|
|
628
|
+
name: "Claude Opus 4.6 (via OR)",
|
|
629
|
+
contextK: 200,
|
|
630
|
+
reasoning: true
|
|
631
|
+
},
|
|
632
|
+
{
|
|
633
|
+
id: "anthropic/claude-sonnet-4.5",
|
|
634
|
+
name: "Claude Sonnet 4.5 (via OR)",
|
|
635
|
+
contextK: 200
|
|
636
|
+
},
|
|
637
|
+
{
|
|
638
|
+
id: "openai/gpt-4o",
|
|
639
|
+
name: "GPT-4o",
|
|
640
|
+
contextK: 128,
|
|
641
|
+
vision: true
|
|
642
|
+
},
|
|
643
|
+
{
|
|
644
|
+
id: "openai/o3",
|
|
645
|
+
name: "o3 (reasoning)",
|
|
646
|
+
contextK: 200,
|
|
647
|
+
reasoning: true
|
|
648
|
+
},
|
|
649
|
+
{
|
|
650
|
+
id: "google/gemini-2.0-flash",
|
|
651
|
+
name: "Gemini 2.0 Flash",
|
|
652
|
+
contextK: 1e3,
|
|
653
|
+
fast: true
|
|
654
|
+
},
|
|
655
|
+
{
|
|
656
|
+
id: "google/gemini-2.5-pro",
|
|
657
|
+
name: "Gemini 2.5 Pro",
|
|
658
|
+
contextK: 1e3,
|
|
659
|
+
reasoning: true
|
|
660
|
+
},
|
|
661
|
+
{
|
|
662
|
+
id: "x-ai/grok-3",
|
|
663
|
+
name: "Grok 3",
|
|
664
|
+
contextK: 131,
|
|
665
|
+
reasoning: true
|
|
666
|
+
},
|
|
667
|
+
{
|
|
668
|
+
id: "deepseek/deepseek-r1",
|
|
669
|
+
name: "DeepSeek R1",
|
|
670
|
+
contextK: 64,
|
|
671
|
+
reasoning: true
|
|
672
|
+
},
|
|
673
|
+
{
|
|
674
|
+
id: "meta-llama/llama-3.3-70b-instruct",
|
|
675
|
+
name: "Llama 3.3 70B",
|
|
676
|
+
contextK: 128
|
|
677
|
+
},
|
|
678
|
+
{
|
|
679
|
+
id: "qwen/qwen-2.5-72b-instruct",
|
|
680
|
+
name: "Qwen 2.5 72B",
|
|
681
|
+
contextK: 128
|
|
682
|
+
},
|
|
683
|
+
{
|
|
684
|
+
id: "mistralai/mistral-large",
|
|
685
|
+
name: "Mistral Large",
|
|
686
|
+
contextK: 128
|
|
687
|
+
}
|
|
688
|
+
]
|
|
689
|
+
},
|
|
690
|
+
{
|
|
691
|
+
id: "openai",
|
|
692
|
+
displayName: "🧠 OpenAI",
|
|
693
|
+
authType: "api_key",
|
|
694
|
+
authLabel: "OpenAI API Key",
|
|
695
|
+
authHint: "platform.openai.com/api-keys",
|
|
696
|
+
supportsTranscription: true,
|
|
697
|
+
models: [
|
|
698
|
+
{
|
|
699
|
+
id: "gpt-4o",
|
|
700
|
+
name: "GPT-4o",
|
|
701
|
+
contextK: 128,
|
|
702
|
+
vision: true,
|
|
703
|
+
flagship: true
|
|
704
|
+
},
|
|
705
|
+
{
|
|
706
|
+
id: "gpt-4o-mini",
|
|
707
|
+
name: "GPT-4o Mini",
|
|
708
|
+
contextK: 128,
|
|
709
|
+
fast: true
|
|
710
|
+
},
|
|
711
|
+
{
|
|
712
|
+
id: "o3",
|
|
713
|
+
name: "o3 (reasoning)",
|
|
714
|
+
contextK: 200,
|
|
715
|
+
reasoning: true
|
|
716
|
+
},
|
|
717
|
+
{
|
|
718
|
+
id: "o4-mini",
|
|
719
|
+
name: "o4-mini (reasoning)",
|
|
720
|
+
contextK: 200,
|
|
721
|
+
reasoning: true,
|
|
722
|
+
fast: true
|
|
723
|
+
}
|
|
724
|
+
]
|
|
725
|
+
},
|
|
726
|
+
{
|
|
727
|
+
id: "google",
|
|
728
|
+
displayName: "🔍 Google",
|
|
729
|
+
authType: "api_key",
|
|
730
|
+
authLabel: "Google AI API Key",
|
|
731
|
+
authHint: "aistudio.google.com/app/apikey",
|
|
732
|
+
supportsTranscription: true,
|
|
733
|
+
models: [
|
|
734
|
+
{
|
|
735
|
+
id: "gemini-2.5-pro",
|
|
736
|
+
name: "Gemini 2.5 Pro",
|
|
737
|
+
contextK: 1e3,
|
|
738
|
+
reasoning: true,
|
|
739
|
+
flagship: true
|
|
740
|
+
},
|
|
741
|
+
{
|
|
742
|
+
id: "gemini-2.0-flash",
|
|
743
|
+
name: "Gemini 2.0 Flash",
|
|
744
|
+
contextK: 1e3,
|
|
745
|
+
fast: true
|
|
746
|
+
},
|
|
747
|
+
{
|
|
748
|
+
id: "gemini-1.5-pro",
|
|
749
|
+
name: "Gemini 1.5 Pro",
|
|
750
|
+
contextK: 2e3
|
|
751
|
+
}
|
|
752
|
+
]
|
|
753
|
+
},
|
|
754
|
+
{
|
|
755
|
+
id: "xai",
|
|
756
|
+
displayName: "⚡ xAI (Grok)",
|
|
757
|
+
authType: "api_key",
|
|
758
|
+
authLabel: "xAI API Key",
|
|
759
|
+
authHint: "console.x.ai",
|
|
760
|
+
models: [{
|
|
761
|
+
id: "grok-3",
|
|
762
|
+
name: "Grok 3",
|
|
763
|
+
contextK: 131,
|
|
764
|
+
reasoning: true,
|
|
765
|
+
flagship: true
|
|
766
|
+
}, {
|
|
767
|
+
id: "grok-3-mini",
|
|
768
|
+
name: "Grok 3 Mini",
|
|
769
|
+
contextK: 131,
|
|
770
|
+
fast: true
|
|
771
|
+
}]
|
|
772
|
+
},
|
|
773
|
+
{
|
|
774
|
+
id: "minimax",
|
|
775
|
+
displayName: "🎯 MiniMax",
|
|
776
|
+
authType: "api_key",
|
|
777
|
+
authLabel: "MiniMax API Key",
|
|
778
|
+
authHint: "platform.minimaxi.com",
|
|
779
|
+
models: [{
|
|
780
|
+
id: "MiniMax-Text-01",
|
|
781
|
+
name: "MiniMax Text-01",
|
|
782
|
+
contextK: 1e3,
|
|
783
|
+
flagship: true
|
|
784
|
+
}, {
|
|
785
|
+
id: "abab6.5s-chat",
|
|
786
|
+
name: "ABAB 6.5S",
|
|
787
|
+
contextK: 245
|
|
788
|
+
}]
|
|
789
|
+
},
|
|
790
|
+
{
|
|
791
|
+
id: "moonshot",
|
|
792
|
+
displayName: "🌙 Moonshot (Kimi)",
|
|
793
|
+
authType: "api_key",
|
|
794
|
+
authLabel: "Moonshot API Key",
|
|
795
|
+
authHint: "platform.moonshot.cn",
|
|
796
|
+
models: [{
|
|
797
|
+
id: "moonshot-v1-128k",
|
|
798
|
+
name: "Moonshot v1 128K",
|
|
799
|
+
contextK: 128,
|
|
800
|
+
flagship: true
|
|
801
|
+
}, {
|
|
802
|
+
id: "moonshot-v1-8k",
|
|
803
|
+
name: "Moonshot v1 8K",
|
|
804
|
+
contextK: 8,
|
|
805
|
+
fast: true
|
|
806
|
+
}]
|
|
807
|
+
},
|
|
808
|
+
{
|
|
809
|
+
id: "qwen",
|
|
810
|
+
displayName: "🐉 Qwen (Alibaba)",
|
|
811
|
+
authType: "api_key",
|
|
812
|
+
authLabel: "DashScope API Key",
|
|
813
|
+
authHint: "dashscope.aliyuncs.com",
|
|
814
|
+
models: [
|
|
815
|
+
{
|
|
816
|
+
id: "qwen-max",
|
|
817
|
+
name: "Qwen Max",
|
|
818
|
+
contextK: 32,
|
|
819
|
+
flagship: true
|
|
820
|
+
},
|
|
821
|
+
{
|
|
822
|
+
id: "qwen-plus",
|
|
823
|
+
name: "Qwen Plus",
|
|
824
|
+
contextK: 128
|
|
825
|
+
},
|
|
826
|
+
{
|
|
827
|
+
id: "qwen-turbo",
|
|
828
|
+
name: "Qwen Turbo",
|
|
829
|
+
contextK: 128,
|
|
830
|
+
fast: true
|
|
831
|
+
},
|
|
832
|
+
{
|
|
833
|
+
id: "qwen3-235b-a22b",
|
|
834
|
+
name: "Qwen3 235B",
|
|
835
|
+
contextK: 32,
|
|
836
|
+
reasoning: true
|
|
837
|
+
}
|
|
838
|
+
]
|
|
839
|
+
},
|
|
840
|
+
{
|
|
841
|
+
id: "zai",
|
|
842
|
+
displayName: "🔧 Z.AI",
|
|
843
|
+
authType: "api_key",
|
|
844
|
+
authLabel: "Z.AI API Key",
|
|
845
|
+
authHint: "z.ai",
|
|
846
|
+
models: [{
|
|
847
|
+
id: "glm-4-plus",
|
|
848
|
+
name: "GLM-4 Plus",
|
|
849
|
+
contextK: 128,
|
|
850
|
+
flagship: true
|
|
851
|
+
}, {
|
|
852
|
+
id: "glm-4-flash",
|
|
853
|
+
name: "GLM-4 Flash",
|
|
854
|
+
contextK: 128,
|
|
855
|
+
fast: true
|
|
856
|
+
}]
|
|
857
|
+
},
|
|
858
|
+
{
|
|
859
|
+
id: "litellm",
|
|
860
|
+
displayName: "🔀 LiteLLM (proxy)",
|
|
861
|
+
authType: "api_key",
|
|
862
|
+
authLabel: "LiteLLM Master Key",
|
|
863
|
+
authHint: "Your self-hosted LiteLLM proxy key",
|
|
864
|
+
models: [{
|
|
865
|
+
id: "gpt-4o",
|
|
866
|
+
name: "GPT-4o (via proxy)",
|
|
867
|
+
contextK: 128,
|
|
868
|
+
flagship: true
|
|
869
|
+
}]
|
|
870
|
+
},
|
|
871
|
+
{
|
|
872
|
+
id: "cloudflare",
|
|
873
|
+
displayName: "☁️ Cloudflare AI Gateway",
|
|
874
|
+
authType: "api_key",
|
|
875
|
+
authLabel: "Cloudflare API Token",
|
|
876
|
+
authHint: "dash.cloudflare.com → AI → Gateway",
|
|
877
|
+
models: [{
|
|
878
|
+
id: "@cf/meta/llama-3.3-70b-instruct-fp8-fast",
|
|
879
|
+
name: "Llama 3.3 70B (CF)",
|
|
880
|
+
contextK: 128,
|
|
881
|
+
flagship: true
|
|
882
|
+
}]
|
|
883
|
+
},
|
|
884
|
+
{
|
|
885
|
+
id: "copilot",
|
|
886
|
+
displayName: "🤖 GitHub Copilot",
|
|
887
|
+
authType: "oauth",
|
|
888
|
+
authLabel: "GitHub OAuth Token",
|
|
889
|
+
authHint: "github.com/settings/tokens",
|
|
890
|
+
models: [{
|
|
891
|
+
id: "gpt-4o",
|
|
892
|
+
name: "GPT-4o (Copilot)",
|
|
893
|
+
contextK: 128,
|
|
894
|
+
flagship: true
|
|
895
|
+
}, {
|
|
896
|
+
id: "claude-sonnet-4-5",
|
|
897
|
+
name: "Claude Sonnet (Copilot)",
|
|
898
|
+
contextK: 200
|
|
899
|
+
}]
|
|
900
|
+
},
|
|
901
|
+
{
|
|
902
|
+
id: "custom",
|
|
903
|
+
displayName: "🔌 Custom (OpenAI-compatible API)",
|
|
904
|
+
authType: "api_key",
|
|
905
|
+
authLabel: "API Key",
|
|
906
|
+
authHint: "Any OpenAI-compatible /chat/completions API (e.g. Ads Power, Proxies, new LLM APIs)",
|
|
907
|
+
models: [{
|
|
908
|
+
id: "__manual__",
|
|
909
|
+
name: "Enter model ID manually",
|
|
910
|
+
contextK: 128,
|
|
911
|
+
flagship: true
|
|
912
|
+
}]
|
|
913
|
+
}
|
|
914
|
+
];
|
|
915
|
+
function getProvider(id) {
|
|
916
|
+
return PROVIDERS.find((p) => p.id === id);
|
|
917
|
+
}
|
|
918
|
+
/** Providers that support voice note transcription. Shown in wizard. */
|
|
919
|
+
function getTranscriptionProviders() {
|
|
920
|
+
return PROVIDERS.filter((p) => p.supportsTranscription);
|
|
921
|
+
}
|
|
922
|
+
function formatModel(m) {
|
|
923
|
+
const badges = [];
|
|
924
|
+
if (m.flagship) badges.push(chalk.default.yellow("★"));
|
|
925
|
+
if (m.reasoning) badges.push(chalk.default.magenta("reasoning"));
|
|
926
|
+
if (m.fast) badges.push(chalk.default.green("fast"));
|
|
927
|
+
if (m.vision) badges.push(chalk.default.cyan("vision"));
|
|
928
|
+
const ctx = m.contextK >= 1e3 ? `${m.contextK}K` : `${m.contextK}K`;
|
|
929
|
+
return `${badges.join(" ")} ${m.name} ${chalk.default.gray(`ctx ${ctx}`)}`.trim();
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
//#endregion
|
|
933
|
+
//#region src/cli/channels.ts
|
|
934
|
+
const CHANNEL_DEFS = [
|
|
935
|
+
{
|
|
936
|
+
id: "telegram",
|
|
937
|
+
name: "Telegram",
|
|
938
|
+
emoji: "✈️",
|
|
939
|
+
requiresGateway: false,
|
|
940
|
+
supportsDM: true,
|
|
941
|
+
platforms: ["all"],
|
|
942
|
+
tokenLabel: "Telegram Bot Token",
|
|
943
|
+
tokenHint: "Get from @BotFather → /newbot",
|
|
944
|
+
setupSteps: [
|
|
945
|
+
"1. Open Telegram → @BotFather → /newbot",
|
|
946
|
+
"2. Set name and username (must end in bot)",
|
|
947
|
+
"3. Copy the Bot Token",
|
|
948
|
+
" 🔗 t.me/BotFather"
|
|
949
|
+
],
|
|
950
|
+
npmPackage: "node-telegram-bot-api",
|
|
951
|
+
defaultDMPolicy: "pairing"
|
|
952
|
+
},
|
|
953
|
+
{
|
|
954
|
+
id: "discord",
|
|
955
|
+
name: "Discord",
|
|
956
|
+
emoji: "🎮",
|
|
957
|
+
requiresGateway: false,
|
|
958
|
+
supportsDM: true,
|
|
959
|
+
platforms: ["all"],
|
|
960
|
+
tokenLabel: "Discord Bot Token",
|
|
961
|
+
tokenHint: "discord.com/developers/applications",
|
|
962
|
+
setupSteps: [
|
|
963
|
+
"1. discord.com/developers/applications → New Application",
|
|
964
|
+
"2. Bot → Add Bot → Reset Token",
|
|
965
|
+
"3. OAuth2 → General → copy Application ID",
|
|
966
|
+
" 🔗 discord.com/developers/applications"
|
|
967
|
+
],
|
|
968
|
+
extraFields: [{
|
|
969
|
+
name: "clientId",
|
|
970
|
+
label: "Client ID",
|
|
971
|
+
required: true
|
|
972
|
+
}],
|
|
973
|
+
npmPackage: "discord.js",
|
|
974
|
+
defaultDMPolicy: "pairing"
|
|
975
|
+
},
|
|
976
|
+
{
|
|
977
|
+
id: "whatsapp",
|
|
978
|
+
name: "WhatsApp",
|
|
979
|
+
emoji: "📱",
|
|
980
|
+
requiresGateway: true,
|
|
981
|
+
supportsDM: true,
|
|
982
|
+
platforms: ["all"],
|
|
983
|
+
tokenLabel: "WhatsApp Business API key",
|
|
984
|
+
tokenHint: "business.whatsapp.com",
|
|
985
|
+
setupSteps: [
|
|
986
|
+
"1. developers.facebook.com → My Apps → Create App",
|
|
987
|
+
"2. WhatsApp → API Setup → Access Token",
|
|
988
|
+
" 🔗 business.whatsapp.com"
|
|
989
|
+
],
|
|
990
|
+
npmPackage: "@whiskeysockets/baileys",
|
|
991
|
+
defaultDMPolicy: "pairing"
|
|
992
|
+
},
|
|
993
|
+
{
|
|
994
|
+
id: "slack",
|
|
995
|
+
name: "Slack",
|
|
996
|
+
emoji: "💼",
|
|
997
|
+
requiresGateway: true,
|
|
998
|
+
supportsDM: true,
|
|
999
|
+
platforms: ["all"],
|
|
1000
|
+
tokenLabel: "Slack Bot Token (xoxb-...)",
|
|
1001
|
+
tokenHint: "api.slack.com/apps",
|
|
1002
|
+
setupSteps: [
|
|
1003
|
+
"1. api.slack.com/apps → Create App → Bot",
|
|
1004
|
+
"2. Install App → copy Bot Token (xoxb-)",
|
|
1005
|
+
"3. Basic Information → Signing Secret",
|
|
1006
|
+
" 🔗 api.slack.com/apps"
|
|
1007
|
+
],
|
|
1008
|
+
extraFields: [{
|
|
1009
|
+
name: "signingSecret",
|
|
1010
|
+
label: "Signing Secret",
|
|
1011
|
+
required: true
|
|
1012
|
+
}],
|
|
1013
|
+
defaultDMPolicy: "allowlist"
|
|
1014
|
+
},
|
|
1015
|
+
{
|
|
1016
|
+
id: "signal",
|
|
1017
|
+
name: "Signal",
|
|
1018
|
+
emoji: "🔒",
|
|
1019
|
+
requiresGateway: true,
|
|
1020
|
+
supportsDM: true,
|
|
1021
|
+
platforms: ["linux", "darwin"],
|
|
1022
|
+
tokenLabel: "Registered phone number",
|
|
1023
|
+
tokenHint: "Requires signal-cli installed",
|
|
1024
|
+
setupSteps: [
|
|
1025
|
+
"1. Install signal-cli or signald",
|
|
1026
|
+
"2. Link your number (signal-cli link or signald register)",
|
|
1027
|
+
" 🔗 github.com/AsamK/signal-cli"
|
|
1028
|
+
],
|
|
1029
|
+
notes: "Needs signal-cli + registered number",
|
|
1030
|
+
defaultDMPolicy: "pairing"
|
|
1031
|
+
},
|
|
1032
|
+
{
|
|
1033
|
+
id: "imessage",
|
|
1034
|
+
name: "iMessage",
|
|
1035
|
+
emoji: "🍏",
|
|
1036
|
+
requiresGateway: true,
|
|
1037
|
+
supportsDM: true,
|
|
1038
|
+
platforms: ["darwin"],
|
|
1039
|
+
tokenLabel: "BlueBubbles server password",
|
|
1040
|
+
tokenHint: "bluebubbles.app on macOS",
|
|
1041
|
+
setupSteps: [
|
|
1042
|
+
"1. Install BlueBubbles on a Mac",
|
|
1043
|
+
"2. Configure and connect",
|
|
1044
|
+
" 🔗 bluebubbles.app"
|
|
1045
|
+
],
|
|
1046
|
+
notes: "macOS only via BlueBubbles",
|
|
1047
|
+
defaultDMPolicy: "pairing"
|
|
1048
|
+
},
|
|
1049
|
+
{
|
|
1050
|
+
id: "imessage-native",
|
|
1051
|
+
name: "iMessage (imsg)",
|
|
1052
|
+
emoji: "💬",
|
|
1053
|
+
requiresGateway: true,
|
|
1054
|
+
supportsDM: true,
|
|
1055
|
+
platforms: ["darwin"],
|
|
1056
|
+
tokenLabel: "Not required",
|
|
1057
|
+
tokenHint: "Uses imsg CLI",
|
|
1058
|
+
setupSteps: [
|
|
1059
|
+
"1. Install imsg CLI: brew install steipete/imsg/imsg",
|
|
1060
|
+
"2. No token required — uses native iMessage",
|
|
1061
|
+
" 🔗 github.com/steipete/imsg"
|
|
1062
|
+
],
|
|
1063
|
+
notes: "macOS only, native via imsg CLI (github.com/steipete/imsg). No BlueBubbles.",
|
|
1064
|
+
defaultDMPolicy: "pairing"
|
|
1065
|
+
},
|
|
1066
|
+
{
|
|
1067
|
+
id: "matrix",
|
|
1068
|
+
name: "Matrix",
|
|
1069
|
+
emoji: "🔢",
|
|
1070
|
+
requiresGateway: true,
|
|
1071
|
+
supportsDM: true,
|
|
1072
|
+
platforms: ["all"],
|
|
1073
|
+
tokenLabel: "Matrix access token",
|
|
1074
|
+
tokenHint: "element.io → Settings → Help → Access Token",
|
|
1075
|
+
setupSteps: [
|
|
1076
|
+
"1. Element → Settings → Help & About → Access Token",
|
|
1077
|
+
"2. Copy the access token",
|
|
1078
|
+
" 🔗 element.io"
|
|
1079
|
+
],
|
|
1080
|
+
extraFields: [{
|
|
1081
|
+
name: "homeserver",
|
|
1082
|
+
label: "Homeserver URL",
|
|
1083
|
+
hint: "https://matrix.org",
|
|
1084
|
+
required: true
|
|
1085
|
+
}, {
|
|
1086
|
+
name: "userId",
|
|
1087
|
+
label: "User ID (@user:server)",
|
|
1088
|
+
required: true
|
|
1089
|
+
}],
|
|
1090
|
+
defaultDMPolicy: "pairing"
|
|
1091
|
+
},
|
|
1092
|
+
{
|
|
1093
|
+
id: "email",
|
|
1094
|
+
name: "Email",
|
|
1095
|
+
emoji: "📧",
|
|
1096
|
+
requiresGateway: false,
|
|
1097
|
+
supportsDM: true,
|
|
1098
|
+
platforms: ["all"],
|
|
1099
|
+
tokenLabel: "Gmail app password or IMAP password",
|
|
1100
|
+
tokenHint: "Use app-specific password",
|
|
1101
|
+
setupSteps: [
|
|
1102
|
+
"1. Enable IMAP/SMTP in your email provider",
|
|
1103
|
+
"2. Use app password if you have 2FA",
|
|
1104
|
+
" 🔗 Gmail: myaccount.google.com/apppasswords"
|
|
1105
|
+
],
|
|
1106
|
+
extraFields: [
|
|
1107
|
+
{
|
|
1108
|
+
name: "imapHost",
|
|
1109
|
+
label: "IMAP host",
|
|
1110
|
+
hint: "imap.gmail.com",
|
|
1111
|
+
required: true
|
|
1112
|
+
},
|
|
1113
|
+
{
|
|
1114
|
+
name: "smtpHost",
|
|
1115
|
+
label: "SMTP host",
|
|
1116
|
+
hint: "smtp.gmail.com",
|
|
1117
|
+
required: true
|
|
1118
|
+
},
|
|
1119
|
+
{
|
|
1120
|
+
name: "user",
|
|
1121
|
+
label: "Email address",
|
|
1122
|
+
required: true
|
|
1123
|
+
}
|
|
1124
|
+
],
|
|
1125
|
+
defaultDMPolicy: "allowlist"
|
|
1126
|
+
},
|
|
1127
|
+
{
|
|
1128
|
+
id: "feishu",
|
|
1129
|
+
name: "Feishu/Lark",
|
|
1130
|
+
emoji: "🪶",
|
|
1131
|
+
requiresGateway: true,
|
|
1132
|
+
supportsDM: true,
|
|
1133
|
+
platforms: ["all"],
|
|
1134
|
+
tokenLabel: "Feishu App ID",
|
|
1135
|
+
tokenHint: "open.feishu.cn",
|
|
1136
|
+
setupSteps: [
|
|
1137
|
+
"1. open.feishu.cn → Create application",
|
|
1138
|
+
"2. Copy App ID and App Secret",
|
|
1139
|
+
" 🔗 open.feishu.cn"
|
|
1140
|
+
],
|
|
1141
|
+
extraFields: [{
|
|
1142
|
+
name: "appSecret",
|
|
1143
|
+
label: "App Secret",
|
|
1144
|
+
required: true
|
|
1145
|
+
}],
|
|
1146
|
+
defaultDMPolicy: "pairing"
|
|
1147
|
+
},
|
|
1148
|
+
{
|
|
1149
|
+
id: "msteams",
|
|
1150
|
+
name: "MS Teams",
|
|
1151
|
+
emoji: "🟦",
|
|
1152
|
+
requiresGateway: true,
|
|
1153
|
+
supportsDM: true,
|
|
1154
|
+
platforms: ["all"],
|
|
1155
|
+
tokenLabel: "Azure Bot App ID",
|
|
1156
|
+
tokenHint: "portal.azure.com → Bot Services",
|
|
1157
|
+
setupSteps: [
|
|
1158
|
+
"1. dev.botframework.com → Register Bot",
|
|
1159
|
+
"2. Azure Bot → Configuration → copy App ID & Secret",
|
|
1160
|
+
" 🔗 dev.botframework.com"
|
|
1161
|
+
],
|
|
1162
|
+
extraFields: [{
|
|
1163
|
+
name: "appPassword",
|
|
1164
|
+
label: "App Password",
|
|
1165
|
+
required: true
|
|
1166
|
+
}],
|
|
1167
|
+
defaultDMPolicy: "allowlist"
|
|
1168
|
+
},
|
|
1169
|
+
{
|
|
1170
|
+
id: "messenger",
|
|
1171
|
+
name: "Messenger",
|
|
1172
|
+
emoji: "💬",
|
|
1173
|
+
requiresGateway: true,
|
|
1174
|
+
supportsDM: true,
|
|
1175
|
+
platforms: ["all"],
|
|
1176
|
+
tokenLabel: "Page Access Token",
|
|
1177
|
+
tokenHint: "developers.facebook.com",
|
|
1178
|
+
setupSteps: [
|
|
1179
|
+
"1. developers.facebook.com → My Apps → Create App",
|
|
1180
|
+
"2. Add Messenger product → Page Access Token",
|
|
1181
|
+
" 🔗 developers.facebook.com"
|
|
1182
|
+
],
|
|
1183
|
+
extraFields: [{
|
|
1184
|
+
name: "verifyToken",
|
|
1185
|
+
label: "Webhook Verify Token",
|
|
1186
|
+
required: true
|
|
1187
|
+
}],
|
|
1188
|
+
defaultDMPolicy: "pairing"
|
|
1189
|
+
},
|
|
1190
|
+
{
|
|
1191
|
+
id: "nostr",
|
|
1192
|
+
name: "Nostr",
|
|
1193
|
+
emoji: "🌐",
|
|
1194
|
+
requiresGateway: false,
|
|
1195
|
+
supportsDM: true,
|
|
1196
|
+
platforms: ["all"],
|
|
1197
|
+
tokenLabel: "Nostr private key (hex or nsec)",
|
|
1198
|
+
tokenHint: "Generate with: openssl rand -hex 32",
|
|
1199
|
+
setupSteps: [
|
|
1200
|
+
"1. Use existing nostr client or generate new keypair",
|
|
1201
|
+
"2. Copy nsec (private key)",
|
|
1202
|
+
" 🔗 nostr.com"
|
|
1203
|
+
],
|
|
1204
|
+
notes: "Decentralized — no account needed",
|
|
1205
|
+
defaultDMPolicy: "open"
|
|
1206
|
+
},
|
|
1207
|
+
{
|
|
1208
|
+
id: "line",
|
|
1209
|
+
name: "LINE",
|
|
1210
|
+
emoji: "💚",
|
|
1211
|
+
requiresGateway: true,
|
|
1212
|
+
supportsDM: true,
|
|
1213
|
+
platforms: ["all"],
|
|
1214
|
+
tokenLabel: "LINE Channel Access Token",
|
|
1215
|
+
tokenHint: "developers.line.biz",
|
|
1216
|
+
setupSteps: [
|
|
1217
|
+
"1. developers.line.biz → Add Messaging API channel",
|
|
1218
|
+
"2. Copy Channel Secret & Access Token",
|
|
1219
|
+
" 🔗 developers.line.biz"
|
|
1220
|
+
],
|
|
1221
|
+
extraFields: [{
|
|
1222
|
+
name: "channelSecret",
|
|
1223
|
+
label: "Channel Secret",
|
|
1224
|
+
required: true
|
|
1225
|
+
}],
|
|
1226
|
+
defaultDMPolicy: "pairing"
|
|
1227
|
+
},
|
|
1228
|
+
{
|
|
1229
|
+
id: "viber",
|
|
1230
|
+
name: "Viber",
|
|
1231
|
+
emoji: "💜",
|
|
1232
|
+
requiresGateway: true,
|
|
1233
|
+
supportsDM: true,
|
|
1234
|
+
platforms: ["all"],
|
|
1235
|
+
tokenLabel: "Viber Auth Token",
|
|
1236
|
+
tokenHint: "partners.viber.com",
|
|
1237
|
+
setupSteps: [
|
|
1238
|
+
"1. partners.viber.com → Create Bot",
|
|
1239
|
+
"2. Copy the Auth Token",
|
|
1240
|
+
" 🔗 partners.viber.com"
|
|
1241
|
+
],
|
|
1242
|
+
defaultDMPolicy: "pairing"
|
|
1243
|
+
},
|
|
1244
|
+
{
|
|
1245
|
+
id: "zalo",
|
|
1246
|
+
name: "Zalo OA",
|
|
1247
|
+
emoji: "🔵",
|
|
1248
|
+
requiresGateway: true,
|
|
1249
|
+
supportsDM: true,
|
|
1250
|
+
platforms: ["all"],
|
|
1251
|
+
tokenLabel: "Zalo OA Access Token",
|
|
1252
|
+
tokenHint: "developers.zalo.me",
|
|
1253
|
+
setupSteps: [
|
|
1254
|
+
"1. developers.zalo.me → Create application",
|
|
1255
|
+
"2. Copy App ID and Access Token",
|
|
1256
|
+
" 🔗 developers.zalo.me"
|
|
1257
|
+
],
|
|
1258
|
+
extraFields: [{
|
|
1259
|
+
name: "secretKey",
|
|
1260
|
+
label: "Secret Key",
|
|
1261
|
+
required: true
|
|
1262
|
+
}],
|
|
1263
|
+
defaultDMPolicy: "pairing"
|
|
1264
|
+
},
|
|
1265
|
+
{
|
|
1266
|
+
id: "twitter",
|
|
1267
|
+
name: "Twitter/X DM",
|
|
1268
|
+
emoji: "🐦",
|
|
1269
|
+
requiresGateway: true,
|
|
1270
|
+
supportsDM: true,
|
|
1271
|
+
platforms: ["all"],
|
|
1272
|
+
tokenLabel: "Twitter API Key",
|
|
1273
|
+
tokenHint: "developer.twitter.com",
|
|
1274
|
+
setupSteps: [
|
|
1275
|
+
"1. developer.twitter.com → Developer Portal",
|
|
1276
|
+
"2. Create Project & App → copy API keys",
|
|
1277
|
+
" 🔗 developer.twitter.com"
|
|
1278
|
+
],
|
|
1279
|
+
extraFields: [
|
|
1280
|
+
{
|
|
1281
|
+
name: "apiKeySecret",
|
|
1282
|
+
label: "API Key Secret",
|
|
1283
|
+
required: true
|
|
1284
|
+
},
|
|
1285
|
+
{
|
|
1286
|
+
name: "accessToken",
|
|
1287
|
+
label: "Access Token",
|
|
1288
|
+
required: true
|
|
1289
|
+
},
|
|
1290
|
+
{
|
|
1291
|
+
name: "accessTokenSecret",
|
|
1292
|
+
label: "Access Token Secret",
|
|
1293
|
+
required: true
|
|
1294
|
+
}
|
|
1295
|
+
],
|
|
1296
|
+
defaultDMPolicy: "allowlist"
|
|
1297
|
+
},
|
|
1298
|
+
{
|
|
1299
|
+
id: "irc",
|
|
1300
|
+
name: "IRC",
|
|
1301
|
+
emoji: "📡",
|
|
1302
|
+
requiresGateway: true,
|
|
1303
|
+
supportsDM: true,
|
|
1304
|
+
platforms: ["all"],
|
|
1305
|
+
tokenLabel: "NickServ password (optional)",
|
|
1306
|
+
tokenHint: "freenode, libera.chat, etc.",
|
|
1307
|
+
setupSteps: [
|
|
1308
|
+
"1. Choose IRC server (e.g. irc.libera.chat)",
|
|
1309
|
+
"2. Configure nick and password if needed",
|
|
1310
|
+
" 🔗 libera.chat"
|
|
1311
|
+
],
|
|
1312
|
+
extraFields: [{
|
|
1313
|
+
name: "server",
|
|
1314
|
+
label: "IRC server",
|
|
1315
|
+
hint: "irc.libera.chat",
|
|
1316
|
+
required: true
|
|
1317
|
+
}, {
|
|
1318
|
+
name: "nick",
|
|
1319
|
+
label: "Nickname",
|
|
1320
|
+
required: true
|
|
1321
|
+
}],
|
|
1322
|
+
defaultDMPolicy: "allowlist"
|
|
1323
|
+
},
|
|
1324
|
+
{
|
|
1325
|
+
id: "mattermost",
|
|
1326
|
+
name: "Mattermost",
|
|
1327
|
+
emoji: "🏗️",
|
|
1328
|
+
requiresGateway: true,
|
|
1329
|
+
supportsDM: true,
|
|
1330
|
+
platforms: ["all"],
|
|
1331
|
+
tokenLabel: "Mattermost bot token",
|
|
1332
|
+
tokenHint: "Settings → Integrations → Bot Accounts",
|
|
1333
|
+
setupSteps: [
|
|
1334
|
+
"1. Mattermost → Account Settings → Security → Personal Access Tokens",
|
|
1335
|
+
"2. Create token and copy it",
|
|
1336
|
+
" 🔗 docs.mattermost.com"
|
|
1337
|
+
],
|
|
1338
|
+
extraFields: [{
|
|
1339
|
+
name: "serverUrl",
|
|
1340
|
+
label: "Server URL",
|
|
1341
|
+
required: true
|
|
1342
|
+
}],
|
|
1343
|
+
defaultDMPolicy: "allowlist"
|
|
1344
|
+
},
|
|
1345
|
+
{
|
|
1346
|
+
id: "nextcloud",
|
|
1347
|
+
name: "Nextcloud Talk",
|
|
1348
|
+
emoji: "☁️",
|
|
1349
|
+
requiresGateway: true,
|
|
1350
|
+
supportsDM: true,
|
|
1351
|
+
platforms: ["all"],
|
|
1352
|
+
tokenLabel: "Nextcloud app password",
|
|
1353
|
+
tokenHint: "Your Nextcloud server",
|
|
1354
|
+
setupSteps: [
|
|
1355
|
+
"1. Nextcloud Admin → OAuth → new client",
|
|
1356
|
+
"2. Talk → view bot credentials",
|
|
1357
|
+
" 🔗 nextcloud.com"
|
|
1358
|
+
],
|
|
1359
|
+
extraFields: [{
|
|
1360
|
+
name: "serverUrl",
|
|
1361
|
+
label: "Nextcloud URL",
|
|
1362
|
+
required: true
|
|
1363
|
+
}, {
|
|
1364
|
+
name: "username",
|
|
1365
|
+
label: "Username",
|
|
1366
|
+
required: true
|
|
1367
|
+
}],
|
|
1368
|
+
defaultDMPolicy: "allowlist"
|
|
1369
|
+
},
|
|
1370
|
+
{
|
|
1371
|
+
id: "googlechat",
|
|
1372
|
+
name: "Google Chat",
|
|
1373
|
+
emoji: "🔵",
|
|
1374
|
+
requiresGateway: true,
|
|
1375
|
+
supportsDM: false,
|
|
1376
|
+
platforms: ["all"],
|
|
1377
|
+
tokenLabel: "Google Chat Webhook URL",
|
|
1378
|
+
tokenHint: "chat.google.com → Space → Apps & Integrations",
|
|
1379
|
+
setupSteps: [
|
|
1380
|
+
"1. chat.google.com → Space → Apps & Integrations → Manage webhooks",
|
|
1381
|
+
"2. Add webhook and copy the URL",
|
|
1382
|
+
" 🔗 chat.google.com"
|
|
1383
|
+
],
|
|
1384
|
+
defaultDMPolicy: "none"
|
|
1385
|
+
},
|
|
1386
|
+
{
|
|
1387
|
+
id: "whatsapp-baileys",
|
|
1388
|
+
name: "WhatsApp (Baileys)",
|
|
1389
|
+
emoji: "📲",
|
|
1390
|
+
requiresGateway: true,
|
|
1391
|
+
supportsDM: true,
|
|
1392
|
+
platforms: ["all"],
|
|
1393
|
+
tokenLabel: "Not required — scans QR code on first run",
|
|
1394
|
+
setupSteps: [
|
|
1395
|
+
"1. No Meta Business API needed — uses WhatsApp Web protocol",
|
|
1396
|
+
"2. Start the gateway — a QR code will appear in the terminal",
|
|
1397
|
+
"3. Open WhatsApp on your phone → Linked Devices → Link a device",
|
|
1398
|
+
"4. Scan the QR code. Session is saved — no QR needed on future starts",
|
|
1399
|
+
" 🔗 github.com/WhiskeySockets/Baileys"
|
|
1400
|
+
],
|
|
1401
|
+
notes: "WhatsApp Web via Baileys — no Meta Business needed. QR scan on first run.",
|
|
1402
|
+
npmPackage: "@whiskeysockets/baileys",
|
|
1403
|
+
defaultDMPolicy: "pairing"
|
|
1404
|
+
},
|
|
1405
|
+
{
|
|
1406
|
+
id: "instagram",
|
|
1407
|
+
name: "Instagram DMs",
|
|
1408
|
+
emoji: "📸",
|
|
1409
|
+
requiresGateway: true,
|
|
1410
|
+
supportsDM: true,
|
|
1411
|
+
platforms: ["all"],
|
|
1412
|
+
tokenLabel: "Meta Page Access Token",
|
|
1413
|
+
tokenHint: "developers.facebook.com → Instagram product",
|
|
1414
|
+
setupSteps: [
|
|
1415
|
+
"1. Meta for Developers → My Apps → Create App → Business",
|
|
1416
|
+
"2. Add Instagram product → Connect Instagram Business account",
|
|
1417
|
+
"3. Webhooks: subscribe to messages, URL: https://<host>/webhook/instagram",
|
|
1418
|
+
"4. Copy Page Access Token from Graph API Explorer (pages_messaging scope)",
|
|
1419
|
+
" 🔗 developers.facebook.com"
|
|
1420
|
+
],
|
|
1421
|
+
extraFields: [{
|
|
1422
|
+
name: "instagramAccountId",
|
|
1423
|
+
label: "Instagram Business Account ID",
|
|
1424
|
+
required: true
|
|
1425
|
+
}, {
|
|
1426
|
+
name: "verifyToken",
|
|
1427
|
+
label: "Webhook Verify Token (any string)",
|
|
1428
|
+
required: true
|
|
1429
|
+
}],
|
|
1430
|
+
notes: "Requires Instagram Business + Meta App",
|
|
1431
|
+
defaultDMPolicy: "pairing"
|
|
1432
|
+
},
|
|
1433
|
+
{
|
|
1434
|
+
id: "zalo-personal",
|
|
1435
|
+
name: "Zalo Personal",
|
|
1436
|
+
emoji: "🔵",
|
|
1437
|
+
requiresGateway: true,
|
|
1438
|
+
supportsDM: true,
|
|
1439
|
+
platforms: ["all"],
|
|
1440
|
+
tokenLabel: "Zalo cookie token",
|
|
1441
|
+
tokenHint: "Extracted from browser session",
|
|
1442
|
+
setupSteps: [
|
|
1443
|
+
"1. Open Zalo Web in browser (chat.zalo.me)",
|
|
1444
|
+
"2. Open DevTools → Application → Cookies → find zpw_sek or _zlang",
|
|
1445
|
+
"3. Copy the session cookie value",
|
|
1446
|
+
" ⚠ Unofficial API — may break on Zalo updates. Use at your own risk."
|
|
1447
|
+
],
|
|
1448
|
+
notes: "Unofficial — uses Zalo Personal API. May break on updates.",
|
|
1449
|
+
defaultDMPolicy: "pairing"
|
|
1450
|
+
},
|
|
1451
|
+
{
|
|
1452
|
+
id: "voice-call",
|
|
1453
|
+
name: "Voice Call",
|
|
1454
|
+
emoji: "🎙️",
|
|
1455
|
+
requiresGateway: true,
|
|
1456
|
+
supportsDM: false,
|
|
1457
|
+
platforms: ["all"],
|
|
1458
|
+
tokenLabel: "Not required",
|
|
1459
|
+
setupSteps: [
|
|
1460
|
+
"1. No external account needed",
|
|
1461
|
+
"2. Requires microphone + ElevenLabs API key for TTS (optional)",
|
|
1462
|
+
"3. Start with: hyperclaw voice-call",
|
|
1463
|
+
" 💡 Works in terminal — voice input → agent → voice output"
|
|
1464
|
+
],
|
|
1465
|
+
notes: "Terminal voice session — hyperclaw voice-call",
|
|
1466
|
+
defaultDMPolicy: "none"
|
|
1467
|
+
},
|
|
1468
|
+
{
|
|
1469
|
+
id: "web",
|
|
1470
|
+
name: "WebChat UI",
|
|
1471
|
+
emoji: "🌐",
|
|
1472
|
+
requiresGateway: true,
|
|
1473
|
+
supportsDM: false,
|
|
1474
|
+
platforms: ["all"],
|
|
1475
|
+
setupSteps: [
|
|
1476
|
+
"1. Built-in — no setup needed",
|
|
1477
|
+
"2. Start gateway: hyperclaw gateway",
|
|
1478
|
+
"3. Open: http://localhost:<port>/dashboard",
|
|
1479
|
+
" 💡 Works in any browser on your local network"
|
|
1480
|
+
],
|
|
1481
|
+
notes: "Built-in WebChat at http://localhost:<port>",
|
|
1482
|
+
defaultDMPolicy: "none"
|
|
1483
|
+
},
|
|
1484
|
+
{
|
|
1485
|
+
id: "cli",
|
|
1486
|
+
name: "CLI / Terminal",
|
|
1487
|
+
emoji: "🖥️",
|
|
1488
|
+
requiresGateway: false,
|
|
1489
|
+
supportsDM: false,
|
|
1490
|
+
platforms: ["all"],
|
|
1491
|
+
setupSteps: [
|
|
1492
|
+
"1. Always active — no setup needed",
|
|
1493
|
+
"2. Use: hyperclaw chat or hyperclaw agent --message \"...\"",
|
|
1494
|
+
" 💡 Works offline without any channel configured"
|
|
1495
|
+
],
|
|
1496
|
+
notes: "Always active — hyperclaw chat",
|
|
1497
|
+
defaultDMPolicy: "none"
|
|
1498
|
+
},
|
|
1499
|
+
{
|
|
1500
|
+
id: "chrome-extension",
|
|
1501
|
+
name: "Chrome Extension",
|
|
1502
|
+
emoji: "🔌",
|
|
1503
|
+
requiresGateway: true,
|
|
1504
|
+
supportsDM: false,
|
|
1505
|
+
platforms: ["all"],
|
|
1506
|
+
setupSteps: [
|
|
1507
|
+
"1. Open Chrome → chrome://extensions → Enable Developer mode",
|
|
1508
|
+
"2. Load unpacked → select: extensions/chrome-extension/",
|
|
1509
|
+
"3. Extension connects to gateway via WebSocket automatically",
|
|
1510
|
+
" 💡 Gives the agent access to your browser context"
|
|
1511
|
+
],
|
|
1512
|
+
notes: "Load extensions/chrome-extension/ as unpacked extension in Chrome",
|
|
1513
|
+
defaultDMPolicy: "none"
|
|
1514
|
+
},
|
|
1515
|
+
{
|
|
1516
|
+
id: "synology-chat",
|
|
1517
|
+
name: "Synology Chat",
|
|
1518
|
+
emoji: "🖥️",
|
|
1519
|
+
requiresGateway: true,
|
|
1520
|
+
supportsDM: true,
|
|
1521
|
+
platforms: ["all"],
|
|
1522
|
+
tokenLabel: "Incoming Webhook URL",
|
|
1523
|
+
tokenHint: "DSM → Synology Chat → Integration → Incoming Webhooks",
|
|
1524
|
+
setupSteps: [
|
|
1525
|
+
"1. Open Synology Chat (DSM package)",
|
|
1526
|
+
"2. Top-right menu → Integration → Incoming Webhooks → Create",
|
|
1527
|
+
"3. Copy the Webhook URL (used to POST messages from HyperClaw)",
|
|
1528
|
+
"4. Outgoing Webhook: Integration → Outgoing Webhooks → Create",
|
|
1529
|
+
" URL: http://<your-server>:7789/synology-hook",
|
|
1530
|
+
" Method: POST",
|
|
1531
|
+
" 🔗 kb.synology.com/synologychat"
|
|
1532
|
+
],
|
|
1533
|
+
extraFields: [{
|
|
1534
|
+
name: "webhookPort",
|
|
1535
|
+
label: "Outgoing webhook port",
|
|
1536
|
+
hint: "7789",
|
|
1537
|
+
required: false
|
|
1538
|
+
}, {
|
|
1539
|
+
name: "webhookToken",
|
|
1540
|
+
label: "Outgoing webhook token (optional HMAC)",
|
|
1541
|
+
required: false
|
|
1542
|
+
}],
|
|
1543
|
+
notes: "Requires Synology Chat installed on your Synology NAS (DSM 7+)",
|
|
1544
|
+
defaultDMPolicy: "pairing"
|
|
1545
|
+
},
|
|
1546
|
+
{
|
|
1547
|
+
id: "tlon",
|
|
1548
|
+
name: "Tlon (Urbit)",
|
|
1549
|
+
emoji: "🪐",
|
|
1550
|
+
requiresGateway: true,
|
|
1551
|
+
supportsDM: true,
|
|
1552
|
+
platforms: ["all"],
|
|
1553
|
+
tokenLabel: "Urbit login code",
|
|
1554
|
+
tokenHint: "From your Urbit ship: +code",
|
|
1555
|
+
setupSteps: [
|
|
1556
|
+
"1. Start your Urbit ship (e.g. via Port or urbit binary)",
|
|
1557
|
+
"2. In the Dojo: +code → copy the code",
|
|
1558
|
+
"3. Note your ship name (e.g. ~sampel-palnet) and ship URL",
|
|
1559
|
+
"4. Optionally configure a group: ~sampel-palnet/my-group",
|
|
1560
|
+
" 🔗 tlon.io · urbit.org/getting-started"
|
|
1561
|
+
],
|
|
1562
|
+
extraFields: [
|
|
1563
|
+
{
|
|
1564
|
+
name: "shipUrl",
|
|
1565
|
+
label: "Ship URL",
|
|
1566
|
+
hint: "http://localhost:8080",
|
|
1567
|
+
required: true
|
|
1568
|
+
},
|
|
1569
|
+
{
|
|
1570
|
+
name: "ship",
|
|
1571
|
+
label: "Ship name",
|
|
1572
|
+
hint: "~sampel-palnet",
|
|
1573
|
+
required: true
|
|
1574
|
+
},
|
|
1575
|
+
{
|
|
1576
|
+
name: "group",
|
|
1577
|
+
label: "Group path (optional)",
|
|
1578
|
+
hint: "~sampel-palnet/my-group",
|
|
1579
|
+
required: false
|
|
1580
|
+
}
|
|
1581
|
+
],
|
|
1582
|
+
notes: "Requires a running Urbit ship with Tlon/Groups installed",
|
|
1583
|
+
defaultDMPolicy: "pairing"
|
|
1584
|
+
},
|
|
1585
|
+
{
|
|
1586
|
+
id: "twitch",
|
|
1587
|
+
name: "Twitch",
|
|
1588
|
+
emoji: "🟣",
|
|
1589
|
+
requiresGateway: true,
|
|
1590
|
+
supportsDM: true,
|
|
1591
|
+
platforms: ["all"],
|
|
1592
|
+
tokenLabel: "OAuth token (oauth:xxxxxxx)",
|
|
1593
|
+
tokenHint: "twitchapps.com/tmi → connect with your bot account",
|
|
1594
|
+
setupSteps: [
|
|
1595
|
+
"1. Create a Twitch bot account (or use your own)",
|
|
1596
|
+
"2. Go to: twitchapps.com/tmi → Connect → copy the OAuth token",
|
|
1597
|
+
"3. Token format: oauth:xxxxxxxxxxxxxxxxxxxxxx",
|
|
1598
|
+
"4. Bot username = the Twitch account you logged in with",
|
|
1599
|
+
" 💡 To receive commands, users type: !<message>",
|
|
1600
|
+
" 💡 Moderators and the broadcaster bypass the allowlist by default",
|
|
1601
|
+
" 🔗 twitchapps.com/tmi"
|
|
1602
|
+
],
|
|
1603
|
+
extraFields: [
|
|
1604
|
+
{
|
|
1605
|
+
name: "username",
|
|
1606
|
+
label: "Bot Twitch username (lowercase)",
|
|
1607
|
+
required: true
|
|
1608
|
+
},
|
|
1609
|
+
{
|
|
1610
|
+
name: "channels",
|
|
1611
|
+
label: "Channel(s) to join (comma-separated)",
|
|
1612
|
+
hint: "mychannel or mychannel,otherchannel",
|
|
1613
|
+
required: true
|
|
1614
|
+
},
|
|
1615
|
+
{
|
|
1616
|
+
name: "commandPrefix",
|
|
1617
|
+
label: "Command prefix",
|
|
1618
|
+
hint: "! (default)",
|
|
1619
|
+
required: false
|
|
1620
|
+
}
|
|
1621
|
+
],
|
|
1622
|
+
notes: "Chat-based; uses Twitch IRC over WebSocket. Command prefix required (default: !)",
|
|
1623
|
+
defaultDMPolicy: "pairing"
|
|
1624
|
+
}
|
|
1625
|
+
];
|
|
1626
|
+
async function getChannelStatus(def, configuredIds) {
|
|
1627
|
+
const platform = os.default.platform();
|
|
1628
|
+
if (!def.platforms.includes("all") && !def.platforms.includes(platform)) return "unavailable";
|
|
1629
|
+
if (configuredIds.includes(def.id)) return "configured";
|
|
1630
|
+
if (["telegram", "discord"].includes(def.id)) return "recommended";
|
|
1631
|
+
return "available";
|
|
1632
|
+
}
|
|
1633
|
+
/** Human-readable reason why a channel is unavailable on this OS */
|
|
1634
|
+
function unavailableReason(def) {
|
|
1635
|
+
const platform = os.default.platform();
|
|
1636
|
+
if (def.platforms.length === 1 && def.platforms[0] === "darwin") return "macOS only";
|
|
1637
|
+
if (def.platforms.includes("linux") && def.platforms.includes("darwin") && !def.platforms.includes("win32")) return "Linux/macOS only";
|
|
1638
|
+
if (!def.platforms.includes("all") && !def.platforms.includes(platform)) return `not supported on ${platform}`;
|
|
1639
|
+
return "unavailable on this OS";
|
|
1640
|
+
}
|
|
1641
|
+
function statusBadge(status, def) {
|
|
1642
|
+
switch (status) {
|
|
1643
|
+
case "configured": return chalk.default.green("[configured]");
|
|
1644
|
+
case "recommended": return chalk.default.cyan("[recommended]");
|
|
1645
|
+
case "available": return chalk.default.gray("[available]");
|
|
1646
|
+
case "unavailable": {
|
|
1647
|
+
const reason = def ? unavailableReason(def) : "unavailable";
|
|
1648
|
+
return chalk.default.red(`[${reason}]`);
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1652
|
+
const CHANNELS = CHANNEL_DEFS;
|
|
1653
|
+
async function getAvailableChannels(configuredIds = []) {
|
|
1654
|
+
const result = [];
|
|
1655
|
+
for (const def of CHANNEL_DEFS) {
|
|
1656
|
+
const status = await getChannelStatus(def, configuredIds);
|
|
1657
|
+
result.push({
|
|
1658
|
+
...def,
|
|
1659
|
+
status
|
|
1660
|
+
});
|
|
1661
|
+
}
|
|
1662
|
+
return result;
|
|
1663
|
+
}
|
|
1664
|
+
|
|
1665
|
+
//#endregion
|
|
1666
|
+
//#region src/cli/memory.ts
|
|
1667
|
+
const HC_DIR = path.default.join(os.default.homedir(), ".hyperclaw");
|
|
1668
|
+
const AGENTS_FILE = path.default.join(HC_DIR, "AGENTS.md");
|
|
1669
|
+
const MEMORY_FILE = path.default.join(HC_DIR, "MEMORY.md");
|
|
1670
|
+
const SOUL_FILE = path.default.join(HC_DIR, "SOUL.md");
|
|
1671
|
+
const LOG_DIR = path.default.join(HC_DIR, "logs");
|
|
1672
|
+
var MemoryManager = class {
|
|
1673
|
+
async init(opts = {}) {
|
|
1674
|
+
await fs_extra.default.ensureDir(HC_DIR);
|
|
1675
|
+
await fs_extra.default.ensureDir(LOG_DIR);
|
|
1676
|
+
const agentName = opts.agentName || "HyperClaw";
|
|
1677
|
+
const userName = opts.userName || os.default.userInfo().username;
|
|
1678
|
+
const lang = opts.language || "English";
|
|
1679
|
+
const personality = opts.personality || "Direct and efficient, helpful without being sycophantic, honest about uncertainty";
|
|
1680
|
+
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
|
|
1681
|
+
2. Be concise unless detail is explicitly requested
|
|
1682
|
+
3. Never share API keys, tokens, or secrets in responses
|
|
1683
|
+
4. If unsure, ask before acting — especially for destructive operations
|
|
1684
|
+
5. Log all PC access actions to ~/.hyperclaw/pc-access.log`;
|
|
1685
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
1686
|
+
if (!await fs_extra.default.pathExists(AGENTS_FILE)) await fs_extra.default.writeFile(AGENTS_FILE, `# AGENTS.md — Global Rules
|
|
1687
|
+
> All sessions and subagents must follow these rules.
|
|
1688
|
+
|
|
1689
|
+
## Identity
|
|
1690
|
+
- Agent name: ${agentName}
|
|
1691
|
+
- User name: ${userName}
|
|
1692
|
+
- Primary language: ${lang}
|
|
1693
|
+
- Created: ${today}
|
|
1694
|
+
|
|
1695
|
+
## Behavior Rules
|
|
1696
|
+
${rules}
|
|
1697
|
+
|
|
1698
|
+
## DM Policy Default
|
|
1699
|
+
- Require pairing before responding to unknown senders
|
|
1700
|
+
- Allowlist: (add trusted IDs here)
|
|
1701
|
+
|
|
1702
|
+
## Hierarchy
|
|
1703
|
+
- SOUL.md: persona and values (read-only by subagents)
|
|
1704
|
+
- AGENTS.md: operational rules (this file)
|
|
1705
|
+
- MEMORY.md: accumulated facts about the user
|
|
1706
|
+
`);
|
|
1707
|
+
if (!await fs_extra.default.pathExists(MEMORY_FILE)) await fs_extra.default.writeFile(MEMORY_FILE, `# MEMORY.md — User Context
|
|
1708
|
+
> Automatically updated by HyperClaw after each session.
|
|
1709
|
+
|
|
1710
|
+
## User Profile
|
|
1711
|
+
- Name: ${userName}
|
|
1712
|
+
- Language: ${lang}
|
|
1713
|
+
- Initialized: ${today}
|
|
1714
|
+
|
|
1715
|
+
## Notes
|
|
1716
|
+
(auto-populated from conversations)
|
|
1717
|
+
`);
|
|
1718
|
+
if (!await fs_extra.default.pathExists(SOUL_FILE)) await fs_extra.default.writeFile(SOUL_FILE, `# SOUL.md — Agent Persona
|
|
1719
|
+
> Who I am and how I behave.
|
|
1720
|
+
|
|
1721
|
+
## Name
|
|
1722
|
+
${agentName}
|
|
1723
|
+
|
|
1724
|
+
## Personality
|
|
1725
|
+
- ${personality.replace(/\n/g, "\n- ")}
|
|
1726
|
+
|
|
1727
|
+
## Values
|
|
1728
|
+
- User autonomy first
|
|
1729
|
+
- Do no harm
|
|
1730
|
+
- Transparency about capabilities and limits
|
|
1731
|
+
|
|
1732
|
+
## Wake phrase
|
|
1733
|
+
${opts.wakeWord ? opts.wakeWord : `Wake up, ${agentName}! Your user ${userName} needs you.`}
|
|
1734
|
+
`);
|
|
1735
|
+
const logFile = path.default.join(LOG_DIR, `${today}.md`);
|
|
1736
|
+
if (!await fs_extra.default.pathExists(logFile)) await fs_extra.default.writeFile(logFile, `# Session Log — ${today}\n\n`);
|
|
1737
|
+
}
|
|
1738
|
+
async load() {
|
|
1739
|
+
if (!await fs_extra.default.pathExists(AGENTS_FILE)) return null;
|
|
1740
|
+
const agents = await fs_extra.default.readFile(AGENTS_FILE, "utf8").catch(() => "");
|
|
1741
|
+
const memory = await fs_extra.default.readFile(MEMORY_FILE, "utf8").catch(() => "");
|
|
1742
|
+
const soul = await fs_extra.default.readFile(SOUL_FILE, "utf8").catch(() => void 0);
|
|
1743
|
+
return {
|
|
1744
|
+
agents,
|
|
1745
|
+
memory,
|
|
1746
|
+
soul
|
|
1747
|
+
};
|
|
1748
|
+
}
|
|
1749
|
+
async appendRule(rule) {
|
|
1750
|
+
await fs_extra.default.ensureDir(HC_DIR);
|
|
1751
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
1752
|
+
const line = `\n- ${today}: ${rule}\n`;
|
|
1753
|
+
await fs_extra.default.appendFile(AGENTS_FILE, line);
|
|
1754
|
+
console.log(chalk.default.green(` ✅ Rule added to AGENTS.md: ${rule}`));
|
|
1755
|
+
}
|
|
1756
|
+
async addMemory(fact) {
|
|
1757
|
+
await fs_extra.default.ensureDir(HC_DIR);
|
|
1758
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
1759
|
+
await fs_extra.default.appendFile(MEMORY_FILE, `\n- ${today}: ${fact}\n`);
|
|
1760
|
+
console.log(chalk.default.green(` ✅ Memory saved: ${fact}`));
|
|
1761
|
+
}
|
|
1762
|
+
async updateSoul(content) {
|
|
1763
|
+
await fs_extra.default.ensureDir(HC_DIR);
|
|
1764
|
+
await fs_extra.default.writeFile(SOUL_FILE, content);
|
|
1765
|
+
console.log(chalk.default.green(" ✅ SOUL.md updated"));
|
|
1766
|
+
}
|
|
1767
|
+
async show() {
|
|
1768
|
+
const data = await this.load();
|
|
1769
|
+
if (!data) {
|
|
1770
|
+
console.log(chalk.default.yellow("\n No memory initialized. Run: hyperclaw init\n"));
|
|
1771
|
+
return;
|
|
1772
|
+
}
|
|
1773
|
+
console.log(chalk.default.bold.cyan("\n 🧠 MEMORY\n"));
|
|
1774
|
+
for (const [label, content] of [
|
|
1775
|
+
["SOUL.md", data.soul],
|
|
1776
|
+
["AGENTS.md", data.agents],
|
|
1777
|
+
["MEMORY.md", data.memory]
|
|
1778
|
+
]) {
|
|
1779
|
+
if (!content) continue;
|
|
1780
|
+
console.log(chalk.default.bold.white(` ── ${label} ──`));
|
|
1781
|
+
const lines = content.split("\n").slice(0, 20);
|
|
1782
|
+
for (const line of lines) if (line.startsWith("#")) console.log(chalk.default.cyan(` ${line}`));
|
|
1783
|
+
else if (line.startsWith("-")) console.log(chalk.default.gray(` ${line}`));
|
|
1784
|
+
else console.log(` ${line}`);
|
|
1785
|
+
console.log();
|
|
1786
|
+
}
|
|
1787
|
+
}
|
|
1788
|
+
async runPersonaBootstrap() {
|
|
1789
|
+
console.log(chalk.default.bold.cyan("\n 🌅 Wake up, my friend!\n"));
|
|
1790
|
+
const { agentName } = await inquirer.default.prompt([{
|
|
1791
|
+
type: "input",
|
|
1792
|
+
name: "agentName",
|
|
1793
|
+
message: "What should I call myself? (agent name)",
|
|
1794
|
+
default: "HyperClaw"
|
|
1795
|
+
}]);
|
|
1796
|
+
const { userName } = await inquirer.default.prompt([{
|
|
1797
|
+
type: "input",
|
|
1798
|
+
name: "userName",
|
|
1799
|
+
message: `What shall ${agentName} call you?`,
|
|
1800
|
+
default: os.default.userInfo().username
|
|
1801
|
+
}]);
|
|
1802
|
+
console.log(chalk.default.green(`\n ✨ I am ${agentName}. Hello, ${userName}.\n`));
|
|
1803
|
+
return {
|
|
1804
|
+
agentName,
|
|
1805
|
+
userName
|
|
1806
|
+
};
|
|
1807
|
+
}
|
|
1808
|
+
async getContextForAI() {
|
|
1809
|
+
let context = "";
|
|
1810
|
+
for (const [label, file] of [
|
|
1811
|
+
["SOUL", SOUL_FILE],
|
|
1812
|
+
["AGENTS", AGENTS_FILE],
|
|
1813
|
+
["MEMORY", MEMORY_FILE]
|
|
1814
|
+
]) if (await fs_extra.default.pathExists(file)) {
|
|
1815
|
+
const content = await fs_extra.default.readFile(file, "utf8");
|
|
1816
|
+
context += `## ${label}.md\n${content}\n\n`;
|
|
1817
|
+
}
|
|
1818
|
+
return context;
|
|
1819
|
+
}
|
|
1820
|
+
async search(query) {
|
|
1821
|
+
const data = await this.load();
|
|
1822
|
+
if (!data) {
|
|
1823
|
+
console.log(chalk.default.gray(" No memory\n"));
|
|
1824
|
+
return;
|
|
1825
|
+
}
|
|
1826
|
+
const allText = [
|
|
1827
|
+
data.agents,
|
|
1828
|
+
data.memory,
|
|
1829
|
+
data.soul || ""
|
|
1830
|
+
].join("\n");
|
|
1831
|
+
const lines = allText.split("\n").filter((l) => l.toLowerCase().includes(query.toLowerCase()));
|
|
1832
|
+
if (lines.length === 0) {
|
|
1833
|
+
console.log(chalk.default.gray(` Nothing found for "${query}"\n`));
|
|
1834
|
+
return;
|
|
1835
|
+
}
|
|
1836
|
+
console.log(chalk.default.bold.cyan(`\n 🔍 "${query}"\n`));
|
|
1837
|
+
lines.forEach((l) => console.log(` ${l}`));
|
|
1838
|
+
console.log();
|
|
1839
|
+
}
|
|
1840
|
+
async clear(file = "memory") {
|
|
1841
|
+
const { confirm } = await inquirer.default.prompt([{
|
|
1842
|
+
type: "confirm",
|
|
1843
|
+
name: "confirm",
|
|
1844
|
+
message: `Clear ${file === "all" ? "ALL memory files" : "MEMORY.md"}?`,
|
|
1845
|
+
default: false
|
|
1846
|
+
}]);
|
|
1847
|
+
if (!confirm) return;
|
|
1848
|
+
if (file === "all") {
|
|
1849
|
+
await fs_extra.default.remove(AGENTS_FILE);
|
|
1850
|
+
await fs_extra.default.remove(MEMORY_FILE);
|
|
1851
|
+
await fs_extra.default.remove(SOUL_FILE);
|
|
1852
|
+
console.log(chalk.default.green(" ✅ All memory cleared"));
|
|
1853
|
+
} else {
|
|
1854
|
+
await fs_extra.default.writeFile(MEMORY_FILE, "# MEMORY.md\n\n");
|
|
1855
|
+
console.log(chalk.default.green(" ✅ MEMORY.md cleared"));
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1858
|
+
};
|
|
1859
|
+
|
|
1860
|
+
//#endregion
|
|
1861
|
+
//#region src/cli/security.ts
|
|
1862
|
+
async function showSecurityDisclaimer() {
|
|
1863
|
+
console.clear();
|
|
1864
|
+
console.log(chalk.default.bgRed.white.bold("\n ⚠️ SECURITY NOTICE — READ CAREFULLY ⚠️ \n"));
|
|
1865
|
+
console.log(chalk.default.white.bold(" A bad prompt can trick it into doing unsafe things\n"));
|
|
1866
|
+
console.log(chalk.default.hex("#06b6d4")(" Protections to enable:"));
|
|
1867
|
+
console.log(chalk.default.gray(" ● Pairing / allowlists — limit who can DM your agent"));
|
|
1868
|
+
console.log(chalk.default.gray(" ● Sandbox + least privilege — restrict PC access level"));
|
|
1869
|
+
console.log(chalk.default.gray(" ● Keep secrets out — never put tokens in SOUL.md"));
|
|
1870
|
+
console.log(chalk.default.gray(" ● Use strongest model — smarter = better at refusing tricks\n"));
|
|
1871
|
+
const { understood } = await inquirer.default.prompt([{
|
|
1872
|
+
type: "confirm",
|
|
1873
|
+
name: "understood",
|
|
1874
|
+
message: chalk.default.bold("I understand this is powerful and inherently risky. Continue?"),
|
|
1875
|
+
default: false
|
|
1876
|
+
}]);
|
|
1877
|
+
if (!understood) console.log(chalk.default.gray("\n Aborted. Come back when ready.\n"));
|
|
1878
|
+
return understood;
|
|
1879
|
+
}
|
|
1880
|
+
async function configureDMPolicy(channelName) {
|
|
1881
|
+
const { configureDMPolicy: cdmp } = await Promise.resolve().then(() => require("./security-EQ5o-nr4.js"));
|
|
1882
|
+
const res = await cdmp(channelName);
|
|
1883
|
+
return {
|
|
1884
|
+
dmPolicy: res.policy,
|
|
1885
|
+
allowFrom: res.allowFrom ?? []
|
|
1886
|
+
};
|
|
1887
|
+
}
|
|
1888
|
+
|
|
1889
|
+
//#endregion
|
|
1890
|
+
//#region src/terminal/banner.ts
|
|
1891
|
+
require_theme.init_theme();
|
|
1892
|
+
var Banner = class {
|
|
1893
|
+
async showNeonBanner(daemonMode = false) {
|
|
1894
|
+
console.clear();
|
|
1895
|
+
const t = require_theme.getTheme(daemonMode);
|
|
1896
|
+
const icon$1 = daemonMode ? "🩸" : "🦅";
|
|
1897
|
+
try {
|
|
1898
|
+
const title = figlet.default.textSync("HYPERCLAW", { font: "ANSI Shadow" });
|
|
1899
|
+
const g = (0, gradient_string.default)(t.gradient);
|
|
1900
|
+
const lines = title.split("\n");
|
|
1901
|
+
const first = lines[0] ?? "";
|
|
1902
|
+
console.log(`\n ${icon$1} ` + g(first));
|
|
1903
|
+
for (let i = 1; i < lines.length; i++) console.log(g(" " + (lines[i] ?? "")));
|
|
1904
|
+
} catch {
|
|
1905
|
+
console.log(chalk.default.bold.red(`\n ${icon$1} HYPERCLAW\n`));
|
|
1906
|
+
}
|
|
1907
|
+
const subtitle = daemonMode ? chalk.default.hex(t.daemonPrimary)(" 🩸 DAEMON MODE — ALWAYS WATCHING ⚡\n") : t.muted(" 🦅 HyperClaw Bot — AI Gateway v4.0.2 ⚡\n");
|
|
1908
|
+
console.log(subtitle);
|
|
1909
|
+
const boxOpts = {
|
|
1910
|
+
padding: 1,
|
|
1911
|
+
margin: {
|
|
1912
|
+
top: 1,
|
|
1913
|
+
bottom: 1
|
|
1914
|
+
},
|
|
1915
|
+
borderStyle: "round",
|
|
1916
|
+
borderColor: daemonMode ? t.daemonBorderColor : t.borderColor
|
|
1917
|
+
};
|
|
1918
|
+
if (t.boxBg) boxOpts.backgroundColor = t.boxBg;
|
|
1919
|
+
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);
|
|
1920
|
+
console.log(box);
|
|
1921
|
+
console.log(t.muted(" One assistant. All your channels. 🦅\n"));
|
|
1922
|
+
const { maybeShowUpdateNotice } = await Promise.resolve().then(() => require("./update-check-BQOH_-LD.js"));
|
|
1923
|
+
maybeShowUpdateNotice(daemonMode);
|
|
1924
|
+
}
|
|
1925
|
+
async showMiniBanner() {
|
|
1926
|
+
await this.showNeonBanner(false);
|
|
1927
|
+
}
|
|
1928
|
+
async showWizardBanner() {
|
|
1929
|
+
console.clear();
|
|
1930
|
+
const t = require_theme.getTheme(false);
|
|
1931
|
+
const g = (0, gradient_string.default)(t.gradient);
|
|
1932
|
+
try {
|
|
1933
|
+
const title = figlet.default.textSync("HYPERCLAW", { font: "ANSI Shadow" });
|
|
1934
|
+
const lines = title.split("\n");
|
|
1935
|
+
const first = lines[0] ?? "";
|
|
1936
|
+
console.log("\n 🦅 " + g(first));
|
|
1937
|
+
for (let i = 1; i < lines.length; i++) console.log(g(" " + (lines[i] ?? "")));
|
|
1938
|
+
} catch {
|
|
1939
|
+
console.log(t.bold("\n 🦅 HYPERCLAW\n"));
|
|
1940
|
+
}
|
|
1941
|
+
console.log(t.muted(" 🦅 HyperClaw Bot — AI Gateway • SETUP WIZARD v4.0.2 ⚡\n"));
|
|
1942
|
+
const boxOpts = {
|
|
1943
|
+
padding: 1,
|
|
1944
|
+
margin: { bottom: 1 },
|
|
1945
|
+
borderStyle: "round",
|
|
1946
|
+
borderColor: t.borderColor
|
|
1947
|
+
};
|
|
1948
|
+
if (t.boxBg) boxOpts.backgroundColor = t.boxBg;
|
|
1949
|
+
const box = (0, boxen.default)(t.a("◆") + " Provider • Channels • Gateway • Identity", boxOpts);
|
|
1950
|
+
console.log(box);
|
|
1951
|
+
}
|
|
1952
|
+
async showStatus() {
|
|
1953
|
+
const t = require_theme.getTheme(false);
|
|
1954
|
+
const { ConfigManager } = await Promise.resolve().then(() => require("./manager-BX2lIsmk.js"));
|
|
1955
|
+
const { GatewayManager: GatewayManager$1 } = await Promise.resolve().then(() => require("./manager-UY7MecrN.js"));
|
|
1956
|
+
const cfg = await new ConfigManager().load();
|
|
1957
|
+
const gm = new GatewayManager$1();
|
|
1958
|
+
const port = cfg?.gateway?.port ?? 18789;
|
|
1959
|
+
const running = await gm.isRunning(port);
|
|
1960
|
+
console.log(t.bold("\n 🦅 HYPERCLAW STATUS\n"));
|
|
1961
|
+
console.log(` Gateway: ${running ? t.success("● running") : t.error("○ stopped")} port ${port}`);
|
|
1962
|
+
console.log(` Provider: ${t.c(cfg?.provider?.providerId ?? "none")}`);
|
|
1963
|
+
console.log(` Channels: ${t.c(String((cfg?.channels ?? []).length))}`);
|
|
1964
|
+
console.log();
|
|
1965
|
+
}
|
|
1966
|
+
};
|
|
1967
|
+
|
|
1968
|
+
//#endregion
|
|
1969
|
+
//#region src/infra/channel-icons.ts
|
|
1970
|
+
const CDN = "https://cdn.simpleicons.org";
|
|
1971
|
+
function icon(slug, name, color, darkColor) {
|
|
1972
|
+
return {
|
|
1973
|
+
slug,
|
|
1974
|
+
name,
|
|
1975
|
+
color,
|
|
1976
|
+
url: `${CDN}/${slug}`,
|
|
1977
|
+
darkColor
|
|
1978
|
+
};
|
|
1979
|
+
}
|
|
1980
|
+
const CHANNEL_ICONS = {
|
|
1981
|
+
"telegram": icon("telegram", "Telegram", "26A5E4"),
|
|
1982
|
+
"discord": icon("discord", "Discord", "5865F2"),
|
|
1983
|
+
"whatsapp": icon("whatsapp", "WhatsApp", "25D366"),
|
|
1984
|
+
"whatsapp-baileys": icon("whatsapp", "WhatsApp (Baileys)", "25D366"),
|
|
1985
|
+
"slack": icon("slack", "Slack", "4A154B"),
|
|
1986
|
+
"signal": icon("signal", "Signal", "3A76F0", "FFFFFF"),
|
|
1987
|
+
"imessage": icon("imessage", "iMessage", "29CC40"),
|
|
1988
|
+
"imessage-native": icon("imessage", "iMessage (native)", "29CC40"),
|
|
1989
|
+
"matrix": icon("matrix", "Matrix", "000000", "FFFFFF"),
|
|
1990
|
+
"email": icon("gmail", "Email", "EA4335"),
|
|
1991
|
+
"feishu": icon("lark", "Feishu / Lark", "00D6B9"),
|
|
1992
|
+
"msteams": icon("microsoftteams", "Microsoft Teams", "6264A7"),
|
|
1993
|
+
"messenger": icon("messenger", "Facebook Messenger", "00B2FF"),
|
|
1994
|
+
"nostr": icon("nostr", "Nostr", "8E44AD", "C39BD3"),
|
|
1995
|
+
"line": icon("line", "LINE", "00C300"),
|
|
1996
|
+
"viber": icon("viber", "Viber", "7360F2"),
|
|
1997
|
+
"zalo": icon("zalo", "Zalo", "0068FF"),
|
|
1998
|
+
"zalo-personal": icon("zalo", "Zalo Personal", "0068FF"),
|
|
1999
|
+
"twitter": icon("x", "Twitter / X", "000000", "FFFFFF"),
|
|
2000
|
+
"irc": icon("irc", "IRC", "1A3B5C", "FFFFFF"),
|
|
2001
|
+
"mattermost": icon("mattermost", "Mattermost", "0058CC"),
|
|
2002
|
+
"nextcloud": icon("nextcloud", "Nextcloud Talk", "0082C9"),
|
|
2003
|
+
"googlechat": icon("googlechat", "Google Chat", "00897B"),
|
|
2004
|
+
"instagram": icon("instagram", "Instagram", "E4405F"),
|
|
2005
|
+
"synology-chat": icon("synology", "Synology Chat", "B5B5B6", "FFFFFF"),
|
|
2006
|
+
"tlon": icon("urbit", "Tlon (Urbit)", "000000", "FFFFFF"),
|
|
2007
|
+
"twitch": icon("twitch", "Twitch", "9146FF"),
|
|
2008
|
+
"voice-call": icon("webrtc", "Voice Call", "333333", "FFFFFF"),
|
|
2009
|
+
"web": icon("googlechrome", "WebChat", "4285F4"),
|
|
2010
|
+
"cli": icon("gnubash", "CLI / Terminal", "4EAA25", "FFFFFF"),
|
|
2011
|
+
"chrome-extension": icon("googlechrome", "Chrome Extension", "4285F4")
|
|
2012
|
+
};
|
|
2013
|
+
const PROVIDER_ICONS = {
|
|
2014
|
+
"anthropic": icon("anthropic", "Anthropic (Claude)", "D4C5A9", "191919"),
|
|
2015
|
+
"openai": icon("openai", "OpenAI", "000000", "FFFFFF"),
|
|
2016
|
+
"openrouter": icon("openrouter", "OpenRouter", "6467F2"),
|
|
2017
|
+
"groq": icon("groq", "Groq", "F55036"),
|
|
2018
|
+
"xai": icon("x", "xAI (Grok)", "000000", "FFFFFF"),
|
|
2019
|
+
"custom": icon("jsonwebtokens", "Custom OpenAI API", "A0A0A0"),
|
|
2020
|
+
"ollama": icon("ollama", "Ollama (local)", "000000", "FFFFFF"),
|
|
2021
|
+
"mistral": icon("mistral", "Mistral AI", "FF7000"),
|
|
2022
|
+
"cohere": icon("cohere", "Cohere", "39594D", "FFFFFF"),
|
|
2023
|
+
"google": icon("googlegemini", "Google Gemini", "8E75B2"),
|
|
2024
|
+
"deepseek": icon("deepseek", "DeepSeek", "4D6BFE"),
|
|
2025
|
+
"together": icon("togetherai", "Together AI", "000000", "FFFFFF")
|
|
2026
|
+
};
|
|
2027
|
+
|
|
2028
|
+
//#endregion
|
|
2029
|
+
//#region src/cli/onboard.ts
|
|
2030
|
+
/** Brand-colored ■ square — closest we can get to a real icon in a terminal */
|
|
2031
|
+
function brandIcon(id, type = "channel") {
|
|
2032
|
+
const map = type === "channel" ? CHANNEL_ICONS : PROVIDER_ICONS;
|
|
2033
|
+
const ic = map[id];
|
|
2034
|
+
if (!ic) return chalk.default.gray("■");
|
|
2035
|
+
return chalk.default.hex("#" + ic.color)("■");
|
|
2036
|
+
}
|
|
2037
|
+
var HyperClawWizard = class {
|
|
2038
|
+
config = new ConfigStore();
|
|
2039
|
+
daemon = new DaemonManager();
|
|
2040
|
+
gateway = new GatewayManager();
|
|
2041
|
+
async run(options) {
|
|
2042
|
+
const proceed = await showSecurityDisclaimer();
|
|
2043
|
+
if (!proceed) return;
|
|
2044
|
+
const { allThemes, getThemeName, setThemeName } = await Promise.resolve().then(() => require("./theme-Iefa3L63.js"));
|
|
2045
|
+
const currentTheme = getThemeName();
|
|
2046
|
+
const { chosenTheme } = await inquirer.default.prompt([{
|
|
2047
|
+
type: "list",
|
|
2048
|
+
name: "chosenTheme",
|
|
2049
|
+
message: "🎨 Choose a color theme:",
|
|
2050
|
+
default: currentTheme,
|
|
2051
|
+
choices: [
|
|
2052
|
+
{
|
|
2053
|
+
name: `${chalk.default.hex("#06b6d4")("■")} Dark Professional ${chalk.default.gray("(neon cyan on black)")}`,
|
|
2054
|
+
value: "dark"
|
|
2055
|
+
},
|
|
2056
|
+
{
|
|
2057
|
+
name: `${chalk.default.hex("#64748b")("■")} Grey Professional ${chalk.default.gray("(muted cyan, neutral)")}`,
|
|
2058
|
+
value: "grey"
|
|
2059
|
+
},
|
|
2060
|
+
{
|
|
2061
|
+
name: `${chalk.default.hex("#0284c7")("■")} White / Light ${chalk.default.gray("(deep cyan, light bg)")}`,
|
|
2062
|
+
value: "white"
|
|
2063
|
+
}
|
|
2064
|
+
]
|
|
2065
|
+
}]);
|
|
2066
|
+
if (chosenTheme !== currentTheme) await setThemeName(chosenTheme);
|
|
2067
|
+
await new Banner().showWizardBanner();
|
|
2068
|
+
const { mode } = await inquirer.default.prompt([{
|
|
2069
|
+
type: "list",
|
|
2070
|
+
name: "mode",
|
|
2071
|
+
message: "Setup mode:",
|
|
2072
|
+
choices: [{
|
|
2073
|
+
name: `${chalk.default.green("★")} QuickStart ${chalk.default.gray("(Recommended)")}`,
|
|
2074
|
+
value: "quick"
|
|
2075
|
+
}, {
|
|
2076
|
+
name: ` Manual ${chalk.default.gray("(Full control)")}`,
|
|
2077
|
+
value: "manual"
|
|
2078
|
+
}]
|
|
2079
|
+
}]);
|
|
2080
|
+
if (mode === "quick") return this.quickSetup(options);
|
|
2081
|
+
return this.fullSetup(options);
|
|
2082
|
+
}
|
|
2083
|
+
async quickstart(options) {
|
|
2084
|
+
const proceed = await showSecurityDisclaimer();
|
|
2085
|
+
if (!proceed) return;
|
|
2086
|
+
return this.quickSetup(options);
|
|
2087
|
+
}
|
|
2088
|
+
stepHeader(n, total, title) {
|
|
2089
|
+
const { getTheme: getTheme$1 } = (require_theme.init_theme(), require_chunk.__toCommonJS(require_theme.theme_exports));
|
|
2090
|
+
const t = getTheme$1(false);
|
|
2091
|
+
const bar = chalk.default.gray("─".repeat(52));
|
|
2092
|
+
console.log(`\n${bar}`);
|
|
2093
|
+
console.log(` ${t.c(`Step ${n} / ${total}`)} · ${chalk.default.bold(title)}`);
|
|
2094
|
+
console.log(`${bar}\n`);
|
|
2095
|
+
}
|
|
2096
|
+
async quickSetup(options) {
|
|
2097
|
+
const STEPS = 7;
|
|
2098
|
+
this.stepHeader(1, STEPS, "Workspace");
|
|
2099
|
+
const { workspaceName } = await inquirer.default.prompt([{
|
|
2100
|
+
type: "input",
|
|
2101
|
+
name: "workspaceName",
|
|
2102
|
+
message: "Workspace name:",
|
|
2103
|
+
default: "my-hyperclaw"
|
|
2104
|
+
}]);
|
|
2105
|
+
this.stepHeader(2, STEPS, "AI Providers & Models");
|
|
2106
|
+
const { providers: allProviders, primary: providerConfig } = await this.selectProvidersAndModels();
|
|
2107
|
+
this.stepHeader(3, STEPS, "Gateway (optional)");
|
|
2108
|
+
const gatewayConfig = await this.configureGateway(false);
|
|
2109
|
+
this.stepHeader(4, STEPS, "Channels");
|
|
2110
|
+
const channelConfigs = await this.selectChannels();
|
|
2111
|
+
this.stepHeader(5, STEPS, "Agent Identity & Persona");
|
|
2112
|
+
const identity = await this.configureIdentity();
|
|
2113
|
+
this.stepHeader(6, STEPS, "Skills & Hooks");
|
|
2114
|
+
const { hooks, heartbeat: heartbeatEnabled } = await this.configureSkillsAndHooks();
|
|
2115
|
+
this.stepHeader(7, STEPS, "Extras");
|
|
2116
|
+
const memoryIntegration = await this.configureMemoryIntegration();
|
|
2117
|
+
const serviceApiKeys = await this.configureServiceApiKeys();
|
|
2118
|
+
const hyperclawbotConfig = await this.configureHyperClawBot(gatewayConfig);
|
|
2119
|
+
const talkModeConfig = await this.configureTalkMode();
|
|
2120
|
+
await this.saveAll({
|
|
2121
|
+
workspaceName,
|
|
2122
|
+
providerConfig,
|
|
2123
|
+
providers: allProviders,
|
|
2124
|
+
channelConfigs,
|
|
2125
|
+
gatewayConfig,
|
|
2126
|
+
identity,
|
|
2127
|
+
hooks,
|
|
2128
|
+
heartbeatEnabled,
|
|
2129
|
+
memoryIntegration,
|
|
2130
|
+
serviceApiKeys,
|
|
2131
|
+
hyperclawbotConfig,
|
|
2132
|
+
talkModeConfig
|
|
2133
|
+
}, options);
|
|
2134
|
+
}
|
|
2135
|
+
async fullSetup(options) {
|
|
2136
|
+
const STEPS = 9;
|
|
2137
|
+
this.stepHeader(1, STEPS, "Workspace");
|
|
2138
|
+
const { workspaceName } = await inquirer.default.prompt([{
|
|
2139
|
+
type: "input",
|
|
2140
|
+
name: "workspaceName",
|
|
2141
|
+
message: "Workspace directory:",
|
|
2142
|
+
default: "my-hyperclaw"
|
|
2143
|
+
}]);
|
|
2144
|
+
this.stepHeader(2, STEPS, "AI Providers & Models");
|
|
2145
|
+
const { providers: allProviders, primary: providerConfig } = await this.selectProvidersAndModels();
|
|
2146
|
+
this.stepHeader(3, STEPS, "Gateway");
|
|
2147
|
+
const gatewayConfig = await this.configureGateway(true);
|
|
2148
|
+
this.stepHeader(4, STEPS, "Channels");
|
|
2149
|
+
const { configureChannels } = await inquirer.default.prompt([{
|
|
2150
|
+
type: "confirm",
|
|
2151
|
+
name: "configureChannels",
|
|
2152
|
+
message: "Configure chat channels now?",
|
|
2153
|
+
default: true
|
|
2154
|
+
}]);
|
|
2155
|
+
const channelConfigs = configureChannels ? await this.selectChannels(true) : {};
|
|
2156
|
+
if (configureChannels) {
|
|
2157
|
+
const { configureDM } = await inquirer.default.prompt([{
|
|
2158
|
+
type: "confirm",
|
|
2159
|
+
name: "configureDM",
|
|
2160
|
+
message: "Configure DM access policies now?",
|
|
2161
|
+
default: true
|
|
2162
|
+
}]);
|
|
2163
|
+
if (configureDM) for (const [channelId] of Object.entries(channelConfigs)) {
|
|
2164
|
+
const ch = CHANNELS.find((c) => c.id === channelId);
|
|
2165
|
+
if (ch?.supportsDM) {
|
|
2166
|
+
const dm = await configureDMPolicy(ch.name);
|
|
2167
|
+
channelConfigs[channelId].dmPolicy = dm;
|
|
2168
|
+
}
|
|
2169
|
+
}
|
|
2170
|
+
}
|
|
2171
|
+
this.stepHeader(5, STEPS, "Agent Identity & Persona");
|
|
2172
|
+
const identity = await this.configureIdentity();
|
|
2173
|
+
this.stepHeader(6, STEPS, "Skills & Hooks");
|
|
2174
|
+
const { hooks, heartbeat: heartbeatEnabled } = await this.configureSkillsAndHooks();
|
|
2175
|
+
this.stepHeader(7, STEPS, "Services & Daemon");
|
|
2176
|
+
let installDaemon = options.installDaemon ?? false;
|
|
2177
|
+
if (!installDaemon) {
|
|
2178
|
+
console.log(chalk.default.gray(" The daemon runs the gateway as a background service that starts on boot.\n"));
|
|
2179
|
+
const ans = await inquirer.default.prompt([{
|
|
2180
|
+
type: "confirm",
|
|
2181
|
+
name: "installDaemon",
|
|
2182
|
+
message: "Install as system daemon (auto-start on boot)?",
|
|
2183
|
+
default: false
|
|
2184
|
+
}]);
|
|
2185
|
+
installDaemon = ans.installDaemon;
|
|
2186
|
+
} else console.log(chalk.default.green(" ✔ Daemon will be installed automatically (--install-daemon)\n"));
|
|
2187
|
+
this.stepHeader(8, STEPS, "Extras");
|
|
2188
|
+
const memoryIntegration = await this.configureMemoryIntegration();
|
|
2189
|
+
const serviceApiKeys = await this.configureServiceApiKeys();
|
|
2190
|
+
const hyperclawbotConfig = await this.configureHyperClawBot(gatewayConfig);
|
|
2191
|
+
const talkModeConfig = await this.configureTalkMode();
|
|
2192
|
+
this.stepHeader(9, STEPS, "Launch");
|
|
2193
|
+
const launchChoices = [{
|
|
2194
|
+
name: `TUI — terminal dashboard`,
|
|
2195
|
+
value: "tui"
|
|
2196
|
+
}];
|
|
2197
|
+
if (gatewayConfig && !gatewayConfig.remote) launchChoices.push({
|
|
2198
|
+
name: `Web — browser at http://localhost:${gatewayConfig.port}`,
|
|
2199
|
+
value: "web"
|
|
2200
|
+
});
|
|
2201
|
+
launchChoices.push({
|
|
2202
|
+
name: chalk.default.gray("Do this later — I will start manually"),
|
|
2203
|
+
value: "skip"
|
|
2204
|
+
});
|
|
2205
|
+
const { hatchMode } = await inquirer.default.prompt([{
|
|
2206
|
+
type: "list",
|
|
2207
|
+
name: "hatchMode",
|
|
2208
|
+
message: "How do you want to hatch your bot?",
|
|
2209
|
+
choices: launchChoices
|
|
2210
|
+
}]);
|
|
2211
|
+
await this.saveAll({
|
|
2212
|
+
workspaceName,
|
|
2213
|
+
providerConfig,
|
|
2214
|
+
providers: allProviders,
|
|
2215
|
+
channelConfigs,
|
|
2216
|
+
gatewayConfig: gatewayConfig ? {
|
|
2217
|
+
...gatewayConfig,
|
|
2218
|
+
hooks: hooks.length > 0
|
|
2219
|
+
} : null,
|
|
2220
|
+
identity,
|
|
2221
|
+
hooks,
|
|
2222
|
+
heartbeatEnabled,
|
|
2223
|
+
installDaemon,
|
|
2224
|
+
hatchMode,
|
|
2225
|
+
memoryIntegration,
|
|
2226
|
+
serviceApiKeys,
|
|
2227
|
+
hyperclawbotConfig,
|
|
2228
|
+
talkModeConfig
|
|
2229
|
+
}, options);
|
|
2230
|
+
}
|
|
2231
|
+
async selectProvidersAndModels() {
|
|
2232
|
+
const { getTheme: getTheme$1 } = (require_theme.init_theme(), require_chunk.__toCommonJS(require_theme.theme_exports));
|
|
2233
|
+
const t = getTheme$1(false);
|
|
2234
|
+
console.log(chalk.default.gray(" Select one or more AI providers. The first one will be primary.\n"));
|
|
2235
|
+
const { selectedProviderIds } = await inquirer.default.prompt([{
|
|
2236
|
+
type: "checkbox",
|
|
2237
|
+
name: "selectedProviderIds",
|
|
2238
|
+
message: "Select AI providers:",
|
|
2239
|
+
validate: (v) => v.length > 0 || "Select at least one provider",
|
|
2240
|
+
choices: PROVIDERS.map((p) => ({
|
|
2241
|
+
name: `${brandIcon(p.id, "provider")} ${p.displayName.replace(/^.{1,2}\s/, "").padEnd(18)}${p.supportsTranscription ? chalk.default.gray(" 🎤") : ""}`,
|
|
2242
|
+
value: p.id,
|
|
2243
|
+
checked: p.id === "openrouter"
|
|
2244
|
+
}))
|
|
2245
|
+
}]);
|
|
2246
|
+
const configured = [];
|
|
2247
|
+
for (let i = 0; i < selectedProviderIds.length; i++) {
|
|
2248
|
+
const pid = selectedProviderIds[i];
|
|
2249
|
+
const provider = getProvider(pid);
|
|
2250
|
+
const isPrimary = i === 0;
|
|
2251
|
+
console.log(`\n ${t.c("▸")} ${chalk.default.bold(provider.displayName)} ${isPrimary ? chalk.default.green("(primary)") : chalk.default.gray(`(#${i + 1})`)}\n`);
|
|
2252
|
+
if (provider.authHint) console.log(chalk.default.gray(` 💡 ${provider.authHint}\n`));
|
|
2253
|
+
let apiKey = "";
|
|
2254
|
+
let baseUrl;
|
|
2255
|
+
let modelId = "";
|
|
2256
|
+
if (provider.authType === "api_key") if (pid === "custom") {
|
|
2257
|
+
const r = await inquirer.default.prompt([
|
|
2258
|
+
{
|
|
2259
|
+
type: "input",
|
|
2260
|
+
name: "baseUrl",
|
|
2261
|
+
message: " Base URL:",
|
|
2262
|
+
validate: (v) => v.trim().length > 5 || "Required"
|
|
2263
|
+
},
|
|
2264
|
+
{
|
|
2265
|
+
type: "password",
|
|
2266
|
+
name: "apiKey",
|
|
2267
|
+
message: ` ${provider.authLabel}:`,
|
|
2268
|
+
mask: "●",
|
|
2269
|
+
validate: (v) => v.trim().length > 5 || "Required"
|
|
2270
|
+
},
|
|
2271
|
+
{
|
|
2272
|
+
type: "input",
|
|
2273
|
+
name: "modelId",
|
|
2274
|
+
message: " Model ID:",
|
|
2275
|
+
validate: (v) => v.trim().length > 0 || "Required"
|
|
2276
|
+
}
|
|
2277
|
+
]);
|
|
2278
|
+
baseUrl = r.baseUrl.trim().replace(/\/$/, "");
|
|
2279
|
+
apiKey = r.apiKey;
|
|
2280
|
+
modelId = r.modelId.trim();
|
|
2281
|
+
} else {
|
|
2282
|
+
const r = await inquirer.default.prompt([{
|
|
2283
|
+
type: "password",
|
|
2284
|
+
name: "apiKey",
|
|
2285
|
+
message: ` ${provider.authLabel}:`,
|
|
2286
|
+
mask: "●"
|
|
2287
|
+
}]);
|
|
2288
|
+
apiKey = r.apiKey;
|
|
2289
|
+
}
|
|
2290
|
+
if (!modelId) {
|
|
2291
|
+
const modelChoices = provider.models.filter((m) => m.id !== "__manual__").length ? [...provider.models.filter((m) => m.id !== "__manual__").map((m) => ({
|
|
2292
|
+
name: formatModel(m),
|
|
2293
|
+
value: m.id
|
|
2294
|
+
})), {
|
|
2295
|
+
name: chalk.default.gray("Enter manually..."),
|
|
2296
|
+
value: "__manual__"
|
|
2297
|
+
}] : [{
|
|
2298
|
+
name: chalk.default.gray("Enter model ID manually"),
|
|
2299
|
+
value: "__manual__"
|
|
2300
|
+
}];
|
|
2301
|
+
const { modelChoice } = await inquirer.default.prompt([{
|
|
2302
|
+
type: "list",
|
|
2303
|
+
name: "modelChoice",
|
|
2304
|
+
message: " Default model:",
|
|
2305
|
+
choices: modelChoices
|
|
2306
|
+
}]);
|
|
2307
|
+
if (modelChoice === "__manual__") {
|
|
2308
|
+
const { manual } = await inquirer.default.prompt([{
|
|
2309
|
+
type: "input",
|
|
2310
|
+
name: "manual",
|
|
2311
|
+
message: " Model ID:",
|
|
2312
|
+
default: provider.models[0]?.id || ""
|
|
2313
|
+
}]);
|
|
2314
|
+
modelId = manual;
|
|
2315
|
+
} else modelId = modelChoice;
|
|
2316
|
+
}
|
|
2317
|
+
configured.push({
|
|
2318
|
+
providerId: pid,
|
|
2319
|
+
apiKey,
|
|
2320
|
+
modelId,
|
|
2321
|
+
...baseUrl ? { baseUrl } : {}
|
|
2322
|
+
});
|
|
2323
|
+
console.log(t.c(` ✔ ${provider.displayName} → ${modelId}\n`));
|
|
2324
|
+
}
|
|
2325
|
+
return {
|
|
2326
|
+
providers: configured,
|
|
2327
|
+
primary: configured[0]
|
|
2328
|
+
};
|
|
2329
|
+
}
|
|
2330
|
+
async configureGateway(full = false) {
|
|
2331
|
+
const { gatewayMode } = await inquirer.default.prompt([{
|
|
2332
|
+
type: "list",
|
|
2333
|
+
name: "gatewayMode",
|
|
2334
|
+
message: "Gateway setup:",
|
|
2335
|
+
choices: [
|
|
2336
|
+
{
|
|
2337
|
+
name: `Local gateway ${chalk.default.gray("(run on this machine)")}`,
|
|
2338
|
+
value: "local"
|
|
2339
|
+
},
|
|
2340
|
+
{
|
|
2341
|
+
name: `Remote gateway ${chalk.default.gray("(info-only — connect to existing server)")}`,
|
|
2342
|
+
value: "remote"
|
|
2343
|
+
},
|
|
2344
|
+
{
|
|
2345
|
+
name: chalk.default.gray("Skip (no gateway — CLI-only mode)"),
|
|
2346
|
+
value: "skip"
|
|
2347
|
+
}
|
|
2348
|
+
]
|
|
2349
|
+
}]);
|
|
2350
|
+
if (gatewayMode === "skip") {
|
|
2351
|
+
console.log(chalk.default.gray(" ↳ Gateway skipped. Enable later: hyperclaw gateway start\n"));
|
|
2352
|
+
return null;
|
|
2353
|
+
}
|
|
2354
|
+
if (gatewayMode === "remote") {
|
|
2355
|
+
const { remoteUrl } = await inquirer.default.prompt([{
|
|
2356
|
+
type: "input",
|
|
2357
|
+
name: "remoteUrl",
|
|
2358
|
+
message: "Remote gateway URL (e.g. wss://myserver.com:18789):",
|
|
2359
|
+
validate: (v) => v.startsWith("ws") || "Must start with ws:// or wss://"
|
|
2360
|
+
}]);
|
|
2361
|
+
const { remoteToken } = await inquirer.default.prompt([{
|
|
2362
|
+
type: "password",
|
|
2363
|
+
name: "remoteToken",
|
|
2364
|
+
message: "Remote gateway auth token:",
|
|
2365
|
+
mask: "●"
|
|
2366
|
+
}]);
|
|
2367
|
+
console.log(chalk.default.green(" ✔ Remote gateway configured\n"));
|
|
2368
|
+
return {
|
|
2369
|
+
port: 18789,
|
|
2370
|
+
bind: remoteUrl,
|
|
2371
|
+
authToken: remoteToken,
|
|
2372
|
+
tailscaleExposure: "off",
|
|
2373
|
+
runtime: "node",
|
|
2374
|
+
enabledChannels: [],
|
|
2375
|
+
hooks: true,
|
|
2376
|
+
remote: true
|
|
2377
|
+
};
|
|
2378
|
+
}
|
|
2379
|
+
const { useGateway } = await inquirer.default.prompt([{
|
|
2380
|
+
type: "confirm",
|
|
2381
|
+
name: "useGateway",
|
|
2382
|
+
message: "Enable the local WebSocket gateway? (needed for channels, web UI, mobile apps)",
|
|
2383
|
+
default: true
|
|
2384
|
+
}]);
|
|
2385
|
+
if (!useGateway) {
|
|
2386
|
+
console.log(chalk.default.gray(" ↳ Gateway skipped. You can enable it later: hyperclaw gateway start\n"));
|
|
2387
|
+
return null;
|
|
2388
|
+
}
|
|
2389
|
+
const running = await this.gateway.isRunning(18789);
|
|
2390
|
+
if (running) console.log(chalk.default.gray(` ⚠ Gateway already running at ws://127.0.0.1:18789`));
|
|
2391
|
+
const detectedRuntime = await this.gateway.detectRuntime();
|
|
2392
|
+
const questions = [{
|
|
2393
|
+
type: "input",
|
|
2394
|
+
name: "port",
|
|
2395
|
+
message: "Gateway port:",
|
|
2396
|
+
default: String(18789),
|
|
2397
|
+
validate: (v) => Number(v) > 1024 && Number(v) < 65535 ? true : "Enter valid port (1024-65535)"
|
|
2398
|
+
}];
|
|
2399
|
+
if (full) questions.push({
|
|
2400
|
+
type: "list",
|
|
2401
|
+
name: "bind",
|
|
2402
|
+
message: "Bind address:",
|
|
2403
|
+
choices: [
|
|
2404
|
+
{
|
|
2405
|
+
name: `127.0.0.1 ${chalk.default.gray("Loopback only (secure)")}`,
|
|
2406
|
+
value: "127.0.0.1"
|
|
2407
|
+
},
|
|
2408
|
+
{
|
|
2409
|
+
name: `0.0.0.0 ${chalk.default.gray("All interfaces (LAN)")}`,
|
|
2410
|
+
value: "0.0.0.0"
|
|
2411
|
+
},
|
|
2412
|
+
{
|
|
2413
|
+
name: `Tailscale ${chalk.default.gray("VPN peers only")}`,
|
|
2414
|
+
value: "tailscale"
|
|
2415
|
+
},
|
|
2416
|
+
{
|
|
2417
|
+
name: `Custom ${chalk.default.gray("Enter IP manually")}`,
|
|
2418
|
+
value: "custom"
|
|
2419
|
+
}
|
|
2420
|
+
],
|
|
2421
|
+
default: "127.0.0.1"
|
|
2422
|
+
}, {
|
|
2423
|
+
type: "list",
|
|
2424
|
+
name: "tailscaleExposure",
|
|
2425
|
+
message: "Tailscale exposure:",
|
|
2426
|
+
choices: [
|
|
2427
|
+
{
|
|
2428
|
+
name: this.gateway.exposureLabel("off"),
|
|
2429
|
+
value: "off"
|
|
2430
|
+
},
|
|
2431
|
+
{
|
|
2432
|
+
name: this.gateway.exposureLabel("serve"),
|
|
2433
|
+
value: "serve"
|
|
2434
|
+
},
|
|
2435
|
+
{
|
|
2436
|
+
name: this.gateway.exposureLabel("funnel"),
|
|
2437
|
+
value: "funnel"
|
|
2438
|
+
}
|
|
2439
|
+
],
|
|
2440
|
+
default: "off",
|
|
2441
|
+
when: (a) => a.bind === "tailscale"
|
|
2442
|
+
}, {
|
|
2443
|
+
type: "list",
|
|
2444
|
+
name: "runtime",
|
|
2445
|
+
message: "Runtime:",
|
|
2446
|
+
choices: [
|
|
2447
|
+
{
|
|
2448
|
+
name: `Node.js ${detectedRuntime === "node" ? chalk.default.green("(detected)") : ""}`,
|
|
2449
|
+
value: "node"
|
|
2450
|
+
},
|
|
2451
|
+
{
|
|
2452
|
+
name: `Bun ${detectedRuntime === "bun" ? chalk.default.green("(detected)") : chalk.default.gray("(faster)")}`,
|
|
2453
|
+
value: "bun"
|
|
2454
|
+
},
|
|
2455
|
+
{
|
|
2456
|
+
name: `Deno ${detectedRuntime === "deno" ? chalk.default.green("(detected)") : ""}`,
|
|
2457
|
+
value: "deno"
|
|
2458
|
+
}
|
|
2459
|
+
],
|
|
2460
|
+
default: detectedRuntime
|
|
2461
|
+
});
|
|
2462
|
+
questions.push({
|
|
2463
|
+
type: "password",
|
|
2464
|
+
name: "authToken",
|
|
2465
|
+
message: `Auth token: ${chalk.default.gray("(blank = auto-generate)")}`,
|
|
2466
|
+
mask: "●"
|
|
2467
|
+
});
|
|
2468
|
+
const answers = await inquirer.default.prompt(questions);
|
|
2469
|
+
const token = answers.authToken || this.gateway.generateToken();
|
|
2470
|
+
if (!answers.authToken) console.log(chalk.default.green(" ✔ Auth token auto-generated\n"));
|
|
2471
|
+
return {
|
|
2472
|
+
port: Number(answers.port),
|
|
2473
|
+
bind: answers.bind || "127.0.0.1",
|
|
2474
|
+
authToken: token,
|
|
2475
|
+
tailscaleExposure: answers.tailscaleExposure || "off",
|
|
2476
|
+
runtime: answers.runtime || detectedRuntime,
|
|
2477
|
+
enabledChannels: [],
|
|
2478
|
+
hooks: true
|
|
2479
|
+
};
|
|
2480
|
+
}
|
|
2481
|
+
async selectChannels(full = false) {
|
|
2482
|
+
console.log(chalk.default.hex("#06b6d4")("\n 📱 Communication Channels\n"));
|
|
2483
|
+
const available = await getAvailableChannels();
|
|
2484
|
+
const { selectedChannels } = await inquirer.default.prompt([{
|
|
2485
|
+
type: "checkbox",
|
|
2486
|
+
name: "selectedChannels",
|
|
2487
|
+
message: "Enable channels:",
|
|
2488
|
+
choices: available.map((ch) => {
|
|
2489
|
+
const isUnavailable = ch.status === "unavailable";
|
|
2490
|
+
const reason = isUnavailable ? unavailableReason(ch) : "";
|
|
2491
|
+
const icon$1 = brandIcon(ch.id, "channel");
|
|
2492
|
+
return {
|
|
2493
|
+
name: `${icon$1} ${ch.name.padEnd(18)} ${statusBadge(ch.status, ch)}${ch.requiresGateway && !isUnavailable ? chalk.default.gray(" [needs gateway]") : ""}`,
|
|
2494
|
+
value: ch.id,
|
|
2495
|
+
checked: ch.status === "recommended",
|
|
2496
|
+
disabled: isUnavailable ? reason : false
|
|
2497
|
+
};
|
|
2498
|
+
})
|
|
2499
|
+
}]);
|
|
2500
|
+
const channelConfigs = {};
|
|
2501
|
+
for (const channelId of selectedChannels) {
|
|
2502
|
+
const ch = CHANNELS.find((c) => c.id === channelId);
|
|
2503
|
+
if (!ch || !ch.tokenLabel) continue;
|
|
2504
|
+
console.log(chalk.default.hex("#06b6d4")(`\n ${ch.emoji} ${ch.name}`));
|
|
2505
|
+
if (ch.setupSteps?.length) ch.setupSteps.forEach((s) => console.log(chalk.default.gray(` ${s}`)));
|
|
2506
|
+
else if (ch.tokenHint) console.log(chalk.default.gray(` 💡 ${ch.tokenHint}`));
|
|
2507
|
+
const fields = [{
|
|
2508
|
+
type: "password",
|
|
2509
|
+
name: "token",
|
|
2510
|
+
message: `${ch.tokenLabel}:`,
|
|
2511
|
+
mask: "●"
|
|
2512
|
+
}];
|
|
2513
|
+
for (const f of ch.extraFields || []) fields.push({
|
|
2514
|
+
type: "input",
|
|
2515
|
+
name: f.name,
|
|
2516
|
+
message: `${f.label}:${f.hint ? chalk.default.gray(` (${f.hint})`) : ""}`,
|
|
2517
|
+
...f.required ? { validate: (v) => v.trim().length > 0 || `${f.label} is required` } : {}
|
|
2518
|
+
});
|
|
2519
|
+
channelConfigs[channelId] = await inquirer.default.prompt(fields);
|
|
2520
|
+
}
|
|
2521
|
+
if (selectedChannels.includes("twitch") && channelConfigs["twitch"]?.channels) {
|
|
2522
|
+
const raw = channelConfigs["twitch"].channels;
|
|
2523
|
+
channelConfigs["twitch"].channels = raw.split(",").map((s) => s.trim().replace(/^#/, "").toLowerCase()).filter(Boolean);
|
|
2524
|
+
}
|
|
2525
|
+
if (selectedChannels.includes("email")) {
|
|
2526
|
+
const { wantGmailOAuth } = await inquirer.default.prompt([{
|
|
2527
|
+
type: "confirm",
|
|
2528
|
+
name: "wantGmailOAuth",
|
|
2529
|
+
message: "Enable Gmail OAuth for Pub/Sub real-time (send via hyperclaw gmail watch-setup)?",
|
|
2530
|
+
default: false
|
|
2531
|
+
}]);
|
|
2532
|
+
if (wantGmailOAuth) {
|
|
2533
|
+
console.log(chalk.default.gray(" Running OAuth flow for google-gmail..."));
|
|
2534
|
+
try {
|
|
2535
|
+
const { runOAuthFlow } = await Promise.resolve().then(() => require("./oauth-flow-HdvMxKmZ.js"));
|
|
2536
|
+
const { writeOAuthToken } = await Promise.resolve().then(() => require("./oauth-provider-DfEgSucI.js"));
|
|
2537
|
+
const tokens = await runOAuthFlow("google-gmail", {});
|
|
2538
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
2539
|
+
const expires_at = tokens.expires_in ? now + tokens.expires_in : void 0;
|
|
2540
|
+
await writeOAuthToken("google-gmail", {
|
|
2541
|
+
access_token: tokens.access_token,
|
|
2542
|
+
refresh_token: tokens.refresh_token,
|
|
2543
|
+
expires_at,
|
|
2544
|
+
token_url: "https://oauth2.googleapis.com/token"
|
|
2545
|
+
});
|
|
2546
|
+
console.log(chalk.default.green(" ✔ Gmail OAuth configured — next: hyperclaw gmail watch-setup"));
|
|
2547
|
+
} catch (e) {
|
|
2548
|
+
console.log(chalk.default.yellow(` ⚠ OAuth failed — you can run it later: hyperclaw auth oauth google-gmail`));
|
|
2549
|
+
}
|
|
2550
|
+
}
|
|
2551
|
+
}
|
|
2552
|
+
return channelConfigs;
|
|
2553
|
+
}
|
|
2554
|
+
async configureServiceApiKeys() {
|
|
2555
|
+
console.log(chalk.default.hex("#06b6d4")("\n 🔑 Service API Keys — any app with an API key\n"));
|
|
2556
|
+
console.log(chalk.default.gray(" Stored securely in config. How they work:\n"));
|
|
2557
|
+
console.log(chalk.default.gray(" • Wizard: add keys here\n"));
|
|
2558
|
+
console.log(chalk.default.gray(" • Config: ~/.hyperclaw/hyperclaw.json → skills.apiKeys\n"));
|
|
2559
|
+
console.log(chalk.default.gray(" • Env: HACKERONE_*, BUGCROWD_*, SYNACK_*, or CUSTOM_ID_API_KEY\n"));
|
|
2560
|
+
console.log(chalk.default.gray(" • Tools: built-in tools read them automatically for research.\n"));
|
|
2561
|
+
const { wantServiceKeys } = await inquirer.default.prompt([{
|
|
2562
|
+
type: "confirm",
|
|
2563
|
+
name: "wantServiceKeys",
|
|
2564
|
+
message: "Add API keys for external services (HackerOne, Bugcrowd, Synack, or custom)?",
|
|
2565
|
+
default: false
|
|
2566
|
+
}]);
|
|
2567
|
+
if (!wantServiceKeys) return {};
|
|
2568
|
+
const KNOWN_SERVICES = [
|
|
2569
|
+
{
|
|
2570
|
+
id: "hackerone",
|
|
2571
|
+
name: "HackerOne",
|
|
2572
|
+
hint: "username:token (Basic auth)"
|
|
2573
|
+
},
|
|
2574
|
+
{
|
|
2575
|
+
id: "bugcrowd",
|
|
2576
|
+
name: "Bugcrowd",
|
|
2577
|
+
hint: "Token from Bugcrowd API Credentials"
|
|
2578
|
+
},
|
|
2579
|
+
{
|
|
2580
|
+
id: "synack",
|
|
2581
|
+
name: "Synack",
|
|
2582
|
+
hint: "API token from Synack"
|
|
2583
|
+
},
|
|
2584
|
+
{
|
|
2585
|
+
id: "__custom__",
|
|
2586
|
+
name: "Other (custom)",
|
|
2587
|
+
hint: "Any app with an API key"
|
|
2588
|
+
}
|
|
2589
|
+
];
|
|
2590
|
+
const { servicesToAdd } = await inquirer.default.prompt([{
|
|
2591
|
+
type: "checkbox",
|
|
2592
|
+
name: "servicesToAdd",
|
|
2593
|
+
message: "Select services:",
|
|
2594
|
+
choices: KNOWN_SERVICES.filter((s) => s.id !== "__custom__").map((s) => ({
|
|
2595
|
+
name: `${s.name} ${chalk.default.gray(`(${s.hint})`)}`,
|
|
2596
|
+
value: s.id
|
|
2597
|
+
})),
|
|
2598
|
+
validate: (v) => true
|
|
2599
|
+
}]);
|
|
2600
|
+
const apiKeys = {};
|
|
2601
|
+
for (const sid of servicesToAdd || []) {
|
|
2602
|
+
const svc = KNOWN_SERVICES.find((s) => s.id === sid);
|
|
2603
|
+
const r = await inquirer.default.prompt([{
|
|
2604
|
+
type: "password",
|
|
2605
|
+
name: "key",
|
|
2606
|
+
message: `${svc?.name || sid} API key:`,
|
|
2607
|
+
mask: "●",
|
|
2608
|
+
validate: (v) => v.trim().length > 3 || "Required"
|
|
2609
|
+
}]);
|
|
2610
|
+
apiKeys[sid] = r.key.trim();
|
|
2611
|
+
}
|
|
2612
|
+
const { addCustom } = await inquirer.default.prompt([{
|
|
2613
|
+
type: "confirm",
|
|
2614
|
+
name: "addCustom",
|
|
2615
|
+
message: "Add a custom service (any app)?",
|
|
2616
|
+
default: false
|
|
2617
|
+
}]);
|
|
2618
|
+
if (addCustom) {
|
|
2619
|
+
const { customId, customKey } = await inquirer.default.prompt([{
|
|
2620
|
+
type: "input",
|
|
2621
|
+
name: "customId",
|
|
2622
|
+
message: "Service ID (e.g. my-app, ads-power):",
|
|
2623
|
+
validate: (v) => /^[a-z0-9_-]+$/i.test(v?.trim() || "") || "Letters, numbers, - _ only"
|
|
2624
|
+
}, {
|
|
2625
|
+
type: "password",
|
|
2626
|
+
name: "customKey",
|
|
2627
|
+
message: "API key:",
|
|
2628
|
+
mask: "●",
|
|
2629
|
+
validate: (v) => v.trim().length > 3 || "Required"
|
|
2630
|
+
}]);
|
|
2631
|
+
apiKeys[customId.trim().toLowerCase()] = customKey.trim();
|
|
2632
|
+
}
|
|
2633
|
+
if (Object.keys(apiKeys).length > 0) console.log(chalk.default.green(` ✔ Saved ${Object.keys(apiKeys).length} API key(s)`));
|
|
2634
|
+
return apiKeys;
|
|
2635
|
+
}
|
|
2636
|
+
async configureHyperClawBot(gatewayConfig) {
|
|
2637
|
+
console.log(chalk.default.hex("#06b6d4")("\n 🤖 HyperClaw Bot — Remote control via Telegram\n"));
|
|
2638
|
+
const trans = getTranscriptionProviders();
|
|
2639
|
+
if (trans.length > 0) {
|
|
2640
|
+
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."));
|
|
2641
|
+
console.log();
|
|
2642
|
+
}
|
|
2643
|
+
const { wantHyperClawBot } = await inquirer.default.prompt([{
|
|
2644
|
+
type: "confirm",
|
|
2645
|
+
name: "wantHyperClawBot",
|
|
2646
|
+
message: "Enable HyperClaw Bot for remote control (status, restart, /agent from mobile)?",
|
|
2647
|
+
default: false
|
|
2648
|
+
}]);
|
|
2649
|
+
if (!wantHyperClawBot) return void 0;
|
|
2650
|
+
const { token } = await inquirer.default.prompt([{
|
|
2651
|
+
type: "input",
|
|
2652
|
+
name: "token",
|
|
2653
|
+
message: "Telegram Bot token (from @BotFather):",
|
|
2654
|
+
validate: (v) => v.trim().length > 10 || "Required"
|
|
2655
|
+
}]);
|
|
2656
|
+
const { userIds } = await inquirer.default.prompt([{
|
|
2657
|
+
type: "input",
|
|
2658
|
+
name: "userIds",
|
|
2659
|
+
message: "Allowed user IDs (comma-separated, leave empty = everyone):",
|
|
2660
|
+
default: ""
|
|
2661
|
+
}]);
|
|
2662
|
+
const gatewayUrl = `http://localhost:${gatewayConfig?.port || 18789}`;
|
|
2663
|
+
const { saveBotConfig } = await Promise.resolve().then(() => require("./hyperclawbot-DfMGowZC.js"));
|
|
2664
|
+
await saveBotConfig({
|
|
2665
|
+
platform: "telegram",
|
|
2666
|
+
token: token.trim(),
|
|
2667
|
+
gatewayUrl,
|
|
2668
|
+
allowedUsers: userIds ? userIds.split(",").map((s) => s.trim()).filter(Boolean) : [],
|
|
2669
|
+
gatewayToken: void 0,
|
|
2670
|
+
enabled: true,
|
|
2671
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2672
|
+
});
|
|
2673
|
+
console.log(chalk.default.green(" ✔ HyperClaw Bot configured — Start: hyperclaw bot start"));
|
|
2674
|
+
return { token: token.trim() };
|
|
2675
|
+
}
|
|
2676
|
+
async configureTalkMode() {
|
|
2677
|
+
console.log(chalk.default.hex("#06b6d4")("\n 🎙️ Talk Mode — ElevenLabs TTS\n"));
|
|
2678
|
+
const { wantTalkMode } = await inquirer.default.prompt([{
|
|
2679
|
+
type: "confirm",
|
|
2680
|
+
name: "wantTalkMode",
|
|
2681
|
+
message: "Enable Talk Mode (voice responses via ElevenLabs)?",
|
|
2682
|
+
default: false
|
|
2683
|
+
}]);
|
|
2684
|
+
if (!wantTalkMode) return void 0;
|
|
2685
|
+
const { apiKey } = await inquirer.default.prompt([{
|
|
2686
|
+
type: "password",
|
|
2687
|
+
name: "apiKey",
|
|
2688
|
+
message: "ElevenLabs API key (elevenlabs.io):",
|
|
2689
|
+
mask: "●",
|
|
2690
|
+
validate: (v) => v.trim().length > 10 || "Required"
|
|
2691
|
+
}]);
|
|
2692
|
+
console.log(chalk.default.green(" ✔ Talk Mode configured"));
|
|
2693
|
+
return {
|
|
2694
|
+
apiKey: apiKey.trim(),
|
|
2695
|
+
voiceId: "21m00Tcm4TlvDq8ikWAM",
|
|
2696
|
+
modelId: "eleven_multilingual_v2"
|
|
2697
|
+
};
|
|
2698
|
+
}
|
|
2699
|
+
async configureMemoryIntegration() {
|
|
2700
|
+
console.log(chalk.default.hex("#06b6d4")("\n 📂 Memory Integration (Obsidian / Raycast / Hazel)\n"));
|
|
2701
|
+
const { vaultPath } = await inquirer.default.prompt([{
|
|
2702
|
+
type: "input",
|
|
2703
|
+
name: "vaultPath",
|
|
2704
|
+
message: "Sync memory to vault? Enter path or leave empty to skip:",
|
|
2705
|
+
default: ""
|
|
2706
|
+
}]);
|
|
2707
|
+
const vaultDir = String(vaultPath || "").trim();
|
|
2708
|
+
if (!vaultDir) return void 0;
|
|
2709
|
+
const { dailyNotes } = await inquirer.default.prompt([{
|
|
2710
|
+
type: "confirm",
|
|
2711
|
+
name: "dailyNotes",
|
|
2712
|
+
message: "Write daily notes with session summaries?",
|
|
2713
|
+
default: true
|
|
2714
|
+
}]);
|
|
2715
|
+
return {
|
|
2716
|
+
vaultDir,
|
|
2717
|
+
dailyNotes,
|
|
2718
|
+
syncOnAppend: true
|
|
2719
|
+
};
|
|
2720
|
+
}
|
|
2721
|
+
async configureIdentity() {
|
|
2722
|
+
console.log(chalk.default.hex("#06b6d4")("\n 🦅 Agent Identity\n"));
|
|
2723
|
+
const identity = await inquirer.default.prompt([
|
|
2724
|
+
{
|
|
2725
|
+
type: "input",
|
|
2726
|
+
name: "userName",
|
|
2727
|
+
message: "Your name:",
|
|
2728
|
+
default: "Boss"
|
|
2729
|
+
},
|
|
2730
|
+
{
|
|
2731
|
+
type: "input",
|
|
2732
|
+
name: "agentName",
|
|
2733
|
+
message: "Agent name:",
|
|
2734
|
+
default: "Hyper"
|
|
2735
|
+
},
|
|
2736
|
+
{
|
|
2737
|
+
type: "input",
|
|
2738
|
+
name: "wakeWord",
|
|
2739
|
+
message: "Wake word for voice:",
|
|
2740
|
+
default: (a) => `Hey ${a.agentName || "Hyper"}`
|
|
2741
|
+
},
|
|
2742
|
+
{
|
|
2743
|
+
type: "list",
|
|
2744
|
+
name: "personality",
|
|
2745
|
+
message: "Personality:",
|
|
2746
|
+
choices: [
|
|
2747
|
+
"Professional and concise",
|
|
2748
|
+
"Friendly and casual",
|
|
2749
|
+
"Witty with humor",
|
|
2750
|
+
"Direct and no-nonsense",
|
|
2751
|
+
"Custom"
|
|
2752
|
+
]
|
|
2753
|
+
},
|
|
2754
|
+
{
|
|
2755
|
+
type: "input",
|
|
2756
|
+
name: "customPersonality",
|
|
2757
|
+
message: "Describe personality:",
|
|
2758
|
+
when: (a) => a.personality === "Custom"
|
|
2759
|
+
},
|
|
2760
|
+
{
|
|
2761
|
+
type: "list",
|
|
2762
|
+
name: "language",
|
|
2763
|
+
message: "Primary language:",
|
|
2764
|
+
choices: [
|
|
2765
|
+
"English",
|
|
2766
|
+
"Greek",
|
|
2767
|
+
"Spanish",
|
|
2768
|
+
"French",
|
|
2769
|
+
"German",
|
|
2770
|
+
"Chinese",
|
|
2771
|
+
"Japanese",
|
|
2772
|
+
"Arabic"
|
|
2773
|
+
]
|
|
2774
|
+
}
|
|
2775
|
+
]);
|
|
2776
|
+
console.log(chalk.default.gray("\n This is the greeting sent to your channels when the agent starts.\n"));
|
|
2777
|
+
const { wakeUpMessage } = await inquirer.default.prompt([{
|
|
2778
|
+
type: "input",
|
|
2779
|
+
name: "wakeUpMessage",
|
|
2780
|
+
message: "Agent wake-up message:",
|
|
2781
|
+
default: (identity.agentName || "Hyper") + " is online. How can I help you today?"
|
|
2782
|
+
}]);
|
|
2783
|
+
const { wantSystemPrompt } = await inquirer.default.prompt([{
|
|
2784
|
+
type: "confirm",
|
|
2785
|
+
name: "wantSystemPrompt",
|
|
2786
|
+
message: "Add a custom system prompt / instructions for the agent?",
|
|
2787
|
+
default: false
|
|
2788
|
+
}]);
|
|
2789
|
+
let systemPrompt = "";
|
|
2790
|
+
if (wantSystemPrompt) {
|
|
2791
|
+
console.log(chalk.default.gray(" Tip: describe role, restrictions, tone, specific knowledge, etc.\n"));
|
|
2792
|
+
const r = await inquirer.default.prompt([{
|
|
2793
|
+
type: "editor",
|
|
2794
|
+
name: "systemPrompt",
|
|
2795
|
+
message: "System prompt (opens editor — save & close to continue):"
|
|
2796
|
+
}]);
|
|
2797
|
+
systemPrompt = r.systemPrompt?.trim() || "";
|
|
2798
|
+
}
|
|
2799
|
+
const globalRules = [
|
|
2800
|
+
"Always respond in the user's preferred language",
|
|
2801
|
+
"Never reveal the gateway auth token or API keys",
|
|
2802
|
+
"Confirm before destructive or irreversible actions",
|
|
2803
|
+
"Respect user privacy — never share conversation data",
|
|
2804
|
+
"All subagents inherit these rules — cannot be overridden"
|
|
2805
|
+
];
|
|
2806
|
+
const { addRules } = await inquirer.default.prompt([{
|
|
2807
|
+
type: "confirm",
|
|
2808
|
+
name: "addRules",
|
|
2809
|
+
message: "Add custom rules to AGENTS.md?",
|
|
2810
|
+
default: false
|
|
2811
|
+
}]);
|
|
2812
|
+
let customRules = [];
|
|
2813
|
+
if (addRules) {
|
|
2814
|
+
const { rules } = await inquirer.default.prompt([{
|
|
2815
|
+
type: "input",
|
|
2816
|
+
name: "rules",
|
|
2817
|
+
message: "Rules (semicolon-separated):",
|
|
2818
|
+
filter: (v) => v.split(";").map((r) => r.trim()).filter(Boolean)
|
|
2819
|
+
}]);
|
|
2820
|
+
customRules = rules;
|
|
2821
|
+
}
|
|
2822
|
+
const personality = identity.personality === "Custom" ? identity.customPersonality || "Custom" : identity.personality;
|
|
2823
|
+
return {
|
|
2824
|
+
agentName: identity.agentName,
|
|
2825
|
+
userName: identity.userName,
|
|
2826
|
+
language: identity.language,
|
|
2827
|
+
personality,
|
|
2828
|
+
wakeUpMessage: wakeUpMessage.trim(),
|
|
2829
|
+
systemPrompt: systemPrompt || void 0,
|
|
2830
|
+
rules: [...globalRules, ...customRules]
|
|
2831
|
+
};
|
|
2832
|
+
}
|
|
2833
|
+
async configureSkillsAndHooks() {
|
|
2834
|
+
const { wantSkills } = await inquirer.default.prompt([{
|
|
2835
|
+
type: "confirm",
|
|
2836
|
+
name: "wantSkills",
|
|
2837
|
+
message: "Configure skills & hooks now?",
|
|
2838
|
+
default: false
|
|
2839
|
+
}]);
|
|
2840
|
+
if (!wantSkills) {
|
|
2841
|
+
console.log(chalk.default.gray(" ↳ Skipped — enable later: hyperclaw hooks enable <name>\n"));
|
|
2842
|
+
return {
|
|
2843
|
+
hooks: [],
|
|
2844
|
+
heartbeat: false
|
|
2845
|
+
};
|
|
2846
|
+
}
|
|
2847
|
+
const { selectedHooks } = await inquirer.default.prompt([{
|
|
2848
|
+
type: "checkbox",
|
|
2849
|
+
name: "selectedHooks",
|
|
2850
|
+
message: "Select hooks to enable:",
|
|
2851
|
+
choices: [
|
|
2852
|
+
{
|
|
2853
|
+
name: `${"boot.md".padEnd(22)} ${chalk.default.gray("Run commands on agent startup")}`,
|
|
2854
|
+
value: "boot"
|
|
2855
|
+
},
|
|
2856
|
+
{
|
|
2857
|
+
name: `${"command-logger".padEnd(22)} ${chalk.default.gray("Log every tool call to file")}`,
|
|
2858
|
+
value: "command-logger"
|
|
2859
|
+
},
|
|
2860
|
+
{
|
|
2861
|
+
name: `${"session-memory".padEnd(22)} ${chalk.default.gray("Persist session context across restarts")}`,
|
|
2862
|
+
value: "session-memory"
|
|
2863
|
+
},
|
|
2864
|
+
{
|
|
2865
|
+
name: `${"morning-briefing".padEnd(22)} ${chalk.default.gray("Daily proactive summary to HEARTBEAT.md")}`,
|
|
2866
|
+
value: "morning-briefing"
|
|
2867
|
+
}
|
|
2868
|
+
]
|
|
2869
|
+
}]);
|
|
2870
|
+
const heartbeat = selectedHooks.includes("morning-briefing");
|
|
2871
|
+
try {
|
|
2872
|
+
const { HookLoader } = await Promise.resolve().then(() => require("./loader-aSIGRXBq.js"));
|
|
2873
|
+
const loader = new HookLoader();
|
|
2874
|
+
for (const h of selectedHooks) loader.enable(h);
|
|
2875
|
+
} catch {}
|
|
2876
|
+
if (selectedHooks.length > 0) console.log(chalk.default.green(` ✔ Enabled: ${selectedHooks.join(", ")}\n`));
|
|
2877
|
+
return {
|
|
2878
|
+
hooks: selectedHooks,
|
|
2879
|
+
heartbeat
|
|
2880
|
+
};
|
|
2881
|
+
}
|
|
2882
|
+
async saveAll(data, options) {
|
|
2883
|
+
console.log();
|
|
2884
|
+
const spinner = (0, ora.default)("Saving configuration...").start();
|
|
2885
|
+
const current = await this.config.load();
|
|
2886
|
+
const skillsPatch = { installed: current?.skills?.installed || [] };
|
|
2887
|
+
if (data.serviceApiKeys && Object.keys(data.serviceApiKeys).length > 0) skillsPatch.apiKeys = {
|
|
2888
|
+
...current?.skills?.apiKeys || {},
|
|
2889
|
+
...data.serviceApiKeys
|
|
2890
|
+
};
|
|
2891
|
+
if (current?.skills?.vtApiKey) skillsPatch.vtApiKey = current.skills.vtApiKey;
|
|
2892
|
+
const finalSkills = skillsPatch.apiKeys || skillsPatch.vtApiKey ? skillsPatch : current?.skills || { installed: [] };
|
|
2893
|
+
await this.config.save({
|
|
2894
|
+
...current,
|
|
2895
|
+
version: "4.0.2",
|
|
2896
|
+
workspaceName: data.workspaceName,
|
|
2897
|
+
provider: data.providerConfig,
|
|
2898
|
+
providers: data.providers || (data.providerConfig ? [data.providerConfig] : []),
|
|
2899
|
+
gateway: data.gatewayConfig || void 0,
|
|
2900
|
+
channels: Object.keys(data.channelConfigs || {}),
|
|
2901
|
+
channelConfigs: data.channelConfigs,
|
|
2902
|
+
skills: finalSkills,
|
|
2903
|
+
enabledHooks: data.hooks || [],
|
|
2904
|
+
identity: {
|
|
2905
|
+
agentName: data.identity?.agentName,
|
|
2906
|
+
userName: data.identity?.userName,
|
|
2907
|
+
language: data.identity?.language,
|
|
2908
|
+
wakeWord: data.identity?.wakeWord || `Hey ${data.identity?.agentName || "Hyper"}`,
|
|
2909
|
+
wakeUpMessage: data.identity?.wakeUpMessage,
|
|
2910
|
+
systemPrompt: data.identity?.systemPrompt,
|
|
2911
|
+
personality: data.identity?.personality,
|
|
2912
|
+
rules: data.identity?.rules
|
|
2913
|
+
},
|
|
2914
|
+
memoryIntegration: data.memoryIntegration,
|
|
2915
|
+
talkMode: data.talkModeConfig,
|
|
2916
|
+
pcAccess: {
|
|
2917
|
+
enabled: true,
|
|
2918
|
+
level: "full",
|
|
2919
|
+
allowedPaths: [],
|
|
2920
|
+
allowedCommands: [],
|
|
2921
|
+
confirmDestructive: false,
|
|
2922
|
+
maxOutputBytes: 5e4
|
|
2923
|
+
},
|
|
2924
|
+
hatchMode: data.hatchMode || "tui",
|
|
2925
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2926
|
+
});
|
|
2927
|
+
spinner.succeed("Configuration saved");
|
|
2928
|
+
const memory = new MemoryManager();
|
|
2929
|
+
await memory.init(data.identity);
|
|
2930
|
+
if (data.heartbeatEnabled) try {
|
|
2931
|
+
const { HookLoader } = await Promise.resolve().then(() => require("./loader-aSIGRXBq.js"));
|
|
2932
|
+
const loader = new HookLoader();
|
|
2933
|
+
loader.enable("morning-briefing");
|
|
2934
|
+
console.log(chalk.default.gray(" ✔ Morning Briefing hook enabled"));
|
|
2935
|
+
} catch {}
|
|
2936
|
+
await this.testConnections(data.channelConfigs || {});
|
|
2937
|
+
if (data.installDaemon || options.daemon || options.installDaemon) {
|
|
2938
|
+
const s = (0, ora.default)("🩸 Installing system daemon...").start();
|
|
2939
|
+
await this.daemon.install();
|
|
2940
|
+
s.succeed(chalk.default.red("🩸 System daemon installed (starts on boot)"));
|
|
2941
|
+
}
|
|
2942
|
+
if (data.gatewayConfig?.tailscaleExposure && data.gatewayConfig.tailscaleExposure !== "off") await this.gateway.applyTailscaleExposure(data.gatewayConfig.tailscaleExposure, data.gatewayConfig.port);
|
|
2943
|
+
if (data.gatewayConfig && (options.startNow || data.installDaemon)) {
|
|
2944
|
+
const s = (0, ora.default)("Starting gateway...").start();
|
|
2945
|
+
await this.daemon.start();
|
|
2946
|
+
s.succeed(`Gateway running at ws://localhost:${data.gatewayConfig.port}`);
|
|
2947
|
+
}
|
|
2948
|
+
this.showSuccessScreen(data);
|
|
2949
|
+
}
|
|
2950
|
+
async testConnections(configs) {
|
|
2951
|
+
for (const [channelId, cfg] of Object.entries(configs)) {
|
|
2952
|
+
const ch = CHANNELS.find((c) => c.id === channelId);
|
|
2953
|
+
if (!ch || !cfg?.token) continue;
|
|
2954
|
+
const s = (0, ora.default)(`Testing ${ch.name}...`).start();
|
|
2955
|
+
await new Promise((r) => setTimeout(r, 900 + Math.random() * 600));
|
|
2956
|
+
s.succeed(`${ch.emoji} ${ch.name} connected`);
|
|
2957
|
+
}
|
|
2958
|
+
}
|
|
2959
|
+
showSuccessScreen(data) {
|
|
2960
|
+
const channels = Object.keys(data.channelConfigs || {});
|
|
2961
|
+
const gwUrl = `ws://localhost:${data.gatewayConfig?.port || 1515}`;
|
|
2962
|
+
const hasEmail = channels.includes("email");
|
|
2963
|
+
const hasHyperClawBot = !!data.hyperclawbotConfig?.token;
|
|
2964
|
+
const cmdLines = [
|
|
2965
|
+
chalk.default.gray(" hyperclaw dashboard — TUI dashboard"),
|
|
2966
|
+
chalk.default.gray(" hyperclaw hub — Skill hub"),
|
|
2967
|
+
chalk.default.gray(" hyperclaw gateway status — Gateway panel"),
|
|
2968
|
+
chalk.default.gray(" hyperclaw ") + chalk.default.red("daemon") + chalk.default.gray(" status — Service status"),
|
|
2969
|
+
chalk.default.gray(" hyperclaw voice — Voice settings"),
|
|
2970
|
+
chalk.default.gray(" hyperclaw canvas show — AI canvas")
|
|
2971
|
+
];
|
|
2972
|
+
if (hasHyperClawBot) cmdLines.push(chalk.default.gray(" hyperclaw bot start — HyperClawBot remote control"));
|
|
2973
|
+
if (hasEmail) cmdLines.push(chalk.default.gray(" hyperclaw gmail watch-setup — Gmail Pub/Sub (real-time)"));
|
|
2974
|
+
cmdLines.push(chalk.default.gray(" hyperclaw nodes — Mobile nodes (Connect tab)"));
|
|
2975
|
+
cmdLines.push(chalk.default.gray(" hyperclaw cron list — Scheduled tasks"));
|
|
2976
|
+
const lines = [
|
|
2977
|
+
`${chalk.default.gray("Agent:")} ${chalk.default.hex("#06b6d4")(data.identity?.agentName)} (you: ${data.identity?.userName})`,
|
|
2978
|
+
`${chalk.default.gray("Model:")} ${data.providerConfig?.modelId}`,
|
|
2979
|
+
`${chalk.default.gray("Provider:")} ${data.providerConfig?.providerId}`,
|
|
2980
|
+
`${chalk.default.gray("Gateway:")} ${gwUrl}`,
|
|
2981
|
+
`${chalk.default.gray("Channels:")} ${channels.length ? channels.join(", ") : "CLI only"}`,
|
|
2982
|
+
"",
|
|
2983
|
+
chalk.default.hex("#06b6d4")("Commands:"),
|
|
2984
|
+
...cmdLines
|
|
2985
|
+
].join("\n");
|
|
2986
|
+
console.log("\n" + (0, boxen.default)(chalk.default.hex("#06b6d4")("🎉 HyperClaw v4.0.2 ready!\n\n") + lines, {
|
|
2987
|
+
padding: 1,
|
|
2988
|
+
borderStyle: "round",
|
|
2989
|
+
borderColor: "cyan",
|
|
2990
|
+
margin: 1,
|
|
2991
|
+
backgroundColor: "#0a0a0a"
|
|
2992
|
+
}));
|
|
2993
|
+
}
|
|
2994
|
+
};
|
|
2995
|
+
|
|
2996
|
+
//#endregion
|
|
2997
|
+
Object.defineProperty(exports, 'Banner', {
|
|
2998
|
+
enumerable: true,
|
|
2999
|
+
get: function () {
|
|
3000
|
+
return Banner;
|
|
3001
|
+
}
|
|
3002
|
+
});
|
|
3003
|
+
Object.defineProperty(exports, 'ConfigStore', {
|
|
3004
|
+
enumerable: true,
|
|
3005
|
+
get: function () {
|
|
3006
|
+
return ConfigStore;
|
|
3007
|
+
}
|
|
3008
|
+
});
|
|
3009
|
+
Object.defineProperty(exports, 'DaemonManager', {
|
|
3010
|
+
enumerable: true,
|
|
3011
|
+
get: function () {
|
|
3012
|
+
return DaemonManager;
|
|
3013
|
+
}
|
|
3014
|
+
});
|
|
3015
|
+
Object.defineProperty(exports, 'GatewayManager', {
|
|
3016
|
+
enumerable: true,
|
|
3017
|
+
get: function () {
|
|
3018
|
+
return GatewayManager;
|
|
3019
|
+
}
|
|
3020
|
+
});
|
|
3021
|
+
Object.defineProperty(exports, 'HyperClawWizard', {
|
|
3022
|
+
enumerable: true,
|
|
3023
|
+
get: function () {
|
|
3024
|
+
return HyperClawWizard;
|
|
3025
|
+
}
|
|
3026
|
+
});
|